Skip to content

fix(parquet): Skip fixed-length byte arrays without a length prefix - #18195

Merged
meta-codesync[bot] merged 3 commits into
facebookincubator:mainfrom
iemejia:pr/parquet-fixed-string-skip
Aug 18, 2026
Merged

meta-codesync[bot] merged 3 commits into
facebookincubator:mainfrom
iemejia:pr/parquet-fixed-string-skip

Conversation

@iemejia

@iemejia iemejia commented Jul 20, 2026 •

Copy link
Copy Markdown
Contributor

Reading a Parquet FIXED_LEN_BYTE_ARRAY column as VARBINARY or VARCHAR returned wrong values or crashed whenever rows had to be skipped, for example under a filter on a sibling column.

StringDecoder::skip() advanced the buffer by treating the first four bytes of each value as a length prefix (the variable-length BYTE_ARRAY layout). Fixed-length values carry no prefix, so a value whose leading bytes encode a large number ran the read pointer off the page. readWithVisitor() already handles fixed-length values via readFixedString(); only skip() was missing the corresponding branch.

skip() now advances by numValues * fixedLength_ for fixed-length columns, matching the stride readFixedString() uses. This is both a correctness fix and an O(1) skip (previously O(N)).

Test: ParquetReaderTest.fixedLenByteArraySkipWithFilter reads a 4-byte fixed binary column whose value equals the row index, filters to the even rows, and checks alignment. It segfaults on the previous code and passes with the fix.

Split out of #17991 per review feedback.

Part of #17994.

@netlify

netlify Bot commented Jul 20, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for meta-velox canceled.

Name Link
🔨 Latest commit b5504e7
🔍 Latest deploy log https://app.netlify.com/projects/meta-velox/deploys/6a7c3b2b8887900008364dee

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jul 20, 2026
@github-actions

github-actions Bot commented Jul 20, 2026 •

Copy link
Copy Markdown

Selective Build Plan

Linux release with adapters is running a full build (PR has a standing approval). See the CI workflows README for what this means.


Selective build plan

@github-actions

github-actions Bot commented Jul 20, 2026 •

Copy link
Copy Markdown

CI Failure Analysis

Auto-generated by the CI Failure Analysis workflow. This comment is updated in place each time CI fails on a new commit, so it always reflects the latest run — re-pushing or re-running CI will refresh the analysis below. Last updated 2026-08-12 09:07:42 UTC from workflow run 31579425982.

🟡 Window Fuzzer with Presto as source of truth — FUZZER Failure View logs

Fuzzer failure: Instance 4 (seed 851970327) aborted — verification rate dropped below 50% threshold.

3 of 4 fuzzer instances passed. Instance 4 ran 7 iterations but only 3 were verified (42.86% < 50% threshold), triggering the VELOX_CHECK_GE assertion in WindowFuzzer::go().

File: velox/exec/fuzzer/WindowFuzzer.cpp:593
Expression: (stats_.numVerified + stats_.numVerificationSkipped) / (double)iteration >= 0.5
Actual: 0.4286 vs required 0.5

Root cause: Presto reference DB rejected a query with:
  "Window frame offset value must not be negative or null"
(PrestoQueryRunner.cpp:121)

This means the fuzzer generated a window query with a negative/null frame offset
that Presto rejects, reducing the verified iteration count below the threshold.

Correlation with PR changes:
This failure is not related to the PR changes. PR #18195 modifies StringDecoder.h to fix FIXED_LEN_BYTE_ARRAY skipping in the Parquet reader and adds a corresponding regression test (ParquetReaderTest.fixedLenByteArraySkipWithFilter). The Window Fuzzer failure is in a completely separate subsystem (query execution fuzzer infrastructure) and involves Presto reference query validation — no code paths overlap.

Known issues:

Reproduce locally:

./velox_window_fuzzer_test \
    --seed 851970327 \
    --duration_sec 300 \
    --batch_size=50 \
    --minloglevel=0 \
    --enable_window_reference_verification \
    --presto_url=http://127.0.0.1:8080 \
    --req_timeout_ms=10000

(Requires a local Presto server running on port 8080.)

Recommended fix:
No action needed from this PR — this is a pre-existing flaky failure. Consider retrying the CI workflow. The underlying issue is tracked in #16917.

@iemejia

iemejia commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Separated into its independent PR as requested by @PingLiuPing

@PingLiuPing PingLiuPing left a comment

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.

Thanks.
I verified the test failed without the fix.

->valueAt(0));
}

// Skipping over FIXED_LEN_BYTE_ARRAY values must advance the decoder by a

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.

