-
Notifications
You must be signed in to change notification settings - Fork 3k
Fixes read metadata failed after dropped partition for V1 format #3411
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 all commits
340d1cf
b38e747
0aa180b
ac228c0
07688e6
4bd2c51
574ddbc
159a0d5
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 |
|---|---|---|
|
|
@@ -32,6 +32,7 @@ | |
| import org.apache.iceberg.transforms.Transform; | ||
| import org.apache.iceberg.transforms.Transforms; | ||
| import org.apache.iceberg.transforms.UnknownTransform; | ||
| import org.apache.iceberg.types.Type; | ||
| import org.apache.iceberg.types.Types.NestedField; | ||
| import org.apache.iceberg.types.Types.StructType; | ||
|
|
||
|
|
@@ -210,7 +211,8 @@ public static StructType partitionType(Table table) { | |
| } | ||
|
|
||
| Map<Integer, PartitionField> fieldMap = Maps.newHashMap(); | ||
| List<NestedField> structFields = Lists.newArrayList(); | ||
| Map<Integer, Type> typeMap = Maps.newHashMap(); | ||
| Map<Integer, String> nameMap = Maps.newHashMap(); | ||
|
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 have run into similar issues, and I think this will help resolve the type change of columns in older specs.
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.
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. @aokolnychyi I think you're referring to what's happening in V1 tables. For those the spec is ever-growing in a way that no partition fields/transforms are removed, but rather converted to void. Since in V2, specs don't retain old deleted partition fields this rename is not required for normal operations. The problem I'm describing only affects the metadata table queries, because for V2, due to the lack of above renames, Partitioning.partitionType() collects all partition fields from all previous specs too. With the lack of renames this can result in the same field name being present multiple times, and cause the PartitionsTable's (or DataFilesTable's) schema to be failed to get constructed.
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 guess you are right, @szlta. Here is an example to reproduce. While it would be great to update the logic that evolves the spec, I think we have to adapt the method that builds a common representation too. Otherwise, existing tables may be broken. Maintaining a set of used names and appending a suffix of the field ID sounds like a reasonable approach. Thoughts, @rdblue @RussellSpitzer @szehon-ho @flyrain?
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. Looks like we are using field IDs while projecting values from the common representation in Spark.
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 think we have two cases to solve this problem. The combined partition struct type is used in both metadata tables and when we need to project the _partition field for certain queries. For queries that use the _partition field, I think adding a suffix to make the names unique is the right approach. That's internal so it doesn't really matter what we rename to, as long as we get the projections that produce the partition tuple for a given spec right. For metadata tables, we expect the field names to match the partition names. A simple example is that we use identity partitions using the original column name. So it would be weird to partition by In @aokolnychyi's example, This is also an area where we may want to bring back compatible partition columns. There's no reason why Anton's example couldn't detect 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. What about partition transforms where the transform itself could change, but the field name remains the same? A partition in the old spec where I agree with @rdblue that it's weird to have
So as per above example use case the Note this is how it would be for V2 tables. For V1, due to the renaming we're already doing, the renamed fields would be present too (unless we aim at changing that too): I'm personally more for solution 2, where we don't have to omit the old partitions but at the same time we get a nice and coherent partition info result. It's kind of in league what I'm proposing in issue #4292 (we could also continue the discussion of this problem there, I didn't mean to hijack @ConeyLiu 's PR like this)
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'm also up for solution 2, where we rename just the columns from older specs. That sounds like a reasonable solution.
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'll catch up today. Sorry for the delay!
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 think that the fixes in this PR look good other than a couple minor updates that are needed. We should definitely follow up with a PR that fixes the names as suggested by @szlta. |
||
|
|
||
| // sort the spec IDs in descending order to pick up the most recent field names | ||
| List<Integer> specIds = table.specs().keySet().stream() | ||
|
|
@@ -222,27 +224,40 @@ public static StructType partitionType(Table table) { | |
|
|
||
| for (PartitionField field : spec.fields()) { | ||
| int fieldId = field.fieldId(); | ||
| NestedField structField = spec.partitionType().field(fieldId); | ||
| PartitionField existingField = fieldMap.get(fieldId); | ||
|
|
||
| if (existingField == null) { | ||
| fieldMap.put(fieldId, field); | ||
| NestedField structField = spec.partitionType().field(fieldId); | ||
| structFields.add(structField); | ||
| typeMap.put(fieldId, structField.type()); | ||
| nameMap.put(fieldId, structField.name()); | ||
|
|
||
ConeyLiu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } else { | ||
| // verify the fields are compatible as they may conflict in v1 tables | ||
| ValidationException.check(equivalentIgnoringNames(field, existingField), | ||
| "Conflicting partition fields: ['%s', '%s']", | ||
| field, existingField); | ||
|
|
||
| // use the correct type for dropped partitions in v1 tables | ||
| if (isVoidTransform(existingField) && !isVoidTransform(field)) { | ||
| fieldMap.put(fieldId, field); | ||
| typeMap.put(fieldId, structField.type()); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| List<NestedField> sortedStructFields = structFields.stream() | ||
| .sorted(Comparator.comparingInt(NestedField::fieldId)) | ||
| List<NestedField> sortedStructFields = fieldMap.keySet().stream() | ||
| .sorted(Comparator.naturalOrder()) | ||
| .map(fieldId -> NestedField.optional(fieldId, nameMap.get(fieldId), typeMap.get(fieldId))) | ||
|
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 think the changes in this file are correct and fix the first issue, where the type may be incorrect for void transform fields in v1 tables. |
||
| .collect(Collectors.toList()); | ||
| return StructType.of(sortedStructFields); | ||
| } | ||
|
|
||
| private static boolean isVoidTransform(PartitionField field) { | ||
| return field.transform().equals(Transforms.alwaysNull()); | ||
| } | ||
|
|
||
| private static List<Transform<?, ?>> collectUnknownTransforms(Table table) { | ||
| List<Transform<?, ?>> unknownTransforms = Lists.newArrayList(); | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.