Skip to content

Commit b5dd612

Browse files
Yuhtafacebook-github-bot
authored andcommitted
fix(nimble): Keep nulls in dictionary reads without AVX2 (facebookincubator#18621)
Summary: Reading a Nimble string column that keeps its dictionary encoding dropped every null on any platform where `process::hasAvx2()` is false, which is all of aarch64. A 200-row column holding `always_the_same` with a null every 7th row came back as a `DictionaryVector` carrying no null flags, so row 0 read as `always_the_same` instead of null. The dense dictionary-index path wrote nulls only into the reader's read-range bitmap. It then relied on `returnReaderNulls_` to hand that bitmap back from `resultNulls()`. `setReturnNullsMode` clears that flag whenever `useBulkPath()` is false, and `process::hasAvx2()` makes that permanent off AVX2. `resultNulls()` then returns the output-indexed buffer instead, which nothing on this path filled. It now copies the read-range nulls into that buffer, the way the sparse row-set path already did. Reviewed By: vandreykiv Differential Revision: D116833280
1 parent 29357c4 commit b5dd612

3 files changed

Lines changed: 122 additions & 70 deletions

File tree

velox/dwio/nimble/encodings/common/Encoding.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,25 @@ void readDenseMaterializedIndices(
527527
/*sourceBegin=*/valueOutputOffset,
528528
rawOuterNonNullRows,
529529
rawOutputValues);
530+
531+
// Nulls were materialized into the reader's read-range bitmap only.
532+
// `resultNulls()' hands that bitmap back while `returnReaderNulls_' holds,
533+
// but `setReturnNullsMode' clears the flag whenever `useBulkPath()' is false
534+
// -- notably on a platform without AVX2, where every read takes that branch.
535+
// `resultNulls()' then returns the output-indexed `resultNulls_', which
536+
// nothing on this path writes, so the output would silently lose its nulls.
537+
// Copy the read-range nulls across, mirroring what
538+
// `readSparseMaterializedIndices' does for the sparse row set.
539+
if (!visitor.reader().returnReaderNulls()) {
540+
auto* rawResultNulls = visitor.reader().rawResultNulls();
541+
NIMBLE_CHECK_NOT_NULL(
542+
rawResultNulls,
543+
"prepareResultNulls must allocate result nulls before the dense index "
544+
"path writes them");
545+
velox::bits::copyBits(
546+
rawNulls, readOffset, rawResultNulls, valueOutputOffset, numReadRows);
547+
visitor.reader().setHasNulls();
548+
}
530549
visitor.addNumValues(numReadRows);
531550
visitor.setRowIndex(visitor.numRows());
532551
}

velox/dwio/nimble/encodings/tests/ReadWithVisitorTest.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4256,9 +4256,16 @@ TEST_P(ReadWithVisitorNonLegacyTest, readDenseMaterializedIndicesWithNulls) {
42564256

42574257
ASSERT_EQ(reader->numValues(), 0);
42584258

4259-
// Call the helper with nulls.
4259+
// Call the helper with nulls. prepareResultNulls must mirror what
4260+
// ChunkedDecoder wires up in production: the dense index path materializes
4261+
// nulls into the read-range bitmap only, so it needs an allocated,
4262+
// output-indexed result-nulls buffer to copy them into whenever
4263+
// returnReaderNulls_ is false -- as it is here, the scan spec carries a
4264+
// filter.
42604265
ReadWithVisitorParams params{.numScanned = 0};
4261-
params.prepareResultNulls = [] {};
4266+
params.prepareResultNulls = [&] {
4267+
reader->prepareNulls(rows, /*hasNulls=*/true, /*extraRows=*/8);
4268+
};
42624269
detail::readDenseMaterializedIndices(
42634270
*encoding,
42644271
visitor,

0 commit comments

Comments
 (0)