Let's move this explanation to dwio/parquet/tests/examples/README.md

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done. Moved the fixture explanation to velox/dwio/parquet/tests/examples/README.md as a flba_skip.parquet entry (metadata + purpose), matching the format used for the other fixtures. The inline comment in the test is now a short pointer to that entry.

@iemejia
iemejia force-pushed the pr/parquet-fixed-string-skip branch from b911a42 to 59c86ce Compare July 22, 2026 10:26
@iemejia

iemejia commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

Linux release with adapters failure is a broken-main issue, unrelated to this PR.

cudf now requires CMake 4.0 (cudf.cmake:18, from #17637), but the adapters container still ships CMake 3.31.1:

CMake 4.0 or higher is required. You are running version 3.31.1

The setup scripts that install CMake 4.3.2 only re-run when a PR touches scripts/setup-*, so unrelated PRs build with the stale CMake and fail. This hits every recent main push — the container image just needs rebuilding. No action needed here.

iemejia added a commit to iemejia/velox that referenced this pull request Jul 22, 2026
Full scans of plain-encoded BOOLEAN columns without nulls decode about 14x faster (51 us -> 3.6 us for 100K values on an AMD EPYC core at -O3). Reads with nulls, sparse rows, or filters are unchanged.

BooleanDecoder previously called readBoolean() once per value, paying a branch and a remaining-bits update per bit. readWithVisitor() now takes a fast path for dense null-free reads that loads one byte and hands its 8 bits to the visitor through an unrolled loop, dropping the per-bit branch. On an early atEnd exit it records how many bits of the current byte are still unread, so a dense read whose row count is not a multiple of 8 resumes at the correct bit on the next call.

A micro-benchmark (BooleanDecoderBenchmark) isolates the decoder from the reader pipeline to measure the gain, following the existing NestedStructureDecoderBenchmark convention.

The O(1) FIXED_LEN_BYTE_ARRAY skip originally bundled here was split out into facebookincubator#18195. Part of facebookincubator#17994.
iemejia added a commit to iemejia/velox that referenced this pull request Jul 24, 2026
Full scans of plain-encoded BOOLEAN columns without nulls decode about 14x faster (51 us -> 3.6 us for 100K values on an AMD EPYC core at -O3). Reads with nulls, sparse rows, or filters are unchanged.

BooleanDecoder previously called readBoolean() once per value, paying a branch and a remaining-bits update per bit. readWithVisitor() now takes a fast path for dense null-free reads that loads one byte and hands its 8 bits to the visitor through an unrolled loop, dropping the per-bit branch. On an early atEnd exit it records how many bits of the current byte are still unread, so a dense read whose row count is not a multiple of 8 resumes at the correct bit on the next call.

A micro-benchmark (BooleanDecoderBenchmark) isolates the decoder from the reader pipeline to measure the gain, following the existing NestedStructureDecoderBenchmark convention. The decoded output is guarded with a checksum excluded from timing so the optimizer cannot discard the decode work.

The O(1) FIXED_LEN_BYTE_ARRAY skip originally bundled here was split out into facebookincubator#18195. Part of facebookincubator#17994.
When a Parquet FIXED_LEN_BYTE_ARRAY column is read as VARBINARY or VARCHAR and some rows are skipped, for example under a filter on a sibling column, the reader returned wrong values or crashed. `StringDecoder::skip()` advanced the buffer by treating the first four bytes of each value as a length prefix, but fixed-length values carry no prefix, so a value whose leading bytes encode a large number ran the read pointer off the page.

`skip()` now advances by `numValues * fixedLength_` for fixed-length columns, the same stride `readFixedString()` uses when it reads them.
Per review feedback, document the flba_skip.parquet fixture in
examples/README.md alongside the other fixtures, and replace the inline
explanation in the test with a short pointer to it.
@iemejia
iemejia force-pushed the pr/parquet-fixed-string-skip branch from 59c86ce to 532779f Compare August 12, 2026 08:40
@iemejia

iemejia commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

This one also seems to be ready to go @PingLiuPing PTAL again

@PingLiuPing PingLiuPing added the ready-to-merge PR that have been reviewed and are ready for merging. PRs with this tag notify the Velox Meta oncall label Aug 13, 2026
@meta-codesync

meta-codesync Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

@kgpai has imported this pull request. If you are a Meta employee, you can view this in D116320333.

@meta-codesync
meta-codesync Bot merged commit c4299b7 into facebookincubator:main Aug 18, 2026
52 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. ready-to-merge PR that have been reviewed and are ready for merging. PRs with this tag notify the Velox Meta oncall

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants