-
Notifications
You must be signed in to change notification settings - Fork 1.6k
GH-3601: Avoid repeated created_by parsing in footer metadata conversion #3607
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
Open
yadavay-amzn
wants to merge
3
commits into
apache:master
Choose a base branch
from
yadavay-amzn:fix/3601-shouldIgnoreStatistics-cache
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+208
−16
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
6bd8aa9
GH-3601: Compute shouldIgnoreStatistics once per file in ParquetMetad…
yadavay-amzn 9c5df29
GH-3601: Address review - factor CorruptStatistics into file-level + …
yadavay-amzn 4bc68a9
GH-3601: Rename to mayHaveCorruptStatistics and minimize new public API
yadavay-amzn File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2159,4 +2159,110 @@ public void testColumnIndexNanCountsRoundTrip() { | |
| assertNotNull(roundTrip); | ||
| assertEquals(List.of(1L, 0L, 0L), roundTrip.getNanCounts()); | ||
| } | ||
|
|
||
| @Test | ||
| public void testCorruptStatsPerColumnGate() { | ||
| // A created_by string from a version known to have the PARQUET-251 bug | ||
| String corruptCreatedBy = "parquet-mr version 1.6.0 (build abcd)"; | ||
|
|
||
| // Set up legacy V1 statistics with min/max (not min_value/max_value) | ||
| org.apache.parquet.format.Statistics formatStats = new org.apache.parquet.format.Statistics(); | ||
| byte[] minBytes = new byte[] {0, 1, 2, 3}; | ||
| byte[] maxBytes = new byte[] {4, 5, 6, 7}; | ||
| formatStats.setMin(minBytes); | ||
| formatStats.setMax(maxBytes); | ||
| formatStats.setNull_count(5); | ||
|
|
||
| // For BINARY column: stats should be ignored (only null_count preserved) | ||
| PrimitiveType binaryType = new PrimitiveType(Repetition.OPTIONAL, PrimitiveTypeName.BINARY, "bin_col"); | ||
| Statistics binaryStats = ParquetMetadataConverter.fromParquetStatisticsInternal( | ||
| corruptCreatedBy, formatStats, binaryType, ParquetMetadataConverter.SortOrder.SIGNED); | ||
| assertFalse("BINARY min/max should be ignored for corrupt file", binaryStats.hasNonNullValue()); | ||
|
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. you might need to rebase the PR because we switched all assertions to AssertJ and removed all JUnit4 usages. |
||
| assertEquals(5, binaryStats.getNumNulls()); | ||
|
|
||
| // For FIXED_LEN_BYTE_ARRAY column: stats should also be ignored | ||
| PrimitiveType fixedType = | ||
| new PrimitiveType(Repetition.OPTIONAL, PrimitiveTypeName.FIXED_LEN_BYTE_ARRAY, 4, "fixed_col"); | ||
| Statistics fixedStats = ParquetMetadataConverter.fromParquetStatisticsInternal( | ||
| corruptCreatedBy, formatStats, fixedType, ParquetMetadataConverter.SortOrder.SIGNED); | ||
| assertFalse("FIXED_LEN_BYTE_ARRAY min/max should be ignored for corrupt file", fixedStats.hasNonNullValue()); | ||
| assertEquals(5, fixedStats.getNumNulls()); | ||
|
|
||
| // For INT32 column: stats should NOT be ignored (per-column gate) | ||
| PrimitiveType int32Type = new PrimitiveType(Repetition.OPTIONAL, PrimitiveTypeName.INT32, "int_col"); | ||
| Statistics int32Stats = ParquetMetadataConverter.fromParquetStatisticsInternal( | ||
| corruptCreatedBy, formatStats, int32Type, ParquetMetadataConverter.SortOrder.SIGNED); | ||
| assertTrue("INT32 min/max should NOT be ignored for corrupt file", int32Stats.hasNonNullValue()); | ||
| assertEquals(5, int32Stats.getNumNulls()); | ||
|
|
||
| // For INT64 column: stats should NOT be ignored | ||
| org.apache.parquet.format.Statistics formatStatsLong = new org.apache.parquet.format.Statistics(); | ||
| byte[] minLong = new byte[] {0, 0, 0, 0, 0, 0, 0, 1}; | ||
| byte[] maxLong = new byte[] {0, 0, 0, 0, 0, 0, 0, 7}; | ||
| formatStatsLong.setMin(minLong); | ||
| formatStatsLong.setMax(maxLong); | ||
| formatStatsLong.setNull_count(5); | ||
| PrimitiveType int64Type = new PrimitiveType(Repetition.OPTIONAL, PrimitiveTypeName.INT64, "long_col"); | ||
| Statistics int64Stats = ParquetMetadataConverter.fromParquetStatisticsInternal( | ||
| corruptCreatedBy, formatStatsLong, int64Type, ParquetMetadataConverter.SortOrder.SIGNED); | ||
| assertTrue("INT64 min/max should NOT be ignored for corrupt file", int64Stats.hasNonNullValue()); | ||
| assertEquals(5, int64Stats.getNumNulls()); | ||
|
|
||
| // For a non-corrupt file, BINARY stats should be preserved | ||
| String goodCreatedBy = "parquet-mr version 1.8.0 (build abcd)"; | ||
| Statistics binaryStatsGood = ParquetMetadataConverter.fromParquetStatisticsInternal( | ||
| goodCreatedBy, formatStats, binaryType, ParquetMetadataConverter.SortOrder.SIGNED); | ||
| assertTrue("BINARY min/max should be kept for non-corrupt file", binaryStatsGood.hasNonNullValue()); | ||
| } | ||
|
|
||
| @Test | ||
| public void testSchemaGateSkipsCorruptStatsCheckForNonBinarySchema() throws Exception { | ||
| // A file with ONLY INT32/INT64 columns should never trigger corrupt stats detection, | ||
| // even with a known-corrupt createdBy string. | ||
| String corruptCreatedBy = "parquet-mr version 1.6.0 (build abcd)"; | ||
|
|
||
| MessageType intOnlySchema = Types.buildMessage() | ||
| .required(PrimitiveTypeName.INT32) | ||
| .named("id") | ||
| .required(PrimitiveTypeName.INT64) | ||
| .named("ts") | ||
| .named("msg"); | ||
|
|
||
| ParquetMetadataConverter converter = new ParquetMetadataConverter(); | ||
| List<SchemaElement> schemaElements = converter.toParquetSchema(intOnlySchema); | ||
|
|
||
| // Build ColumnMetaData with V1 statistics for the INT32 column | ||
| org.apache.parquet.format.Statistics stats = new org.apache.parquet.format.Statistics(); | ||
| stats.setMin(new byte[] {0, 0, 0, 1}); | ||
| stats.setMax(new byte[] {0, 0, 0, 7}); | ||
| stats.setNull_count(0); | ||
|
|
||
| ColumnMetaData cmd = new ColumnMetaData( | ||
| Type.INT32, | ||
| Collections.<org.apache.parquet.format.Encoding>emptyList(), | ||
| Collections.singletonList("id"), | ||
| UNCOMPRESSED, | ||
| 100L, | ||
| 200L, | ||
| 100L, | ||
| 0L); | ||
| cmd.setStatistics(stats); | ||
|
|
||
| ColumnChunk cc = new ColumnChunk(0L); | ||
| cc.setMeta_data(cmd); | ||
| RowGroup rg = new RowGroup(List.of(cc), 100L, 1); | ||
|
|
||
| FileMetaData fmd = new FileMetaData(1, schemaElements, 1, List.of(rg)); | ||
| fmd.setCreated_by(corruptCreatedBy); | ||
|
|
||
| // Parse via fromParquetMetadata – the schema gate should prevent fileHasCorruptStats | ||
| ParquetMetadata metadata = converter.fromParquetMetadata(fmd); | ||
| Statistics<?> columnStats = | ||
| metadata.getBlocks().get(0).getColumns().get(0).getStatistics(); | ||
|
|
||
| // Stats should be preserved (not ignored) because schema has no BINARY/FIXED columns | ||
| assertTrue( | ||
| "INT32 stats should be preserved when schema has no corrupt-stats-affected columns", | ||
| columnStats.hasNonNullValue()); | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
buildColumnChunkMetaDatacan delegate to a package-private overload that takes the boolean similar to what you have done but with few changes,No need to pass createdBy downstream, the boolean is all the internal overload needs. SortOrder computation moves here since we bypass
fromParquetStatisticsto avoid re-parsing createdBy as you have already done by replacingfromParquetStatisticsInternalwithfromParquetStatistics.Also notice how the new public methods we extracted in
CorruptStatisticsare being used in each delegate method hereThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
buildColumnChunkMetaDatanow delegates to a package-private overload taking the boolean, with the SortOrder computation moved into it; the public(..., String createdBy)overload is preserved for back-compat.