Skip to content

fix: read uint64 columns without boxing through BigInteger - #803

Open
jackylee-ch wants to merge 1 commit into
lance-format:mainfrom
jackylee-ch:fix/uint64-accessor-no-biginteger
Open

fix: read uint64 columns without boxing through BigInteger#803
jackylee-ch wants to merge 1 commit into
lance-format:mainfrom
jackylee-ch:fix/uint64-accessor-no-biginteger

Conversation

@jackylee-ch

Copy link
Copy Markdown
Contributor

Summary

UInt8Accessor.getLong boxed through UInt8Vector.getObjectNoOverflow, which returns the unsigned
value as a BigInteger. longValueExact() then throws ArithmeticException: BigInteger out of long range for every uint64 at or above 2^63, and NPEs on a null slot, instead of wrapping. The class
javadoc, LanceArrowUtils.scala:102, and the _rowaddr read at
LanceFragmentColumnarBatchScanner.java:197 all treat uint64 as wrapping into LongType.

Reachable through any uint64 column, including _rowid and _rowaddr, which
LanceArrowUtils.scala:538 maps to ArrowType.Int(64, false).

Test plan

  • New LanceArrowColumnVectorTest case: fails on the old accessor with ArithmeticException
  • *Vector*/*Blob*/*Update* suites on Spark 4.1/Scala 2.13 and 3.5/Scala 2.12; make lint

🤖 Generated with Claude Code

getObjectNoOverflow returns the unsigned value as a BigInteger, so
longValueExact throws ArithmeticException for every uint64 at or above
2^63 and NPEs on a null slot. Read the raw two's-complement long instead,
which is the wrap behaviour the class javadoc and LanceArrowUtils already
document and what the _rowaddr path in LanceFragmentColumnarBatchScanner
already does, and it drops a BigInteger allocation per row.
@github-actions github-actions Bot added the bug Something isn't working label Sep 3, 2026

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

Gate recommendation: approve.

The primitive read fixes the demonstrated high-bit and null failures while preserving the connector’s existing UInt64-to-LongType contract. It also removes per-row BigInteger allocation, and the focused regression covers scalar and bulk access across the supported Arrow boundaries.

@lance-gatekeeper lance-gatekeeper Bot added the K-approved Latest Gatekeeper recommendation permits acceptance. label Sep 3, 2026

final long getLong(int rowId) {
return accessor.getObjectNoOverflow(rowId).longValueExact();
// Read the raw two's-complement long rather than boxing through getObjectNoOverflow(), which

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: this is a bit verbose we can drop or trim to something like:

// Arrow get() throws on a null slot. Spark getLongs calls getLong without isNullAt.

wdyt?

try (BufferAllocator allocator = new RootAllocator();
UInt8Vector vector = (UInt8Vector) field.createVector(allocator)) {
vector.allocateNew(3);
// 2^63 and 2^64-1 are ordinary uint64 values. LongType carries them as their

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: im fine with these comments but can drop wdyt?

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

Labels

bug Something isn't working K-approved Latest Gatekeeper recommendation permits acceptance.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants