Skip to content

feat: Add support for CLP JSON string serialization.#40

Merged
wraymo merged 12 commits into
y-scope:presto-0.293-clp-connectorfrom
wraymo:clp_get_json_string
Oct 24, 2025
Merged

feat: Add support for CLP JSON string serialization.#40
wraymo merged 12 commits into
y-scope:presto-0.293-clp-connectorfrom
wraymo:clp_get_json_string

Conversation

@wraymo

@wraymo wraymo commented Oct 17, 2025

Copy link
Copy Markdown

Description

This PR adds support for CLP JSON string serialization, enabling the clp_get_json_string UDF to return serialized JSON strings in Presto.

Specifically:

  1. Introduces __json_string to represent columns that require serialized JSON output.
  2. Excludes such columns from the projected columns list while recording their column indices for later processing.
  3. Adds dedicated ClpArchiveJsonStringVectorLoader and ClpIrJsonStringVectorLoader to initialize the JSON serializer and perform on-demand serialization when creating vectors for these columns.

Checklist

  • The PR satisfies the contribution guidelines.
  • This is a breaking change and that has been indicated in the PR title, OR this isn't a
    breaking change.
  • Necessary docs have been updated, OR no docs need to be updated.

Validation performed

Did an end-to-end testing with a sample dataset

presto:default> select timestamp, clp_get_json_string() from default where clp_wildcard_string_column() in ('profile_update', 'logout');
      timestamp       |                                                                          _col1                                                                           
----------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------
 2025-05-13T15:00:20Z | {"timestamp":"2025-05-13T15:00:20Z","level":"INFO","service":"user-service","event":{"action":"logout"},"user":{"id":"12345"}}                           
 2025-05-13T15:00:06Z | {"timestamp":"2025-05-13T15:00:06Z","level":"INFO","service":"user-service","event":{"action":"profile_update"},"user":{"id":"67890","name":"Jane Doe"}} 
 2025-05-13T15:00:20Z | {"timestamp":"2025-05-13T15:00:20Z","level":"INFO","service":"user-service","event":{"action":"logout"},"user":{"id":"12345"}}                           
 2025-05-13T15:00:06Z | {"timestamp":"2025-05-13T15:00:06Z","level":"INFO","service":"user-service","event":{"action":"profile_update"},"user":{"id":"67890","name":"Jane Doe"}} 
(4 rows)

Query 20251018_182705_00001_ikv76, FINISHED, 1 node
Splits: 3 total, 3 done (100.00%)
[Latency: client-side: 225ms, server-side: 191ms] [6 rows, 0B] [31 rows/s, 0B/s]

presto:default> select event.type, clp_get_json_string() from default_ir;
      type      |                                                                                                                            _col1                                                                                     >
