Skip to content

(improvement) Add VectorType support to numpy_parser for 2D array parsing - #731

Draft
mykaul wants to merge 3 commits into
scylladb:masterfrom
mykaul:vector-numpy-parser-2d
Draft

(improvement) Add VectorType support to numpy_parser for 2D array parsing#731
mykaul wants to merge 3 commits into
scylladb:masterfrom
mykaul:vector-numpy-parser-2d

Conversation

@mykaul

@mykaul mykaul commented Mar 7, 2026

Copy link
Copy Markdown

Summary

  • Add native VectorType support to the NumPy row parser, producing 2D masked arrays of shape (num_rows, vector_dimension) instead of falling back to object arrays
  • Enables zero-copy vector data ingestion for ML/AI workloads using the NumPy result path

Details

Changes to cassandra/numpy_parser.pyx:

  • make_array() detects VectorType columns and creates 2D np.ma.empty((array_size, vector_size), dtype=...) arrays with the correct numeric dtype (float32, float64, int32, int64, int16)
  • ArrDesc extended with mask_stride field to handle 2D mask arrays where stride = vector_dimension bools (not 1 bool)
  • unpack_row() uses direct memcpy of the full vector payload (e.g., 3072 bytes for float[768]) into the pre-allocated 2D array buffer
  • make_native_byteorder() handles bulk byte-swap on any-dimensional arrays transparently
  • Falls back to object arrays for unsupported vector subtypes

Result: For a query returning N rows of Vector<float, 768>, the NumpyParser produces an (N, 768) float32 array directly from wire bytes — the fastest possible path when consuming results as numpy arrays.

Tests: Comprehensive unit tests in tests/unit/test_numpy_parser.py covering all supported numeric subtypes, NULL handling, mask strides, and unsupported type fallback.

This commit is fully independent — it only modifies numpy_parser.pyx and adds a new test file.

Copilot AI 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.

Pull request overview

This PR adds native VectorType handling to the Cython NumPy row parser so vector columns can be parsed into 2D NumPy (masked) arrays instead of falling back to object arrays, improving performance for vector/embedding workloads.

Changes:

  • Extend cassandra/numpy_parser.pyx to allocate 2D arrays for VectorType columns and correctly advance/mark masks for 2D shapes.
  • Add unit tests in tests/unit/test_numpy_parser.py covering several numeric vector subtypes and mixed scalar+vector results.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 12 comments.

File Description
cassandra/numpy_parser.pyx Adds VectorType 2D array allocation + 2D mask stride handling; updates NULL-mask write logic.
tests/unit/test_numpy_parser.py Introduces unit tests for vector parsing into 2D NumPy arrays.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/unit/test_numpy_parser.py Outdated
Comment thread tests/unit/test_numpy_parser.py Outdated
Comment thread tests/unit/test_numpy_parser.py Outdated
Comment thread tests/unit/test_numpy_parser.py Outdated
Comment thread tests/unit/test_numpy_parser.py Outdated
Comment thread tests/unit/test_numpy_parser.py Outdated
Comment thread tests/unit/test_numpy_parser.py Outdated
Comment thread tests/unit/test_numpy_parser.py Outdated
Comment thread tests/unit/test_numpy_parser.py Outdated
Comment thread tests/unit/test_numpy_parser.py Outdated
Copilot AI review requested due to automatic review settings July 29, 2026 15:22
@mykaul
mykaul force-pushed the vector-numpy-parser-2d branch from 6474dae to e73ef19 Compare July 29, 2026 15:22
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 90103dde-aa46-46cc-a27a-fe374b92b1e6

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The NumPy parser now supports VectorType columns by allocating 2D masked arrays for supported fixed-width subtypes and object arrays for unsupported subtypes. Row unpacking validates payload sizes, tracks separate data and mask strides, zero-fills NULL data, and supports native byte-order conversion for 2D arrays. New tests cover numeric vectors, mixed columns, large dimensions, NULL handling, malformed payloads, and fallback behavior.

Sequence Diagram(s)

sequenceDiagram
  participant BytesIOReader
  participant NumpyParser
  participant make_array
  participant NumPyArray
  BytesIOReader->>NumpyParser: provide row payloads
  NumpyParser->>make_array: allocate VectorType result
  make_array->>NumPyArray: create 2D masked or object array
  NumpyParser->>NumPyArray: validate and copy vector data
  NumpyParser->>NumPyArray: apply NULL mask and zero data
Loading

Suggested labels: enhancement, area/Driver_-_python-driver

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the main change: adding VectorType support for 2D NumPy parsing.
Description check ✅ Passed The description includes a summary, implementation details, and test coverage, matching the template's main requirements.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

@mykaul

mykaul commented Jul 29, 2026

Copy link
Copy Markdown
Author

Cleaned this up: rebased onto current master (zero conflicts), and closed out the review feedback.

What was already fixed (previous commit 6474daec0), now verified against live code and marked resolved on all 12 copilot review threads:

  • Copyright header on the new test file (DataStax -> ScyllaDB)
  • Missing test coverage for NULL/mask-stride handling and unsupported-subtype fallback
  • Hard-coded little-endian dtypes ('<f4', '<f8', '<i2', '<i4', '<i8') across every test replaced with native numpy dtypes
  • Dead mask_true constant and unused uint8_t cimport removed
  • Unused Mock import removed
  • The memcpy buffer-overflow guard in unpack_row() (added right before the raw-copy fast path for VectorType columns)

What this pass added on top:

  • The overflow guard only checked buf.size > arr.stride, which blocks the memory-safety-critical direction (overflow) but let undersized payloads (buf.size < arr.stride) silently pass through, leaving the remainder of the row filled with uninitialized/stale heap bytes rather than raising. Tightened it to buf.size != arr.stride so both directions are rejected, with an updated error message.
  • Added test coverage for both the oversized and undersized payload cases, since neither had a test before.
  • Reverted an unrelated bit of diff noise in make_arrays() (arrays = []/.append() -> [None]*n/index-assign, functionally identical) back to the original pattern for a cleaner diff.
  • Ran the full unit suite (tests/unit/test_numpy_parser.py, tests/unit/test_types.py, and the broader tests/unit/ suite) after rebuilding the Cython extension - all green (747 passed, 38 skipped).

This is still a draft/perf-experiment PR, so flagging rather than blocking: the same unchecked-length-before-memcpy pattern that motivated the bounds check here also seems to recur in PR #689's Cython deserializers. Might be worth a shared bounds-checking convention/helper addressed once across both PRs rather than fixing it ad hoc per PR - leaving that decision to a human rather than filing it myself.

Copilot AI 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.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (3)

cassandra/numpy_parser.pyx:146

  • The new 2-D vector buffer is later passed to make_native_byteorder(), where arr.byteswap() uses its default inplace=False. On little-endian hosts this allocates and copies a second full vector result, doubling peak data-buffer memory for the large embedding workloads this path targets. Because this array is newly allocated and exclusively owned, byte-swapping it in place before changing the dtype would avoid that extra full-size copy.
            a = np.ma.empty((array_size, vector_size), dtype=dtype)

tests/unit/test_numpy_parser.py:32

  • This broad ImportError handler also catches failures importing cassandra.numpy_parser or its Cython dependencies and then marks NumPy unavailable, causing every new test to be skipped. A broken or missing parser extension can therefore make this suite pass without exercising the PR. Gate imports using the repository's HAVE_CYTHON/HAVE_NUMPY flags, but let parser import failures surface when both dependencies are expected.
except ImportError:
    HAVE_NUMPY = False

cassandra/numpy_parser.pyx:189

  • A NULL numeric/vector value only masks the row; its backing bytes remain whatever np.ma.empty() left in the allocation. The masked array exposes those bytes through its public .data attribute, so each NULL vector can return a full row of stale process-heap data—the same information leak the undersized-payload test is intended to prevent. Zero the row before setting its mask.
        else:
            memset(<char *>arr.mask_ptr, 1, arr.mask_stride)

@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.

🧹 Nitpick comments (2)
cassandra/numpy_parser.pyx (1)

182-195: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Good fix — strict size equality closes the buffer-overflow/under-copy hole.

The buf.size != arr.stride guard correctly rejects both oversized and undersized payloads before memcpy, preventing heap overflow (oversized) and stale/uninitialized data (undersized). The mask_stride-based pointer advance also correctly generalizes the previous fixed-increment logic to support 2D vector masks.

One small improvement: the error message only includes the column index, not its name, making it harder to diagnose which column failed for wide result sets.

💡 Include column name in the error message
             if buf.size != arr.stride:
                 raise ValueError(
-                    "Column %d: received %d bytes but array stride is %d "
+                    "Column %d (%s): received %d bytes but array stride is %d "
                     "(payload must exactly match the expected element size)" %
-                    (i, buf.size, arr.stride))
+                    (i, desc.colnames[i], buf.size, arr.stride))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cassandra/numpy_parser.pyx` around lines 182 - 195, Update the ValueError
raised in the buffer-size validation within the result parsing loop to include
the failing column’s name alongside its index, while preserving the existing
size details and strict equality check. Use the column-name symbol already
available in the surrounding parsing context; do not alter the memcpy or pointer
advancement logic.
tests/unit/test_numpy_parser.py (1)

411-424: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider an end-to-end fallback test through parse_rows().

This test only checks make_array()'s allocation shape/dtype for an unsupported subtype. It doesn't exercise the actual unpack_row object-path (from_binary deserialization + Py_INCREF storage) for a VectorType column with an unsupported subtype, which is the path that matters at runtime.

🧪 Suggested additional test
def test_unsupported_subtype_full_parse_round_trip(self):
    """Test full parse_rows() round trip for an unsupported vector subtype"""
    vector_type = self._create_vector_type(cqltypes.UTF8Type, 2)
    # build a buffer using VectorType.serialize()/protocol wire format
    # and assert result["vec"] is a 1D object array of lists
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/test_numpy_parser.py` around lines 411 - 424, Add an end-to-end
test alongside test_unsupported_subtype_falls_back_to_object_array that builds a
VectorType UTF8 payload using its serialization/protocol wire format, parses it
through parse_rows(), and verifies the resulting “vec” column is a 1D object
array containing the expected deserialized lists. Exercise the unpack_row object
path rather than testing make_array() directly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@cassandra/numpy_parser.pyx`:
- Around line 182-195: Update the ValueError raised in the buffer-size
validation within the result parsing loop to include the failing column’s name
alongside its index, while preserving the existing size details and strict
equality check. Use the column-name symbol already available in the surrounding
parsing context; do not alter the memcpy or pointer advancement logic.

In `@tests/unit/test_numpy_parser.py`:
- Around line 411-424: Add an end-to-end test alongside
test_unsupported_subtype_falls_back_to_object_array that builds a VectorType
UTF8 payload using its serialization/protocol wire format, parses it through
parse_rows(), and verifies the resulting “vec” column is a 1D object array
containing the expected deserialized lists. Exercise the unpack_row object path
rather than testing make_array() directly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ee4bb2b7-c632-4736-a43e-0846f115f04c

📥 Commits

Reviewing files that changed from the base of the PR and between bcc2d3d and e73ef19.

📒 Files selected for processing (2)
  • cassandra/numpy_parser.pyx
  • tests/unit/test_numpy_parser.py

mykaul added 2 commits July 30, 2026 17:44
Extend NumpyParser to handle VectorType columns by creating 2D NumPy
arrays (rows × vector_dimension) instead of object arrays. This enables
zero-copy parsing for vector embeddings in ML/AI workloads.

Features:
- Detects VectorType via vector_size and subtype attributes
- Creates 2D masked arrays for numeric vector subtypes with a fixed
  per-element wire width (float, double, int32, bigint)
- Falls back to object arrays for subtypes without a fixed wire width,
  including smallint (ShortType does not override serial_size(), so
  Cassandra/Scylla vint-prefixes each element inside a vector instead of
  using a fixed 2-byte field -- mapping it to the fast path would crash
  on a stride mismatch instead of safely falling back)
- Handles endianness conversion for both 1D and 2D arrays, byteswapping
  the freshly-allocated (exclusively owned) array in place instead of
  taking a full extra copy
- Zeroes the data bytes of masked/NULL rows so no uninitialized heap
  memory is exposed for a NULL vector
- Pre-allocates result arrays for efficiency

Supported vector types (fast path):
- Vector<float> → 2D float32 array
- Vector<double> → 2D float64 array
- Vector<int> → 2D int32 array
- Vector<bigint> → 2D int64 array

Vector<smallint> falls back to a 1D object array (see above).

Adds comprehensive test coverage for all supported vector types,
mixed column queries, and large vector dimensions (384-element embeddings),
including round-trip tests that serialize via the actual production
VectorType.serialize() rather than hand-rolled wire bytes.

Signed-off-by: Yaniv Kaul <yaniv.kaul@scylladb.com>
- Add buffer size guard before memcpy in unpack_row() to prevent overflow
- Remove dead mask_true constant and unused uint8_t cimport
- Fix copyright header (DataStax -> ScyllaDB)
- Replace hard-coded little-endian dtypes with native numpy dtypes
- Remove unused Mock import
- Add test for NULL vector mask handling
- Add test for unsupported subtype fallback to object array
Copilot AI review requested due to automatic review settings July 30, 2026 14:48
@mykaul
mykaul force-pushed the vector-numpy-parser-2d branch from e73ef19 to 83a5989 Compare July 30, 2026 14:48

Copilot AI 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.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

cassandra/numpy_parser.pyx:75

  • Removing ShortType from this shared map also changes ordinary smallint result columns: make_array() below now takes its KeyError fallback and returns a 1-D object array instead of the existing masked int16 array. Keep ShortType in the scalar mapping and exclude it only when selecting the VectorType fast path.
# Note: ShortType (smallint) is intentionally absent from this table. Unlike
# LongType/Int32Type/FloatType/DoubleType/CounterColumnType, ShortType does
# not override serial_size() in cassandra.cqltypes, so it has no fixed

cassandra/numpy_parser.pyx:78

  • The PR description promises a 2-D int16 fast path for supported numeric vectors, but this exclusion makes VectorType<smallint> return a 1-D object array, and the new test explicitly locks in that fallback. Either implement the advertised int16 path (including the correct wire-size contract) or update the PR description to remove that claim.
# Note: ShortType (smallint) is intentionally absent from this table. Unlike
# LongType/Int32Type/FloatType/DoubleType/CounterColumnType, ShortType does
# not override serial_size() in cassandra.cqltypes, so it has no fixed
# per-element width when serialized *inside* a VectorType: the server
# vint-prefixes each smallint element rather than encoding it as a plain
# 2-byte big-endian field. Mapping it here would make the vector fast-path

@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

🧹 Nitpick comments (2)
tests/unit/test_numpy_parser.py (2)

316-357: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider covering mask-stride advancement with a NULL in both columns.

This mixed-column test only uses non-NULL values, so the separate mask_ptr/mask_stride advancement in unpack_row (cassandra/numpy_parser.pyx lines 210-212) isn't actually exercised — a scalar column has mask_stride == 1 while the vector column has mask_stride == vector_size. A row where both id and vec are NULL (size = -1) would catch stride desynchronization between the two columns.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/test_numpy_parser.py` around lines 316 - 357, Extend
test_mixed_columns_with_vectors to include a row where both id and vec are NULL,
encoding each column with size = -1, and update the expected outputs to verify
both columns preserve the NULL values while retaining correct shapes and
alignment. Ensure the assertions exercise mask-stride advancement between the
scalar id column and vector vec column.

96-108: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: extract the parser/ParseDesc boilerplate into a helper.

This 10-line block is repeated near-verbatim in ~10 tests, varying only in colnames/coltypes/deserializers.

♻️ Suggested helper
    def _parse(self, serialized, colnames, coltypes, deserializers=None):
        desc = ParseDesc(
            colnames=colnames,
            coltypes=coltypes,
            column_encryption_policy=None,
            coldescs=None,
            deserializers=deserializers or obj_array([None] * len(colnames)),
            protocol_version=5,
        )
        return NumpyParser().parse_rows(BytesIOReader(serialized), desc)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/test_numpy_parser.py` around lines 96 - 108, Extract the repeated
NumpyParser and ParseDesc setup into a test helper, such as _parse, in the test
class or module. Have it accept serialized data, column names, column types, and
optional deserializers, supply the existing default deserializers when omitted,
and return parse_rows’ result; update the affected tests to use this helper.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cassandra/numpy_parser.pyx`:
- Around line 73-83: Restore ShortType handling in the vector decoding flow
without adding it to the fixed-width fast-path table. Add a dedicated
per-element decoder for VectorType<smallint, N> that follows its length-prefixed
wire representation and produces the required 2D int16 masked array, while
preserving the existing fallback behavior for other variable-width subtypes and
avoiding the unpack_row stride assumption.

---

Nitpick comments:
In `@tests/unit/test_numpy_parser.py`:
- Around line 316-357: Extend test_mixed_columns_with_vectors to include a row
where both id and vec are NULL, encoding each column with size = -1, and update
the expected outputs to verify both columns preserve the NULL values while
retaining correct shapes and alignment. Ensure the assertions exercise
mask-stride advancement between the scalar id column and vector vec column.
- Around line 96-108: Extract the repeated NumpyParser and ParseDesc setup into
a test helper, such as _parse, in the test class or module. Have it accept
serialized data, column names, column types, and optional deserializers, supply
the existing default deserializers when omitted, and return parse_rows’ result;
update the affected tests to use this helper.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f479ba91-f46a-4bf6-b58d-446177f1a701

📥 Commits

Reviewing files that changed from the base of the PR and between e73ef19 and 83a5989.

📒 Files selected for processing (2)
  • cassandra/numpy_parser.pyx
  • tests/unit/test_numpy_parser.py

Comment thread cassandra/numpy_parser.pyx Outdated
The memcpy fast-path guard in unpack_row() only rejected oversized
payloads (buf.size > arr.stride), which prevents the buffer-overflow
case but let undersized payloads slip through, leaving the remainder
of the row filled with uninitialized/stale heap bytes instead of
raising. Tighten the check to buf.size != arr.stride so both
directions are rejected, and update the error message accordingly,
including the column name for easier debugging.

Add tests covering both the oversized and undersized payload cases,
since neither had test coverage before.

Also revert the arrays = [] / .append() -> [None]*n / index-assign
change in make_arrays(), which was functionally identical diff noise
unrelated to the VectorType/2D-array feature.

Restore ShortType (smallint) to _cqltype_to_numpy: an earlier fix on
this PR removed it from that table entirely to stop the VectorType
fast-path from assuming a fixed 2-byte stride for vint-prefixed vector
elements, but the table is shared with the plain scalar top-level
column dispatch in make_array(), where smallint genuinely *is*
fixed-width (no length prefix). Removing the entry regressed scalar
smallint columns onto the slow object-array fallback. Fix: keep
ShortType in the shared table, and instead guard the VectorType.subtype
call site with `subtype.serial_size() is not None` (the same check
VectorType.serialize()/deserialize() use) before consulting the table,
so only the vector-specific dispatch excludes non-fixed-width subtypes.
This also protects any subtype added to the table in the future.
Add tests for the restored scalar smallint fast path (both make_array()
directly and an end-to-end parse_rows() round-trip).

Also fix tests/unit/test_numpy_parser.py's HAVE_NUMPY gating: a bare
`except ImportError` around the numpy_parser/bytesio/etc. imports
could not distinguish "numpy genuinely not installed" from "the
numpy_parser Cython extension is broken despite numpy being present",
silently skipping the whole test module in both cases. Gate instead on
cassandra.cython_deps.HAVE_CYTHON and HAVE_NUMPY (plus the
VERIFY_CYTHON override), matching the `numpytest` convention in
tests/unit/cython/utils.py, and import unconditionally once those are
satisfied so a broken extension surfaces as a real test failure.
@mykaul
mykaul force-pushed the vector-numpy-parser-2d branch from 83a5989 to 8f16aba Compare July 30, 2026 16:21
Copilot AI review requested due to automatic review settings July 30, 2026 16:21
@mykaul

mykaul commented Jul 30, 2026

Copy link
Copy Markdown
Author

Follow-up fix: a Copilot review comment on this PR was suppressed due to low confidence (never became a formal resolvable thread), but it correctly caught a regression in the previous commit here:

Removing ShortType from this shared map also changes ordinary smallint result columns: make_array() below now takes its KeyError fallback and returns a 1-D object array instead of the existing masked int16 array. Keep ShortType in the scalar mapping and exclude it only when selecting the VectorType fast path.

Confirmed directly in code: _cqltype_to_numpy in cassandra/numpy_parser.pyx is consulted from two call sites in make_array() -- plain scalar top-level columns and VectorType.subtype dispatch -- and removing ShortType from the dict to fix the vector fast-path (vint-prefixed elements, not fixed 2-byte stride) also silently broke the pre-existing scalar smallint fast path (regressed to the slow object-array fallback).

Fix pushed:

  • Restored cqltypes.ShortType: np.dtype('>i2') to _cqltype_to_numpy, so scalar smallint columns get the fast masked int16 array again.
  • Narrowed the exclusion to only the VectorType.subtype call site, guarded by subtype.serial_size() is not None (the same check VectorType.serialize()/deserialize() use to decide fixed-width vs. vint-prefixed wire encoding). This is more general than special-casing ShortType/ByteType and also protects any subtype added to the table in the future.
  • vector<smallint, N> is unaffected and still correctly falls back to the object array (covered by the existing test_vector_smallint_falls_back_and_round_trips_via_real_serializer regression test).
  • Added TestNumpyParserScalarType tests covering the restored scalar smallint fast path directly (make_array) and end-to-end (parse_rows).

Note on CodeRabbit's separate suggestion to "restore the required int16 vector support" via a dedicated per-element decoder for vector<smallint>: that's a distinct feature request (implementing a real fixed-2D fast path for a vint-prefixed subtype), not the regression above, and it's intentionally out of scope here -- the object-array fallback for vector<smallint> is the existing, deliberate, tested behavior on this PR.

Also fixed a separate, previously-open review concern in tests/unit/test_numpy_parser.py: the module used a single broad except ImportError around the numpy/cassandra.numpy_parser/etc. imports, which could not distinguish "numpy genuinely not installed" (should skip) from "the numpy_parser Cython extension broken while numpy is present" (should fail loudly, not hide behind a skip). Now gated on cassandra.cython_deps.HAVE_CYTHON/HAVE_NUMPY (plus the VERIFY_CYTHON override), matching the numpytest convention in tests/unit/cython/utils.py.

Verified: full tests/unit/ suite green (786 passed, 38 pre-existing/unrelated skips) after rebuilding the Cython extension.

Copilot AI 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.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

cassandra/numpy_parser.pyx:184

  • The PR description promises a native int16 2D array for supported numeric vector subtypes, but this guard excludes ShortType because its serial_size() is None, and the new test explicitly expects an object-array fallback. Either remove int16 support from the PR details or implement the required framed-element conversion so the advertised behavior matches the code.
        if subtype.serial_size() is not None:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants