Repository navigation
Arrow, Spark: Skip unprojected shredded variants in vectorized Parquet reads - #18434
randreucetti wants to merge 1 commit into
Conversation
…t reads VectorizedReaderBuilder visited every variant group in the file schema, including variants that are not projected, and VectorizedVariantVisitor throws on a shredded typed_value. A vectorized read that did not select a shredded variant column therefore failed. Skip the variant visit when the column is not in the expected schema, as is already done for primitives. Closes apache#18430 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
CC: @RussellSpitzer @huaxingao to help since I can't get to it at the moment. Looks right at a high level but needs a closer review. |
There was a problem hiding this comment.
Thank you @randreucetti I agree with the fix. It makes sense in variantVisitor() since the variant's children are visited before variant(...) is called, so I believe this is the only way to indicate to the caller that a variant that's not being projected shouldn't be visited.
Just had some comments on tests.
Separately, I wonder why the reader builders get the full file schema rather than the projection, which is applied later when the pages are read. Not blocking because looks like that's always been the case and we still fundamentally get the benefits of projection pushdown but feels like we could get it even earlier in this logic too.
| } | ||
|
|
||
| @Test | ||
| public void testShreddedVariantInProjectionThrows() { |
There was a problem hiding this comment.
I think the practice for new tests is to just drop the test prefix in the method name; I think something like projectingShreddedVariantUnsupported is also a bit more clear (mostly the "Unsupported" part, the "throws" in the current naming is a bit vague imo)
| } | ||
|
|
||
| @Test | ||
| public void testShreddedVariantSkippedWhenNotInProjection() { |
There was a problem hiding this comment.
I think it's worth adding a parameterized test for when the variant is nested inside struct, as a list element, map key/value and isn't projected.
Closes #18430
Rationale for this change
A vectorized Spark read of a Parquet file that contains a shredded variant column fails when the query doesn't project the variant:
SparkBatchonly routes projected shredded variants to the row reader, so a query likeSELECT sum(id) FROM ton a shredded table uses batch reads.VectorizedSparkParquetReaders.buildReaderthen visits the file schema, andTypeWithSchemaVisitor.visitVariantwalks the unprojected variant group withVectorizedVariantVisitor, which throws ontyped_value. The variant isn't read at all, so the throw is unnecessary.What changes are included in this PR?
VectorizedReaderBuilder.variantVisitor()returnsnullwhen the current variant group isn't in the expected schema.TypeWithSchemaVisitorthen callsvariant(...)with anullresult, so the column gets no reader. This matches howVectorizedReaderBuilder.primitive(...)already treats unprojected primitives.The fix is in the shared
arrowmodule, so it covers every Spark version that usesVectorizedReaderBuilder. Projected shredded variants are unchanged: they still rely onSparkBatchrouting them to the row reader.I kept the change in the vectorized builder and left
TypeWithSchemaVisitor.visitVariantalone. Skipping the sub-visit there when the Iceberg type isnullwould also work, but that visitor is shared with the row readers andBaseParquetWriter, so changing it has a wider blast radius. Happy to move it if reviewers prefer.Are these changes tested?
Yes:
TestVectorizedReaderBuilder.testShreddedVariantSkippedWhenNotInProjectionis the shredded counterpart of the existingtestVariantSkippedWhenNotInProjection. That test only uses an unshredded variant, which is why it didn't catch this. Without the fix the new test fails with theUnsupportedOperationExceptionabove.TestVectorizedReaderBuilder.testShreddedVariantInProjectionThrowschecks that the skip applies only to unprojected variants: a projected shredded variant still reachesVectorizedVariantVisitor.TestSparkVariantRead.testReadShreddedWithoutProjectingVariant(Spark 4.2) is an end-to-end regression test. It writes a shredded table, asserts the file has atyped_valuesubtree, and reads onlyidwith vectorization on and off. Without the fix, the vectorized case fails.The same Spark test applies unchanged to 4.0 and 4.1. I'm happy to add it here or in a follow-up backport PR, whichever reviewers prefer.
Are there any user-facing changes?
No API changes. Vectorized reads that don't project a shredded variant now succeed instead of failing.
AI disclosure
I used an AI coding assistant (Claude Code) to help trace the bug, draft the fix and scaffold the tests. I reproduced the bug, reviewed the change, and ran the tests and checks locally. The area I'd most like reviewers to check is the field lookup in
variantVisitor(). It resolves the variant's field id fromparquetSchema.getType(currentPath()), which relies on the builder'sparquetSchemabeing the same id-assigned file schema thatTypeWithSchemaVisitoris walking. That holds forVectorizedSparkParquetReaders.buildReader.cc @nssalian
🤖 Generated with Claude Code