----------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------->
 network        | {"event":{"details":{"destination":{"ip":"10.0.1.5","port":80},"source":{"ip":"192.168.3.10"}},"severity":"INFO","subtype":"connection","tags":["inbound","tcp"],"type":"network"},"timestamp":1746003000}           >
 storage        | {"event":{"details":{"mount":"/var/log","usage":{"percent":92}},"severity":"WARNING","subtype":"disk_usage","tags":["filesystem","monitoring"],"type":"storage"},"timestamp":1746003005}                             >
 process        | {"event":{"details":{"id":1234,"name":"cron"},"severity":"INFO","subtype":"start","tags":["system","init"],"type":"process"},"timestamp":1746003010}                                                                 >
 authentication | {"event":{"details":{"ip":"203.0.113.20","reason":"invalid password"},"severity":"ERROR","subtype":"failure","tags":["login","security"],"type":"authentication","user":{"method":"ssh","name":"guest"}},"timestamp":>
 service        | {"event":{"details":{"name":"httpd","state":"running","uptime":{"seconds":3600}},"severity":"INFO","subtype":"status","tags":["daemon","http"],"type":"service"},"timestamp":1746003020}                             >
 security       | {"event":{"details":{"payload":"SELECT * FROM users;","rule":{"id":"SQLI-001"},"target":"/api/data"},"severity":"HIGH","subtype":"alert","tags":["threat","sql"],"type":"security"},"timestamp":1746003025}          >
 memory         | {"event":{"details":{"free":{"gb":7.5},"total":{"gb":16},"used":{"gb":8.5}},"severity":"NORMAL","subtype":"usage","tags":["resources"],"type":"memory"},"timestamp":1746003030}                                      >
 configuration  | {"event":{"details":{"component":"database","setting":{"name":"timeout","newValue":60,"oldValue":30}},"severity":"INFO","subtype":"change","tags":["admin","settings"],"type":"configuration","user":{"name":"admin"}>
 file           | {"event":{"details":{"flags":["sensitive","audit"],"operation":"read","path":"/etc/shadow","permissions":"rw-------"},"severity":"WARNING","subtype":"access","tags":["file","privileged"],"type":"file","user":{"nam>
 backup         | {"event":{"details":{"duration":{"seconds":1200},"job":{"name":"daily_backup","status":"completed"},"size":{"gb":25.3}},"severity":"INFO","subtype":"status","tags":["backup","daily"],"type":"backup"},"timestamp":1>
(10 rows)

Query 20251022_215732_00010_fizjr, FINISHED, 1 node
Splits: 2 total, 2 done (100.00%)
[Latency: client-side: 137ms, server-side: 111ms] [10 rows, 0B] [90 rows/s, 0B/s]

Summary by CodeRabbit

  • New Features

    • Project JSON-string columns with lazy on-demand JSON generation so JSON payloads appear inline with other projected fields.
    • Added a standardized __json_string column for requesting serialized JSON payloads.
  • Bug Fixes

    • Improved per-column indexing and projection validation to reduce projection-related errors and ensure correct result construction.
  • Tests

    • Added integration tests verifying JSON-string projection, payload content and IR/output consistency.
  • Chores

    • Updated CLP dependency to a newer revision.

@coderabbitai

coderabbitai Bot commented Oct 17, 2025

Copy link
Copy Markdown

Walkthrough

Adds lazy support for the __json_string projection: introduces JSON-string VectorLoaders for archive and IR paths, refactors cursor indexing and projection handling to per-column indices, updates CMake source lists and CLP FetchContent commit, and adds tests validating __json_string outputs.

Changes

Cohort / File(s) Summary
Dependency Update
CMake/resolve_dependency_modules/clp.cmake
Updates CLP FetchContent GIT_TAG from 67276c09acbd48dd502454288f40072c44628726 to 9e991ab49681faff0aea259a37b489c3f416e06e.
Archive build sources
velox/connectors/clp/search_lib/archive/CMakeLists.txt
Removes several public header sources from the target and adds ClpArchiveJsonStringVectorLoader.cpp.
Archive cursor & projection
velox/connectors/clp/search_lib/archive/ClpArchiveCursor.h, velox/connectors/clp/search_lib/archive/ClpArchiveCursor.cpp
Replaces single reader index with columnIndex_ and projectedColumnIndex_, adds schemaReader_ and jsonStringColumnIndices_, detects __json_string in projection, and routes JSON columns to ClpArchiveJsonStringVectorLoader.
Archive JSON loader (new)
velox/connectors/clp/search_lib/archive/ClpArchiveJsonStringVectorLoader.h, velox/connectors/clp/search_lib/archive/ClpArchiveJsonStringVectorLoader.cpp
Adds ClpArchiveJsonStringVectorLoader: a VectorLoader that uses a clp_s::SchemaReader* and filtered row indices to serialize per-row JSON into a String vector on demand.
IR build sources
velox/connectors/clp/search_lib/ir/CMakeLists.txt
Updates public/source list: removes some public headers and adds ClpIrJsonStringVectorLoader.cpp; retains ClpIrVectorLoader.cpp.
IR cursor & projection
velox/connectors/clp/search_lib/ir/ClpIrCursor.h, velox/connectors/clp/search_lib/ir/ClpIrCursor.cpp
Replaces readerIndex_ with columnIndex_ and projectedColumnIndex_, adds jsonStringColumnIndices_, changes splitFieldsToNamesAndTypes() from const to non-const, and returns LazyVectors via ClpIrJsonStringVectorLoader for __json_string columns.
IR JSON loader (new)
velox/connectors/clp/search_lib/ir/ClpIrJsonStringVectorLoader.h, velox/connectors/clp/search_lib/ir/ClpIrJsonStringVectorLoader.cpp
Adds ClpIrJsonStringVectorLoader: holds filtered IR log events and implements loadInternal to serialize events to JSON and populate a String vector; fails on serialization errors.
Column handle
velox/connectors/clp/ClpColumnHandle.h
Adds public inline static std::string jsonStringColumnName_ = "__json_string".
Tests
velox/connectors/clp/tests/ClpConnectorTest.cpp
Adds test1JsonString and test2JsonString to verify projecting __json_string with other fields, applying filters/queries, and asserting JSON payloads (including IR output checks).

Sequence Diagram(s)

sequenceDiagram
    participant Exec as Query Executor
    participant Cursor as CLP Cursor (Archive / IR)
    participant Loader as Clp*JsonStringVectorLoader
    participant Source as SchemaReader / IR source

    Exec->>Cursor: loadSplit(projection)
    activate Cursor
    Cursor->>Cursor: parse projection, detect "__json_string"
    Cursor->>Cursor: record jsonStringColumnIndices_, set columnIndex_/projectedColumnIndex_
    deactivate Cursor

    Exec->>Cursor: createVectorHelper(column)
    activate Cursor
    Cursor->>Cursor: is column in jsonStringColumnIndices_?
    alt json_string column
        Cursor->>Loader: construct Loader(SourcePtr, filteredRows)
        activate Loader
        Loader-->>Cursor: return LazyVector(loader)
        deactivate Loader
    else regular column
        Cursor->>Source: materialize column data
        Source-->>Cursor: populated Vector
    end
    deactivate Cursor

    Note over Loader,Source: When LazyVector accessed, Loader serializes per-row to JSON
    Loader->>Source: serializeRowToJson(rowIndex)
    Source-->>Loader: json_string
    Loader->>Loader: write into StringVector
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "feat: Add support for CLP JSON string serialization." accurately captures the primary objective of the changeset. The raw summary reveals that the core changes involve introducing new vector loaders (ClpArchiveJsonStringVectorLoader and ClpIrJsonStringVectorLoader), defining a marker column "__json_string", and implementing per-column indexing to handle JSON serialization. While the PR includes supporting changes such as a CLP dependency update and structural refactoring in cursor classes, these are secondary to the main feature goal. The title is concise, specific, and clearly conveys the essential change that would be meaningful to a developer reviewing the pull request history.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9b56170 and 791f03e.

📒 Files selected for processing (1)
  • velox/connectors/clp/search_lib/ir/ClpIrCursor.cpp (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
velox/connectors/clp/search_lib/ir/ClpIrCursor.cpp (2)
velox/connectors/clp/search_lib/archive/ClpArchiveCursor.cpp (2)
  • fetchNext (48-93)
  • fetchNext (48-48)
velox/connectors/clp/search_lib/ir/ClpIrCursor.h (2)
  • columnIndex_ (91-91)
  • projectedColumnIndex_ (92-92)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build with GCC / Ubuntu release with resolve_dependency
🔇 Additional comments (6)
velox/connectors/clp/search_lib/ir/ClpIrCursor.cpp (6)

21-21: LGTM: Necessary includes added.

The includes for ClpColumnHandle and ClpIrJsonStringVectorLoader are required to support JSON string column handling.

Also applies to: 23-23


31-32: LGTM: Counter reset logic is correct.

Resetting both columnIndex_ and projectedColumnIndex_ at the start of fetchNext ensures proper per-batch iteration state management.


65-69: LGTM: Validation correctly accounts for JSON string columns.

The bounds check now properly validates that the sum of projected columns and JSON string columns does not exceed the total output columns.


126-134: LGTM: JSON string column detection is correct.

The method correctly identifies JSON string columns by name and records their indices while excluding them from type mapping. The removal of const from the method signature is necessary since it now modifies the jsonStringColumnIndices_ member.


207-214: LGTM: JSON string column handling logic is correct.

The code correctly increments columnIndex_ and returns a LazyVector with ClpIrJsonStringVectorLoader for JSON string columns, while properly skipping the increment of projectedColumnIndex_ since these columns aren't part of standard projections.


216-230: LGTM: Standard column processing indexing is correct.

The indexing logic properly coordinates columnIndex_ (for all columns) and projectedColumnIndex_ (for projected columns only):

  • Bounds check validates columnIndex_ before accessing outputColumns_[columnIndex_]
  • projectedColumnIndex_ is used for map lookups in projectedColumnIdxNodeIdsMap_
  • Both counters are incremented after processing a standard column

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@wraymo wraymo changed the title add clp_get_json_string support feat: Add support for CLP JSON string serialization Oct 18, 2025
@wraymo wraymo changed the title feat: Add support for CLP JSON string serialization feat: Add support for CLP JSON string serialization for archives Oct 18, 2025
@wraymo
wraymo marked this pull request as ready for review October 18, 2025 18:27

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cf89596 and f4632fd.

📒 Files selected for processing (7)
  • CMake/resolve_dependency_modules/clp.cmake (1 hunks)
  • velox/connectors/clp/search_lib/archive/CMakeLists.txt (1 hunks)
  • velox/connectors/clp/search_lib/archive/ClpArchiveCursor.cpp (7 hunks)
  • velox/connectors/clp/search_lib/archive/ClpArchiveCursor.h (2 hunks)
  • velox/connectors/clp/search_lib/archive/ClpArchiveJsonStringVectorLoader.cpp (1 hunks)
  • velox/connectors/clp/search_lib/archive/ClpArchiveJsonStringVectorLoader.h (1 hunks)
  • velox/connectors/clp/tests/ClpConnectorTest.cpp (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-20T18:00:51.241Z
Learnt from: anlowee
PR: y-scope/velox#25
File: velox/connectors/clp/search_lib/archive/ClpArchiveCursor.h:36-39
Timestamp: 2025-08-20T18:00:51.241Z
Learning: In ClpArchiveCursor.h, the clp_s::InputSource type is available through transitive inclusion via BaseClpCursor.h, which includes "clp_s/InputConfig.hpp" where InputSource is defined. The constructor parameter type is properly accessible without needing direct inclusion.

Applied to files:

  • velox/connectors/clp/search_lib/archive/ClpArchiveCursor.h
🧬 Code graph analysis (5)
velox/connectors/clp/search_lib/archive/ClpArchiveJsonStringVectorLoader.h (2)
velox/connectors/clp/search_lib/archive/ClpArchiveJsonStringVectorLoader.cpp (3)
  • ClpArchiveJsonStringVectorLoader (25-30)
  • loadInternal (32-47)
  • loadInternal (32-36)
velox/vector/LazyVector.h (1)
  • VectorLoader (177-210)
velox/connectors/clp/search_lib/archive/ClpArchiveCursor.h (1)
velox/connectors/clp/search_lib/archive/ClpQueryRunner.h (5)
  • clp_s (21-26)
  • clp_s (28-31)
  • clp_s (33-35)
  • vector (76-78)
  • ClpQueryRunner (43-88)
velox/connectors/clp/tests/ClpConnectorTest.cpp (1)
velox/python/type/type.pyi (1)
  • VARCHAR (29-29)
velox/connectors/clp/search_lib/archive/ClpArchiveJsonStringVectorLoader.cpp (1)
velox/connectors/clp/search_lib/archive/ClpArchiveJsonStringVectorLoader.h (1)
  • ClpArchiveJsonStringVectorLoader (29-44)
velox/connectors/clp/search_lib/archive/ClpArchiveCursor.cpp (2)
velox/connectors/clp/search_lib/archive/ClpArchiveCursor.h (2)
  • projectedColumnIndex_ (69-69)
  • columnIndex_ (68-68)
velox/connectors/clp/search_lib/archive/ClpQueryRunner.h (1)
  • vector (76-78)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build with GCC / Ubuntu release with resolve_dependency
🔇 Additional comments (18)
velox/connectors/clp/search_lib/archive/ClpArchiveCursor.h (3)

19-19: LGTM!

The <unordered_set> include is necessary for the jsonStringColumnIndices_ member.


25-25: LGTM!

Forward declaration is appropriate. Based on learnings, the type is available through transitive inclusion when needed.


60-70: LGTM!

The new members properly support the per-column indexing scheme for JSON string handling. The renaming from readerIndex_ to columnIndex_ improves clarity.

velox/connectors/clp/search_lib/archive/CMakeLists.txt (1)

18-18: LGTM!

The new JSON string vector loader source is correctly added to the build.

velox/connectors/clp/tests/ClpConnectorTest.cpp (2)

210-261: LGTM!

The test comprehensively validates JSON string serialization with proper assertions for the __json_string column alongside regular fields.


441-475: LGTM!

The test properly validates JSON string serialization with KQL pushdown filters and verifies the complete JSON payload structure.

velox/connectors/clp/search_lib/archive/ClpArchiveJsonStringVectorLoader.cpp (2)

25-30: LGTM!

The constructor properly initializes members and sets up the serializer.


41-46: Since clp_s is an external library, let me search for documentation about it and the return type of generate_json_string:

Let me search specifically for the clp_s library source code and the SchemaReader class:

Based on my investigation, I was unable to locate the exact source code for clp_s::SchemaReader::generate_json_string to verify the return type and lifetime guarantees. While I confirmed that clp_s is part of YScope's open-source Compressed Log Processor library, the specific method definition is not accessible in the web search results or the repository files I checked.

The original review comment raises a legitimate concern about StringView lifetime safety that requires verification from someone with access to the clp_s library documentation or source code.

StringView lifetime safety must be verified.

Line 44 constructs a StringView from the return value of schemaReader_->generate_json_string(messageIndex). Confirm that this method returns a string with appropriate lifetime (managed buffer in schemaReader_) rather than a temporary or reference, to avoid dangling pointers when StringView is stored in the vector.

velox/connectors/clp/search_lib/archive/ClpArchiveJsonStringVectorLoader.h (1)

31-43: LGTM!

The class interface is well-designed with appropriate encapsulation. The constructor signature matches the implementation, and the loadInternal override follows the VectorLoader contract.

velox/connectors/clp/search_lib/archive/ClpArchiveCursor.cpp (8)

24-24: LGTM!

Including the new JSON string vector loader header is necessary for the lazy loading implementation below.


49-50: LGTM!

Resetting both column indices at the start of fetchNext ensures proper state for the next batch.


74-76: LGTM!

The refactored approach using schemaReader_ and initialize_filter_with_column_map properly integrates the schema reader for JSON string generation.


104-104: LGTM!

The validation correctly accounts for JSON string columns that are handled separately from the projected columns.


154-159: LGTM!

The detection of __json_string columns and recording their indices is clean and correctly skips further processing for these special columns.


247-256: LGTM!

Pre-sizing the children vector with reserve and using explicit numChildren improves efficiency by avoiding reallocations during vector construction.


261-271: LGTM!

The lazy loading path for JSON string columns is well-implemented, using the new ClpArchiveJsonStringVectorLoader to generate JSON strings on demand.


273-286: LGTM!

The updated column access pattern correctly uses projectedColumnIndex_ to index into projectedColumns and retrieves the corresponding type from outputColumns_.

CMake/resolve_dependency_modules/clp.cmake (1)

19-19: Commit 9e991ab49681faff0aea259a37b489c3f416e06e exists and is accessible in the CLP repository.

Verification confirms the commit is available and publicly accessible via both direct git fetch and GitHub API. The commit has been successfully integrated into the y-scope/clp repository.

Comment thread velox/connectors/clp/search_lib/archive/ClpArchiveJsonStringVectorLoader.h Outdated
Comment thread velox/connectors/clp/tests/ClpConnectorTest.cpp Outdated
@wraymo
wraymo requested a review from gibber9809 October 20, 2025 14:55

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f4632fd and 3095fa0.

📒 Files selected for processing (2)
  • velox/connectors/clp/search_lib/archive/ClpArchiveJsonStringVectorLoader.h (1 hunks)
  • velox/connectors/clp/tests/ClpConnectorTest.cpp (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
velox/connectors/clp/tests/ClpConnectorTest.cpp (1)
velox/python/type/type.pyi (1)
  • VARCHAR (29-29)
velox/connectors/clp/search_lib/archive/ClpArchiveJsonStringVectorLoader.h (2)
velox/connectors/clp/search_lib/archive/ClpArchiveJsonStringVectorLoader.cpp (3)
  • ClpArchiveJsonStringVectorLoader (25-30)
  • loadInternal (32-47)
  • loadInternal (32-36)
velox/vector/LazyVector.h (1)
  • VectorLoader (177-210)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build with GCC / Ubuntu release with resolve_dependency
🔇 Additional comments (4)
velox/connectors/clp/search_lib/archive/ClpArchiveJsonStringVectorLoader.h (2)

23-42: Clean class design for on-demand JSON string loading.

The ClpArchiveJsonStringVectorLoader follows the VectorLoader pattern correctly and provides a focused API for generating serialized JSON strings from CLP archives. The design aligns well with Velox's lazy loading architecture and the PR's objective of supporting __json_string columns.


29-35: Verify lifecycle management of the raw SchemaReader pointer.

The class stores a raw pointer to SchemaReader* retrieved from archiveReader_->read_schema_table() (ClpArchiveCursor.cpp line 74–75). This pointer is passed to the loader which wraps it in a LazyVector for deferred access. The concern is valid: if schemaReader_ is reassigned in a subsequent loop iteration before the LazyVector accesses the loader, the raw pointer becomes stale. Ensure that:

  • The reference lifetime from read_schema_table() extends through the entire vector materialization cycle, or
  • Add documentation confirming that createVector() is always called synchronously within the same batch before schemaReader_ can be reassigned, or
  • Consider storing the SchemaReader reference directly or using a shared ownership model to guarantee validity across deferred access.
velox/connectors/clp/tests/ClpConnectorTest.cpp (2)

210-261: Solid test coverage for JSON string projection with filtering.

The test correctly verifies that __json_string columns return fully serialized JSON payloads for filtered rows. The expected JSON strings accurately reflect the complete row data, including null handling (line 250 shows "userId":null), and the test structure follows the established patterns in this file.


440-475: Review comment is incorrect — JSON string support is Archive-specific, not intended for IR splits.

The code evidence shows JSON string loading is implemented exclusively in ClpArchiveCursor (via jsonStringColumnIndices_ member variable) with no corresponding functionality in ClpIrCursor. Both test1JsonString and test2JsonString use only SplitType::kArchive. This architectural separation indicates JSON strings are an Archive-only feature rather than a gap requiring IR support. No action is needed.

Likely an incorrect or invalid review comment.

@wraymo wraymo changed the title feat: Add support for CLP JSON string serialization for archives feat: Add support for CLP JSON string serialization. Oct 22, 2025

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
velox/connectors/clp/search_lib/ir/ClpIrCursor.cpp (2)

217-237: Incorrect column mapping when JSON-string columns are present.

projectedColumnIndex_ indexes the list of non-JSON projected columns, but you read outputColumns_[projectedColumnIndex_], which includes JSON columns. This misaligns names/types once a __json_string appears before a non-JSON column (e.g., [requestId, __json_string, method]). Result: method is paired with "__json_string" metadata and wrong node-ids.

Apply a mapping from projected index → original output index (skip JSON columns):

-  auto projectedColumn = outputColumns_[projectedColumnIndex_];
-  auto projectedColumnType = projectedColumn.type;
-  auto it = projectedColumnIdxNodeIdsMap_.find(projectedColumnIndex_);
+  // Map projected (non-JSON) index to the corresponding index in outputColumns_.
+  auto toOutputIndex = [&](size_t projectedIdx) -> size_t {
+    size_t count = 0;
+    for (size_t i = 0; i < outputColumns_.size(); ++i) {
+      if (jsonStringColumnIndices_.find(i) != jsonStringColumnIndices_.end()) {
+        continue;
+      }
+      if (count == projectedIdx) {
+        return i;
+      }
+      ++count;
+    }
+    VELOX_FAIL("Projected column index {} out of range ({} non-JSON columns).",
+               projectedIdx, count);
+  };
+  const size_t outIdx = toOutputIndex(projectedColumnIndex_);
+  auto projectedColumn = outputColumns_[outIdx];
+  auto projectedColumnType = projectedColumn.type;
+  auto it = projectedColumnIdxNodeIdsMap_.find(projectedColumnIndex_);
@@
-  projectedColumnIndex_++;
+  ++projectedColumnIndex_;

Please add a unit/integration test with a projection like [__json_string, method] and another like [requestId, __json_string, method] to catch this class of bugs.


59-70: Optional: reset indices in createVector for robustness.

Resetting columnIndex_/projectedColumnIndex_ in fetchNext works today but is fragile if createVector is called more than once per fetch. Consider resetting them at the start of createVector.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3095fa0 and ee38e83.

📒 Files selected for processing (6)
  • velox/connectors/clp/search_lib/ir/CMakeLists.txt (1 hunks)
  • velox/connectors/clp/search_lib/ir/ClpIrCursor.cpp (4 hunks)
  • velox/connectors/clp/search_lib/ir/ClpIrCursor.h (1 hunks)
  • velox/connectors/clp/search_lib/ir/ClpIrJsonStringVectorLoader.cpp (1 hunks)
  • velox/connectors/clp/search_lib/ir/ClpIrJsonStringVectorLoader.h (1 hunks)
  • velox/connectors/clp/tests/ClpConnectorTest.cpp (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
velox/connectors/clp/search_lib/ir/ClpIrCursor.cpp (1)
velox/connectors/clp/search_lib/ir/ClpIrCursor.h (2)
  • columnIndex_ (89-89)
  • projectedColumnIndex_ (90-90)
velox/connectors/clp/search_lib/ir/ClpIrJsonStringVectorLoader.h (2)
velox/vector/LazyVector.h (1)
  • VectorLoader (177-210)
velox/connectors/clp/search_lib/ir/ClpIrJsonStringVectorLoader.cpp (2)
  • loadInternal (24-48)
  • loadInternal (24-28)
velox/connectors/clp/search_lib/ir/ClpIrCursor.h (1)
velox/connectors/clp/search_lib/ir/ClpIrCursor.cpp (2)
  • splitFieldsToNamesAndTypes (124-163)
  • splitFieldsToNamesAndTypes (125-125)
🪛 GitHub Actions: Run Checks
velox/connectors/clp/search_lib/ir/ClpIrJsonStringVectorLoader.h

[error] 1-1: End-of-file-fixer hook failed. The file was modified to add a newline at end of file.

velox/connectors/clp/search_lib/ir/ClpIrJsonStringVectorLoader.cpp

[error] 1-1: End-of-file-fixer hook failed. The file was modified to add a newline at end of file.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build with GCC / Ubuntu release with resolve_dependency
🔇 Additional comments (3)
velox/connectors/clp/search_lib/ir/CMakeLists.txt (1)

18-19: LGTM — source list updated correctly.

ClpIrJsonStringVectorLoader.cpp is included; linking remains appropriate. If this header is intended to be public/installed, ensure install rules export it.

velox/connectors/clp/search_lib/ir/ClpIrJsonStringVectorLoader.cpp (1)

1-50: Commit the updated file to fix the pre-commit EOF newline issue.

The end-of-file-fixer hook ensures that a file either is empty or ends with one newline. The CI hook detected that this file lacked a trailing newline and automatically added one. Commit the modified file to satisfy the pre-commit hook.

velox/connectors/clp/search_lib/ir/ClpIrJsonStringVectorLoader.h (1)

19-22: Remove unused BaseClpCursor.h include from both files.

The BaseClpCursor.h include is not used in either the header or implementation file. The class inherits from VectorLoader (not BaseClpCursor), and the implementation makes no reference to any types or symbols from BaseClpCursor. Remove the include from both line 20 in the header and line 19 in the implementation file.

⛔ Skipped due to learnings
Learnt from: anlowee
PR: y-scope/velox#25
File: velox/connectors/clp/search_lib/ClpVectorLoader.cpp:24-24
Timestamp: 2025-08-20T18:13:32.442Z
Learning: In ClpVectorLoader.cpp, the include of BaseClpCursor.h is necessary because the implementation uses ColumnType enum values (like ColumnType::Integer, ColumnType::Float) in a switch statement. While ClpVectorLoader.h has a forward declaration of ColumnType, the full definition from BaseClpCursor.h is required in the implementation file to access the enum values.

Comment thread velox/connectors/clp/search_lib/ir/ClpIrCursor.cpp Outdated
Comment thread velox/connectors/clp/search_lib/ir/ClpIrCursor.h
Comment thread velox/connectors/clp/search_lib/ir/ClpIrCursor.h
Comment thread velox/connectors/clp/search_lib/ir/ClpIrJsonStringVectorLoader.cpp Outdated
Comment thread velox/connectors/clp/search_lib/ir/ClpIrJsonStringVectorLoader.h Outdated
Comment thread velox/connectors/clp/search_lib/ir/ClpIrJsonStringVectorLoader.h
Comment on lines +35 to +39
void loadInternal(
RowSet rows,
ValueHook* hook,
vector_size_t resultSize,
VectorPtr* result) override;

@coderabbitai coderabbitai Bot Oct 22, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Use-after-free bug in the implementation.

While reviewing the header, I noticed a critical bug in the implementation file ClpIrJsonStringVectorLoader.cpp (lines 40-41):

std::string const jsonString = serializedResult.value().second.dump();
stringVector->set(vectorIndex, StringView(jsonString));

The jsonString variable is local to the loop iteration and will be destroyed, causing the StringView to point to deallocated memory. The string data must be copied into the vector's buffer.

The typical pattern in Velox is to use the vector's buffer or ensure the StringView points to persistent storage. Please verify that FlatVector<StringView>::set correctly handles copying the data, or modify the code to explicitly manage the string lifetime, for example:

std::string const jsonString = serializedResult.value().second.dump();
stringVector->set(vectorIndex, StringView(jsonString.data(), jsonString.size()));

Additionally, verify this pattern against similar VectorLoader implementations in the codebase to ensure correctness.

🤖 Prompt for AI Agents
In velox/connectors/clp/search_lib/ir/ClpIrJsonStringVectorLoader.cpp around
lines 40-41, the code creates a temporary std::string and passes it to
StringView which results in a use-after-free when the temporary is destroyed;
change the code to ensure the vector owns a copy of the bytes: create the JSON
string, then pass StringView(jsonString.data(), jsonString.size()) to
stringVector->set so the vector can copy the bytes (or, if
FlatVector<StringView>::set does not copy, explicitly copy the bytes into the
vector's owned buffer before setting the element). After making the change,
verify the pattern against other VectorLoader implementations in the codebase to
match Velox conventions.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When setting the vector, the string is actually copied

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find and examine the FlatVector::set implementation for StringView
echo "=== Searching for FlatVector::set implementation ==="
ast-grep --pattern 'void set($_, StringView $_) {
  $$$
}'

echo -e "\n=== Searching for FlatVector set method definitions ==="
rg -nP --type=cpp -A 10 'void\s+set\s*\(\s*vector_size_t.*StringView' velox/vector/

echo -e "\n=== Looking for StringView handling in FlatVector ==="
rg -nP --type=cpp -A 15 'class FlatVector.*StringView' velox/vector/

echo -e "\n=== Examining similar patterns in other VectorLoader implementations ==="
rg -nP --type=cpp -B 3 -A 3 'stringVector->set.*StringView' velox/

Length of output: 6332


🏁 Script executed:

#!/bin/bash
# Find FlatVector header files
echo "=== Finding FlatVector header files ==="
fd -e h -e hpp FlatVector velox/vector/

echo -e "\n=== Examining FlatVector.h for set method ==="
cat velox/vector/FlatVector.h | grep -A 30 "void set("

echo -e "\n=== Looking for StringView set implementation in FlatVector ==="
rg -nP --type=cpp -A 20 'template.*StringView.*\n.*void set\(' velox/vector/

echo -e "\n=== Searching for setNoCopy in FlatVector ==="
rg -nP --type=cpp -B 5 -A 10 'setNoCopy|setStringViewBufferHolder' velox/vector/FlatVector

Length of output: 1106


🏁 Script executed:

#!/bin/bash
# Look for FlatVector-inl.h which likely has template specializations
echo "=== Examining FlatVector-inl.h for StringView specialization ==="
cat velox/vector/FlatVector-inl.h | head -200

echo -e "\n=== Search for StringView specific set methods ==="
rg -nP --type=cpp -B 3 -A 15 'template<>\s*inline\s*void\s+FlatVector<StringView>::set' velox/vector/

echo -e "\n=== Search for StringView assignment or copy behavior ==="
rg -nP --type=cpp -B 3 -A 10 'StringView.*operator=|StringView.*copy' velox/vector/FlatVector-inl.h

echo -e "\n=== Look at the actual FlatVector-inl.h content for StringView ==="
grep -n -A 30 "template<>" velox/vector/FlatVector-inl.h | grep -A 30 StringView | head -50

Length of output: 7464


🏁 Script executed:

#!/bin/bash
# Search for StringView template specializations or overloads
echo "=== Looking for StringView specific template specializations ==="
rg -nP --type=cpp -B 5 -A 20 'template.*<>\s*.*FlatVector.*StringView|FlatVector<StringView>' velox/vector/ | head -100

echo -e "\n=== Search for setNoCopy specialization for StringView ==="
rg -nP --type=cpp -B 3 -A 20 'setNoCopy.*StringView' velox/vector/

echo -e "\n=== Check how StringView assignment works in the values buffer ==="
rg -nP --type=cpp -B 5 -A 10 'rawValues_\[.*\]\s*=.*StringView|StringView.*rawValues_' velox/vector/FlatVector-inl.h

echo -e "\n=== Look for Buffer or string copying mechanism ==="
rg -nP --type=cpp -B 3 -A 10 'StringView.*Buffer|addStringBuffer|stringBuffers_' velox/vector/FlatVector-inl.h | head -80

Length of output: 13748


🏁 Script executed:

#!/bin/bash
# Look for the StringView specialization of set method
echo "=== Search for template specialization of set for StringView ==="
cat velox/vector/FlatVector-inl.h | grep -B 10 -A 40 "template<>" | grep -B 10 -A 40 "StringView"

echo -e "\n=== Examine the entire setNoCopy implementation section ==="
rg -nP --type=cpp -B 10 -A 30 'template<>\s*inline\s*void\s+FlatVector<StringView>::setNoCopy' velox/vector/

echo -e "\n=== Look for any copying behavior related to StringView in set ==="
rg -nP --type=cpp -C 15 'StringView.*set\(|void set.*StringView' velox/vector/FlatVector-inl.h | head -150

Length of output: 193


🏁 Script executed:

#!/bin/bash
# Look at the complete FlatVector.h to understand set method
echo "=== Looking at the complete generic set method in FlatVector.h ==="
cat velox/vector/FlatVector.h | grep -B 5 -A 15 "void set(vector_size_t idx, T value)"

echo -e "\n=== Check if there's operator= overload for StringView that does copying ==="
rg -nP --type=cpp -B 5 -A 10 'operator=.*StringView|StringView.*operator=' velox/type/

echo -e "\n=== Look for ensureValues or string buffer management ==="
rg -nP --type=cpp -B 3 -A 15 'void ensureValues\(\)' velox/vector/FlatVector-inl.h

echo -e "\n=== Check StringView assignment in the actual code ==="
cat velox/vector/FlatVector-inl.h | grep -B 20 -A 20 "rawValues_\[.*\] = .*value"

Length of output: 5670


🏁 Script executed:

#!/bin/bash
# Look at StringView's inline string capability
echo "=== Examining StringView inline string behavior ==="
rg -nP --type=cpp -B 5 -A 30 'class StringView|struct StringView' velox/type/StringView.h | head -100

echo -e "\n=== Look for how FlatVector handles StringView assignment ==="
rg -nP --type=cpp -B 5 -A 25 'operator=.*inline|setInline|kPrefixSize|kInlineSize' velox/type/StringView.h | head -80

echo -e "\n=== Check if there's automatic buffer management in FlatVector for StringView ==="
rg -nP --type=cpp -B 3 -A 20 'addStringBuffer|acquireSharedStringBuffers|stringBuffer' velox/vector/FlatVector-inl.h | head -100

Length of output: 6534


🏁 Script executed:

#!/bin/bash
# Check the actual StringView size and how large strings are handled
echo "=== Check StringView inline size threshold ==="
grep -n "kInlineSize\|kPrefixSize" velox/type/StringView.h

echo -e "\n=== Look for how ClpArchiveJsonStringVectorLoader handles strings ==="
cat velox/connectors/clp/search_lib/archive/ClpArchiveJsonStringVectorLoader.cpp

echo -e "\n=== Check what generate_json_string returns ==="
rg -nP --type=cpp -B 3 -A 10 'generate_json_string|string generate_json' velox/connectors/clp/

echo -e "\n=== Check if StringView constructor copies for small strings ==="
cat velox/type/StringView.h | grep -B 5 -A 25 "StringView(const char\* data, int32_t len)"

Length of output: 4857


@wraymo, thank you for the clarification! You're absolutely right. Looking at the pattern across the codebase, I can see this same approach is used consistently in multiple places (ClpArchiveJsonStringVectorLoader.cpp, ClpIrVectorLoader.cpp, and even in test files). The widespread usage and your confirmation indicate that FlatVector<StringView>::set() does indeed handle copying the string data appropriately, likely through Velox's internal string buffer management.

For inline strings (≤12 bytes), StringView copies the data directly into its internal buffer, and for larger strings, the vector's buffer management ensures the data is preserved. My apologies for the false alarm on the use-after-free concern.

Comment on lines +210 to +285
TEST_F(ClpConnectorTest, test1JsonString) {
const std::shared_ptr<std::string> kqlQuery = nullptr;
auto plan = PlanBuilder()
.startTableScan()
.outputType(
ROW({"requestId", "__json_string", "method"},
{VARCHAR(), VARCHAR(), VARCHAR()}))
.tableHandle(std::make_shared<ClpTableHandle>(
kClpConnectorId, "test_1"))
.assignments({
{"requestId",
std::make_shared<ClpColumnHandle>(
"requestId", "requestId", VARCHAR())},
{"__json_string",
std::make_shared<ClpColumnHandle>(
"__json_string", "__json_string", VARCHAR())},
{"method",
std::make_shared<ClpColumnHandle>(
"method", "method", VARCHAR())},
})
.endTableScan()
.filter("method = 'GET'")
.planNode();

const auto methodVector = makeFlatVector<StringView>({
"GET",
"GET",
"GET",
"GET",
"GET",
});
auto output = getResults(
plan,
{makeClpSplit(
getExampleFilePath("test_1.clps"),
ClpConnectorSplit::SplitType::kArchive,
kqlQuery)});
auto expected = makeRowVector(
{// requestId
makeFlatVector<StringView>(
{"req-100", "req-105", "req-107", "req-109", "req-102"}),
// __json_string
makeFlatVector<StringView>({
R"({"timestamp":"2025-04-30T08:45:00Z","requestId":"req-100","userId":"user201","method":"GET","path":"/api/users/1","responseTimeMs":25,"status":200})",
R"({"timestamp":"2025-04-30T08:45:25Z","requestId":"req-105","userId":"user204","method":"GET","path":"/api/dashboard","responseTimeMs":155,"status":200})",
R"({"timestamp":"2025-04-30T08:45:35Z","requestId":"req-107","userId":"user202","method":"GET","path":"/api/users/2/details","responseTimeMs":41,"status":200})",
R"({"timestamp":"2025-04-30T08:45:45Z","requestId":"req-109","userId":"user203","method":"GET","path":"/api/products?category=books","responseTimeMs":88,"status":200})",
R"({"timestamp":"2025-04-30T08:45:10Z","requestId":"req-102","method":"GET","path":"/public/products","responseTimeMs":18,"status":200,"userId":null})",
}),
// method
methodVector});
test::assertEqualVectors(expected, output);

auto irOutput = getResults(
plan,
{makeClpSplit(
getExampleFilePath("test_1_ir.clp.zst"),
ClpConnectorSplit::SplitType::kIr,
kqlQuery)});
auto irExpected = makeRowVector(
{// requestId
makeFlatVector<StringView>(
{"req-100", "req-102", "req-105", "req-107", "req-109"}),
// __json_string
makeFlatVector<StringView>({
R"({"method":"GET","path":"/api/users/1","requestId":"req-100","responseTimeMs":25,"status":200,"timestamp":"2025-04-30T08:45:00Z","userId":"user201"})",
R"({"method":"GET","path":"/public/products","requestId":"req-102","responseTimeMs":18,"status":200,"timestamp":"2025-04-30T08:45:10Z","userId":null})",
R"({"method":"GET","path":"/api/dashboard","requestId":"req-105","responseTimeMs":155,"status":200,"timestamp":"2025-04-30T08:45:25Z","userId":"user204"})",
R"({"method":"GET","path":"/api/users/2/details","requestId":"req-107","responseTimeMs":41,"status":200,"timestamp":"2025-04-30T08:45:35Z","userId":"user202"})",
R"({"method":"GET","path":"/api/products?category=books","requestId":"req-109","responseTimeMs":88,"status":200,"timestamp":"2025-04-30T08:45:45Z","userId":"user203"})",
}),
// method
methodVector});
test::assertEqualVectors(irExpected, irOutput);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Make JSON assertions resilient to key-ordering.

Comparing full JSON strings is brittle. Parse and assert on fields (or compare parsed objects) to avoid failures if key order changes.

Example approach:

  • Parse with folly::parseJson and compare selected keys (path, method, requestId, status).
  • Or parse and compare to an expected folly::dynamic built from fields.

Minimal change: additionally assert that each value is valid JSON and contains required keys.

🤖 Prompt for AI Agents
velox/connectors/clp/tests/ClpConnectorTest.cpp lines 210-285: the test
currently compares raw JSON strings which is brittle to key ordering; update the
assertions to parse each __json_string with folly::parseJson and compare
selected fields (e.g., requestId, method, path, status) or compare to an
expected folly::dynamic constructed from those fields instead of the whole
string; for both the archive and IR result vectors iterate rows, parse the JSON,
assert parsing succeeded, assert required keys exist, and assert the selected
values equal the expected values (keep the existing requestId and method vector
comparisons but replace the full-string __json_string equality with these
parsed-field checks).

Comment thread velox/connectors/clp/tests/ClpConnectorTest.cpp

@gibber9809 gibber9809 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work! Apologies about the delay on the review.

Mostly a small style comment and a few questions.

for (auto const& column : outputColumns_) {
for (size_t i = 0; i < outputColumns_.size(); i++) {
auto const& column = outputColumns_[i];
if ("__json_string" == column.name) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we replace "__json_string" with a constexpr constant throughout the code?

Also, is it ever possible for a user's column to have this name, or is that protected against somehow?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, the user can use any column names if they are quoted. There doesn't seem to be a special column name that we can use exclusively for the json column.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to handle this by giving the output column a special "full JSON string" type then? If so could we add that as a backlog issue to fix this up later? Otherwise fine with this approach for now.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I missed the point of using a constant. Just pushed the fix. Yeah, we could do it internally, but the problem is the presto coordinator side. Probably we should add a field in ClpColumnHandle to indicate if it's a json string column.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's worth adding to the backlog then -- I think it makes sense to use a more robust approach since it should be possible.

auto& reader =
archiveReader_->read_schema_table(currentSchemaId_, false, false);
reader.initialize_filter_with_column_map(queryRunner_.get());
schemaReader_ =

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're passing this pointer to the JSON string vector loader, and the result of read_schema_table is invalidated by the next call to read_schema_table, I just want to double check that we're handling the lifetimes correctly.

Can we ever call read_schema_table multiple times while hanging onto this pointer?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good question. I think the situation is similar to how we handle ColumnReader, since it's also stored in the vector loader. From my understanding, Velox fully processes each input vector before moving on to the next one. For every vector we return, its data is confined within a single schema table — it never spans across multiple ones. So this approach should be fine.

@wraymo
wraymo requested a review from gibber9809 October 22, 2025 22:03
gibber9809
gibber9809 previously approved these changes Oct 22, 2025

@gibber9809 gibber9809 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Title is fine as well. Take a look my last comment about potentially changing the way we indicate full JSON string serialization is required, but I don't want to block this PR based on that.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
velox/connectors/clp/search_lib/archive/ClpArchiveCursor.cpp (2)

103-110: Avoid copying projectedColumns and initialise indices here.

Minor: getProjectedColumns() returns a vector; copying it is unnecessary. Bind by const reference and initialise indices here (see paired diff in fetchNext() comment).

Apply this diff (if you didn’t apply the combined patch already):

-  auto projectedColumns = getProjectedColumns();
+  const auto& projectedColumns = getProjectedColumns();
+  columnIndex_ = 0;
+  projectedColumnIndex_ = 0;

220-224: Duplicate timestamp index evaluation (minor).

EvaluateTimestampIndex is executed twice (Lines 139‑143 and again here). Unless required due to state changes between calls, consider removing the second run to save work.

-  EvaluateTimestampIndex timestamp_index(timestampDict);
-  if (EvaluatedValue::False == timestamp_index.run(expr_)) {
-    VLOG(2) << "No matching timestamp ranges for query '" << query_ << "'";
-    return ErrorCode::InvalidTimestampRange;
-  }
♻️ Duplicate comments (3)
velox/connectors/clp/search_lib/ir/ClpIrCursor.cpp (1)

205-216: Replace C++20 contains() with C++17-compatible code.

Line 206 uses std::unordered_set::contains() which requires C++20, but Velox builds with C++17.

Apply this diff:

-  const bool isJsonString = jsonStringColumnIndices_.contains(columnIndex_);
+  const bool isJsonString =
+      jsonStringColumnIndices_.find(columnIndex_) != jsonStringColumnIndices_.end();
velox/connectors/clp/search_lib/archive/ClpArchiveCursor.cpp (2)

75-78: Schema reader pointer lifetime through LazyVector loaders needs a hard guarantee.

schemaReader_ stores the address of ArchiveReader::read_schema_table(...). That object is documented to be invalidated by subsequent read_schema_table calls. Since the pointer is captured by LazyVector loaders, please confirm that no code path calls read_schema_table again until all LazyVectors produced for the current schema are fully materialised and released. If that guarantee is not absolute, pass a stable handle (e.g., archiveReader_ + currentSchemaId_) to the loader and reacquire the schema table inside the loader per load.

Would you like me to sketch a minimal change to make the loader hold (archiveReader_, currentSchemaId_) instead of a raw pointer?


23-26: Good move to centralise the marker name.

Using ClpColumnHandle::jsonStringColumnName_ removes string literals and keeps things consistent. Given prior discussion about potential name collisions with user columns, let’s track a follow‑up to migrate to a dedicated logical “full JSON string” type in the handle/typing layer. I can open a backlog item if helpful.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2c27605 and f13eb81.

📒 Files selected for processing (3)
  • velox/connectors/clp/ClpColumnHandle.h (1 hunks)
  • velox/connectors/clp/search_lib/archive/ClpArchiveCursor.cpp (7 hunks)
  • velox/connectors/clp/search_lib/ir/ClpIrCursor.cpp (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
velox/connectors/clp/search_lib/archive/ClpArchiveCursor.cpp (2)
velox/connectors/clp/search_lib/archive/ClpArchiveCursor.h (2)
  • projectedColumnIndex_ (69-69)
  • columnIndex_ (68-68)
velox/connectors/clp/search_lib/archive/ClpQueryRunner.h (1)
  • vector (76-78)
velox/connectors/clp/search_lib/ir/ClpIrCursor.cpp (2)
velox/connectors/clp/search_lib/archive/ClpArchiveCursor.cpp (2)
  • fetchNext (48-93)
  • fetchNext (48-48)
velox/connectors/clp/search_lib/ir/ClpIrCursor.h (2)
  • columnIndex_ (91-91)
  • projectedColumnIndex_ (92-92)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build with GCC / Ubuntu release with resolve_dependency
🔇 Additional comments (7)
velox/connectors/clp/ClpColumnHandle.h (1)

45-46: LGTM!

The inline static constant is correctly defined and appropriately scoped as public for use across the CLP connector components.

velox/connectors/clp/search_lib/ir/ClpIrCursor.cpp (4)

21-21: LGTM!

The new includes are necessary for JSON string column support.

Also applies to: 23-23


31-32: LGTM!

Resetting both index counters at the start of fetchNext is correct and consistent with the archive cursor pattern.


126-164: LGTM!

The logic correctly identifies __json_string columns by name, records their indices in outputColumns_, and excludes them from the projected column list passed to the query handler.


65-69: Consider more precise validation.

The validation checks that resolved columns plus JSON string columns don't exceed total columns, but projectedColumnIdxNodeIdsMap_ may not contain all projected columns (only resolved ones). A more precise check would verify that the number of projected columns (non-JSON-string) plus JSON string columns equals the total column count.

Example:

-  VELOX_CHECK_LE(
-      projectedColumnIdxNodeIdsMap_.size() + jsonStringColumnIndices_.size(),
+  const size_t projectedColumnCount = outputColumns_.size() - jsonStringColumnIndices_.size();
+  VELOX_CHECK_EQ(
+      projectedColumnCount + jsonStringColumnIndices_.size(),
       outputColumns_.size(),
-      "Resolved node-id map size ({}) must not exceed projected columns and json string columns ({})",
-      projectedColumnIdxNodeIdsMap_.size() + jsonStringColumnIndices_.size(),
+      "Projected columns ({}) plus JSON string columns ({}) must equal total columns ({})",
+      projectedColumnCount,
+      jsonStringColumnIndices_.size(),
       outputColumns_.size());

Likely an incorrect or invalid review comment.

velox/connectors/clp/search_lib/archive/ClpArchiveCursor.cpp (2)

246-253: Row children preallocation LGTM.

children.reserve(numChildren) and iterating with uint32_t are appropriate here.


274-281: Bounds check and reader/type lookup LGTM.

The bounds check before dereferencing projectedColumns and mapping via projectedColumnIndex_ is correct.

Comment thread velox/connectors/clp/search_lib/archive/ClpArchiveCursor.cpp
Comment thread velox/connectors/clp/search_lib/archive/ClpArchiveCursor.cpp
Comment thread velox/connectors/clp/search_lib/archive/ClpArchiveCursor.cpp Outdated
Comment thread velox/connectors/clp/search_lib/ir/ClpIrCursor.cpp

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (5)
velox/connectors/clp/search_lib/ir/ClpIrCursor.cpp (1)

206-206: Build break: unordered_set::contains requires C++20.

Velox builds with C++17. Replace contains(...) with find(...) != end().

Apply this diff to fix the compatibility issue:

-  if (jsonStringColumnIndices_.contains(columnIndex_)) {
+  if (jsonStringColumnIndices_.find(columnIndex_) != jsonStringColumnIndices_.end()) {
velox/connectors/clp/search_lib/archive/ClpArchiveCursor.cpp (4)

75-78: Pointer lifetime: document invariants for schemaReader_.

schemaReader_ stores the address of ArchiveReader::read_schema_table(...). Ensure LazyVectors are fully materialised before any subsequent read_schema_table() call that would invalidate this pointer. Add a brief comment/assert to encode this invariant.


50-51: Reset column counters at vector creation (not in fetchNext) to avoid state leaks.

fetchNext() may be called once per batch, but createVector() can be called multiple times; keeping resets here is brittle. Move resets to the start of createVector(), and bind projectedColumns as a const reference to avoid an extra copy.

Apply these diffs:

--- a/velox/connectors/clp/search_lib/archive/ClpArchiveCursor.cpp
+++ b/velox/connectors/clp/search_lib/archive/ClpArchiveCursor.cpp
@@
   filteredRowIndices_->clear();
-  projectedColumnIndex_ = 0;
-  columnIndex_ = 0;
--- a/velox/connectors/clp/search_lib/archive/ClpArchiveCursor.cpp
+++ b/velox/connectors/clp/search_lib/archive/ClpArchiveCursor.cpp
@@
 VectorPtr ClpArchiveCursor::createVector(
     memory::MemoryPool* pool,
     const TypePtr& vectorType,
     size_t vectorSize) {
-  auto projectedColumns = getProjectedColumns();
+  const auto& projectedColumns = getProjectedColumns();
+  // Reset per-call to guarantee deterministic mapping regardless of how
+  // many times createVector() is invoked for the current batch.
+  projectedColumnIndex_ = 0;
+  columnIndex_ = 0;
   VELOX_CHECK_EQ(
       projectedColumns.size() + jsonStringColumnIndices_.size(),
       outputColumns_.size(),
       "Projected columns size {} does not match fields size {}",
       projectedColumns.size(),
       outputColumns_.size());
   return createVectorHelper(pool, vectorType, vectorSize, projectedColumns);
 }

Also applies to: 103-111


155-160: Fail fast if “__json_string” isn’t declared as String at planning time.

Without this, mismatched types surface later during materialisation. Add a check when detecting the marker column.

@@
-    for (size_t i = 0; i < outputColumns_.size(); i++) {
+    for (size_t i = 0; i < outputColumns_.size(); i++) {
       auto const& column = outputColumns_[i];
       if (ClpColumnHandle::jsonStringColumnName_ == column.name) {
+        VELOX_CHECK_EQ(
+            column.type,
+            ColumnType::String,
+            "__json_string must be declared as a String/VARCHAR column");
         jsonStringColumnIndices_.insert(i);
         continue;
       }

262-271: Runtime guard: “__json_string” must materialise as VARCHAR.

Add a TypeKind check before constructing the JSON-string LazyVector to improve diagnostics and prevent undefined behaviour.

@@
-  if (jsonStringColumnIndices_.contains(columnIndex_)) {
+  if (jsonStringColumnIndices_.contains(columnIndex_)) {
+    VELOX_CHECK_EQ(
+        vectorType->kind(),
+        TypeKind::VARCHAR,
+        "__json_string columns must materialise as VARCHAR");
     ++columnIndex_;
     return std::make_shared<LazyVector>(
         pool,
         vectorType,
         vectorSize,
         std::make_unique<ClpArchiveJsonStringVectorLoader>(
             schemaReader_, filteredRowIndices_),
         std::move(vector));
   }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f13eb81 and 9b56170.

📒 Files selected for processing (2)
  • velox/connectors/clp/search_lib/archive/ClpArchiveCursor.cpp (7 hunks)
  • velox/connectors/clp/search_lib/ir/ClpIrCursor.cpp (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
velox/connectors/clp/search_lib/ir/ClpIrCursor.cpp (2)
velox/connectors/clp/search_lib/archive/ClpArchiveCursor.cpp (2)
  • fetchNext (48-93)
  • fetchNext (48-48)
velox/connectors/clp/search_lib/ir/ClpIrCursor.h (2)
  • columnIndex_ (91-91)
  • projectedColumnIndex_ (92-92)
velox/connectors/clp/search_lib/archive/ClpArchiveCursor.cpp (2)
velox/connectors/clp/search_lib/archive/ClpArchiveCursor.h (2)
  • projectedColumnIndex_ (69-69)
  • columnIndex_ (68-68)
velox/connectors/clp/search_lib/archive/ClpQueryRunner.h (1)
  • vector (76-78)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build with GCC / Ubuntu release with resolve_dependency
🔇 Additional comments (6)
velox/connectors/clp/search_lib/ir/ClpIrCursor.cpp (5)

21-21: LGTM: Includes are necessary for JSON string support.

The new includes provide the column name constant and the JSON string vector loader class required by the new functionality.

Also applies to: 23-23


31-32: LGTM: Counter reset aligns with new indexing scheme.

Resetting both columnIndex_ and projectedColumnIndex_ at the start of each fetch is correct and consistent with the parallel implementation in ClpArchiveCursor.cpp.


65-69: LGTM: Validation correctly accounts for JSON string columns.

The bounds check and error message properly reflect the new distinction between projected columns and JSON string columns.


126-164: LGTM: JSON string column detection logic is correct.

The method properly identifies __json_string columns by name, records their indices in jsonStringColumnIndices_, and excludes them from the projection list passed to the query handler. The signature change to non-const is necessary since the method now modifies member state.


220-230: Indexing logic is correct.

The use of columnIndex_ to access outputColumns_[columnIndex_] (line 220) and projectedColumnIndex_ for the map lookup (line 222) properly addresses the indexing bug flagged in previous reviews. Both counters are correctly incremented after processing a non-JSON-string column.

velox/connectors/clp/search_lib/archive/ClpArchiveCursor.cpp (1)

23-26: Includes look good.

Using ClpColumnHandle::jsonStringColumnName_ and adding the JSON-string loader header is the right direction.

Comment thread velox/connectors/clp/search_lib/archive/ClpArchiveCursor.cpp
Comment thread velox/connectors/clp/search_lib/ir/ClpIrCursor.cpp
@wraymo
wraymo requested a review from gibber9809 October 23, 2025 21:14

@gibber9809 gibber9809 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New changes LGTM.

@wraymo
wraymo merged commit 425b66f into y-scope:presto-0.293-clp-connector Oct 24, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants