-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Fix variant type filtering in ParquetMetricsRowGroupFilter #14081
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
83ae077
1d41313
99dc168
ab202af
c7c715e
3f06a3d
3ba38bb
a618461
a48ec72
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -62,6 +62,7 @@ | |
| import org.apache.iceberg.avro.AvroSchemaUtil; | ||
| import org.apache.iceberg.data.orc.GenericOrcReader; | ||
| import org.apache.iceberg.data.orc.GenericOrcWriter; | ||
| import org.apache.iceberg.data.parquet.GenericParquetWriter; | ||
| import org.apache.iceberg.exceptions.ValidationException; | ||
| import org.apache.iceberg.expressions.Expression; | ||
| import org.apache.iceberg.io.CloseableIterable; | ||
|
|
@@ -78,6 +79,10 @@ | |
| import org.apache.iceberg.types.Types.FloatType; | ||
| import org.apache.iceberg.types.Types.IntegerType; | ||
| import org.apache.iceberg.types.Types.StringType; | ||
| import org.apache.iceberg.variants.ShreddedObject; | ||
| import org.apache.iceberg.variants.Variant; | ||
| import org.apache.iceberg.variants.VariantMetadata; | ||
| import org.apache.iceberg.variants.Variants; | ||
| import org.apache.orc.OrcFile; | ||
| import org.apache.orc.Reader; | ||
| import org.apache.parquet.hadoop.ParquetFileReader; | ||
|
|
@@ -988,6 +993,97 @@ public void testTransformFilter() { | |
| .isTrue(); | ||
| } | ||
|
|
||
| @TestTemplate | ||
| public void testVariantFilterNotNull() throws IOException { | ||
| assumeThat(format).isEqualTo(FileFormat.PARQUET); | ||
|
|
||
| Schema variantSchema = | ||
| new Schema( | ||
| required(1, "id", IntegerType.get()), | ||
| optional(2, "variant_field", Types.VariantType.get())); | ||
|
|
||
| File parquetFile = new File(tempDir, "test-variant" + System.nanoTime()); | ||
|
|
||
| OutputFile outFile = Files.localOutput(parquetFile); | ||
| try (FileAppender<GenericRecord> appender = | ||
| Parquet.write(outFile) | ||
| .schema(variantSchema) | ||
| .createWriterFunc(GenericParquetWriter::create) | ||
| .build()) { | ||
|
|
||
| for (int i = 0; i < 10; i++) { | ||
| GenericRecord record = GenericRecord.create(variantSchema); | ||
| record.setField("id", i); | ||
|
|
||
| if (i % 2 == 0) { | ||
| VariantMetadata metadata = Variants.metadata("field"); | ||
| ShreddedObject obj = Variants.object(metadata); | ||
| obj.put("field", Variants.of("value" + i)); | ||
| Variant variant = Variant.of(metadata, obj); | ||
| record.setField("variant_field", variant); | ||
| } | ||
|
|
||
| appender.add(record); | ||
| } | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need to write records in these tests? At this level of abstraction, I think we should just create the row group filter with the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1. Seems we don't need to write to the files.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the feedback Amogh! Good point about avoiding file writing at this level. I initially wrote the tests this way because this test class shares a schema for writing out data files in both ORC and Parquet. We're now in a situation where ORC doesn't have full support for variant types while Parquet does, so adding variant fields to the shared schema would break the existing ORC tests. That said, it probably makes sense to use separate schemas for Parquet and ORC given the differnt levels of support. and write the tests to reflect that.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Talked with @geruh offline and I also poked around refactoring this class, and while I think we should (this mixture of orc/parquet both trying to test "row group filtering" is leading to weird tests), it's a big change especially for something going into a patch release. Also while technically the implementation of filtering with variant doesn't depend on the actual contents of the file, after some more thought I concluded that it's better to write a more realistic test which does contain the records like @geruh was doing before. |
||
|
|
||
| InputFile inFile = Files.localInput(parquetFile); | ||
| try (ParquetFileReader reader = ParquetFileReader.open(parquetInputFile(inFile))) { | ||
| assertThat(reader.getRowGroups()).as("Should create only one row group").hasSize(1); | ||
| BlockMetaData blockMetaData = reader.getRowGroups().get(0); | ||
| MessageType fileSchema = reader.getFileMetaData().getSchema(); | ||
|
geruh marked this conversation as resolved.
|
||
|
|
||
| ParquetMetricsRowGroupFilter rowGroupFilter = | ||
| new ParquetMetricsRowGroupFilter(variantSchema, notNull("variant_field"), true); | ||
| boolean shouldRead = rowGroupFilter.shouldRead(fileSchema, blockMetaData); | ||
| assertThat(shouldRead) | ||
|
geruh marked this conversation as resolved.
Outdated
|
||
| .as("Should read: variant notNull filters must be evaluated post scan") | ||
| .isTrue(); | ||
| } | ||
| parquetFile.deleteOnExit(); | ||
| } | ||
|
|
||
| @TestTemplate | ||
| public void testAllNullsVariantNotNull() throws IOException { | ||
| assumeThat(format).isEqualTo(FileFormat.PARQUET); | ||
|
|
||
| Schema variantSchema = | ||
| new Schema( | ||
| required(1, "id", IntegerType.get()), | ||
| optional(2, "variant_field", Types.VariantType.get())); | ||
|
|
||
| File parquetFile = new File(tempDir, "test-variant-nulls" + System.nanoTime()); | ||
|
|
||
| OutputFile outFile = Files.localOutput(parquetFile); | ||
| try (FileAppender<GenericRecord> appender = | ||
| Parquet.write(outFile) | ||
| .schema(variantSchema) | ||
| .createWriterFunc(GenericParquetWriter::create) | ||
| .build()) { | ||
|
|
||
| for (int i = 0; i < 10; i++) { | ||
| GenericRecord record = GenericRecord.create(variantSchema); | ||
| record.setField("id", i); | ||
| record.setField("variant_field", null); | ||
| appender.add(record); | ||
| } | ||
| } | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above, don't think we need to actually write parquet files in these tests
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. addressed above |
||
| InputFile inFile = Files.localInput(parquetFile); | ||
| try (ParquetFileReader reader = ParquetFileReader.open(parquetInputFile(inFile))) { | ||
| BlockMetaData blockMetaData = reader.getRowGroups().get(0); | ||
| MessageType fileSchema = reader.getFileMetaData().getSchema(); | ||
|
|
||
| ParquetMetricsRowGroupFilter rowGroupFilter = | ||
| new ParquetMetricsRowGroupFilter(variantSchema, notNull("variant_field"), true); | ||
| boolean shouldRead = rowGroupFilter.shouldRead(fileSchema, blockMetaData); | ||
| assertThat(shouldRead) | ||
|
geruh marked this conversation as resolved.
Outdated
|
||
| .as("Should read: variant notNull filters must be evaluated post scan even for all nulls") | ||
| .isTrue(); | ||
| } | ||
| parquetFile.deleteOnExit(); | ||
| } | ||
|
|
||
| private boolean shouldRead(Expression expression) { | ||
| return shouldRead(expression, true); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -157,8 +157,10 @@ public <T> Boolean notNull(BoundReference<T> ref) { | |
|
|
||
| // When filtering nested types notNull() is implicit filter passed even though complex | ||
| // filters aren't pushed down in Parquet. Leave all nested column type filters to be | ||
| // evaluated post scan. | ||
| if (schema.findType(id) instanceof Type.NestedType) { | ||
| // evaluated post scan. Variant types also need to be evaluated post scan to access | ||
| // shredded statistics. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd remove the "variant types also need to be evaluated post scan to access shredded statistics". I'd just update to reflect the current state of things which is in that first sentence, "When filtering nested types or variant...." and the second sentence to be "Leave these type filters...". For shredded stats pruning, the core library already contains
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 on this. |
||
| Type type = schema.findType(id); | ||
| if (type instanceof Type.NestedType || type.isVariantType()) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 |
||
| return ROWS_MIGHT_MATCH; | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.