Skip to content

fix(encoding): emit NIL control words when rep/def levels are non-empty but zero-width - #9018

Open
lichuang wants to merge 1 commit into
lance-format:mainfrom
lichuang:fix-repdef
Open

fix(encoding): emit NIL control words when rep/def levels are non-empty but zero-width#9018
lichuang wants to merge 1 commit into
lance-format:mainfrom
lichuang:fix-repdef

Conversation

@lichuang

@lichuang lichuang commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Summary

The structural full-zip encoder wrote one all-zero control-word byte per item for pages whose rep/def level buffers are non-empty but have a computed bit width of zero, while the decoder reads such a zero-bit layout as NIL (zero bytes per control word). The mismatch interleaves phantom bytes into the zipped value stream and corrupts every value that follows.

This adds a zero-width early return in build_control_word_iterator: when total_width == 0 (i.e. max_rep == 0 && max_def == 0), emit ControlWordIterator::Nilary regardless of which level buffers are present, so the writer agrees with the decoder's NIL interpretation.

Root cause

encode_full_zip (rust/lance-encoding/src/encodings/logical/primitive.rs) computes max_rep/max_def from the per-page sliced level buffers, not the schema. When a page's levels are all zero (e.g. a page holding only valid rows of a nullable column after page splitting), total_width == 0 and the layout metadata records bits_rep = 0, bits_def = 0. The decoder reconstructs ControlWordParser::new(0, 0)NIL (bytes_per_word() == 0), but the writer selected its branch purely on buffer presence, producing a Unary8/Binary8 writer that emits one 0x00 byte per item (bytes_per_word() == 1). In the full-zip layout control words and values are interleaved per item, so the decoder's 0-byte step mis-aligns the value stream by one byte per item.

…ty but zero-width, matching the decoder and preventing full-zip stream corruption
@github-actions github-actions Bot added A-encoding Encoding, IO, file reader/writer bug Something isn't working labels Sep 5, 2026

@lance-gatekeeper lance-gatekeeper Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Gate recommendation: approve.

The zero-width guard restores the full-zip writer/parser invariant: (bits_rep, bits_def) = (0, 0) now emits the NIL representation existing readers already consume, avoiding phantom bytes while preserving the stable 2.1 layout. Centralizing this in the control-word iterator is preferable to widening redundant all-valid definition levels or adding decoder ambiguity.

@lance-gatekeeper lance-gatekeeper Bot added the K-approved Latest Gatekeeper recommendation permits acceptance. label Sep 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-encoding Encoding, IO, file reader/writer 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.

1 participant