-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Core: Calling rewrite_position_delete_files fails on tables with more than 1k columns #10020
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
c8288b0
3efefc5
7ff8c9d
cfbb0a6
2642eac
63437ac
a23d249
d45ad89
50871d2
57ede3b
394f07b
6aa3993
1052a33
02d46c5
31dc0e0
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 |
|---|---|---|
|
|
@@ -21,6 +21,7 @@ | |
| import java.io.Serializable; | ||
| import java.util.Arrays; | ||
| import java.util.Collection; | ||
| import java.util.Collections; | ||
| import java.util.Deque; | ||
| import java.util.List; | ||
| import java.util.Locale; | ||
|
|
@@ -34,6 +35,7 @@ | |
| import org.apache.iceberg.relocated.com.google.common.collect.ImmutableMap; | ||
| import org.apache.iceberg.relocated.com.google.common.collect.ImmutableSet; | ||
| import org.apache.iceberg.relocated.com.google.common.collect.Lists; | ||
| import org.apache.iceberg.relocated.com.google.common.collect.Maps; | ||
| import org.apache.iceberg.relocated.com.google.common.collect.Sets; | ||
| import org.apache.iceberg.relocated.com.google.common.primitives.Ints; | ||
| import org.apache.iceberg.types.Type; | ||
|
|
@@ -65,6 +67,8 @@ public class Schema implements Serializable { | |
| private transient Map<Integer, Accessor<StructLike>> idToAccessor = null; | ||
| private transient Map<Integer, String> idToName = null; | ||
| private transient Set<Integer> identifierFieldIdSet = null; | ||
| private transient Map<Integer, Integer> idsToReassigned; | ||
| private transient Map<Integer, Integer> idsToOriginal; | ||
|
|
||
| public Schema(List<NestedField> columns, Map<String, Integer> aliases) { | ||
| this(columns, aliases, ImmutableSet.of()); | ||
|
|
@@ -83,21 +87,47 @@ public Schema(List<NestedField> columns, Set<Integer> identifierFieldIds) { | |
| this(DEFAULT_SCHEMA_ID, columns, identifierFieldIds); | ||
| } | ||
|
|
||
| public Schema(List<NestedField> columns, Set<Integer> identifierFieldIds, TypeUtil.GetID getId) { | ||
| this(DEFAULT_SCHEMA_ID, columns, identifierFieldIds, getId); | ||
| } | ||
|
|
||
| public Schema(int schemaId, List<NestedField> columns) { | ||
| this(schemaId, columns, ImmutableSet.of()); | ||
| } | ||
|
|
||
| public Schema(int schemaId, List<NestedField> columns, Set<Integer> identifierFieldIds) { | ||
| this(schemaId, columns, null, identifierFieldIds); | ||
| this(schemaId, columns, null, identifierFieldIds, null); | ||
| } | ||
|
|
||
| public Schema( | ||
| int schemaId, | ||
| List<NestedField> columns, | ||
| Set<Integer> identifierFieldIds, | ||
| TypeUtil.GetID getId) { | ||
| this(schemaId, columns, null, identifierFieldIds, getId); | ||
| } | ||
|
|
||
| public Schema( | ||
| int schemaId, | ||
| List<NestedField> columns, | ||
| Map<String, Integer> aliases, | ||
| Set<Integer> identifierFieldIds) { | ||
| this(schemaId, columns, aliases, identifierFieldIds, null); | ||
| } | ||
|
|
||
| public Schema( | ||
| int schemaId, | ||
| List<NestedField> columns, | ||
| Map<String, Integer> aliases, | ||
| Set<Integer> identifierFieldIds, | ||
| TypeUtil.GetID getID) { | ||
| this.schemaId = schemaId; | ||
| this.struct = StructType.of(columns); | ||
|
|
||
| this.idsToOriginal = Maps.newHashMap(); | ||
| this.idsToReassigned = Maps.newHashMap(); | ||
| List<NestedField> finalColumns = reassignIds(columns, getID); | ||
|
|
||
| this.struct = StructType.of(finalColumns); | ||
| this.aliasToId = aliases != null ? ImmutableBiMap.copyOf(aliases) : null; | ||
|
|
||
| // validate IdentifierField | ||
|
|
@@ -507,4 +537,40 @@ public String toString() { | |
| .map(this::identifierFieldToString) | ||
| .collect(Collectors.toList()))); | ||
| } | ||
|
|
||
| /** | ||
| * The ID's of some fields will be re-assigned if GetID is specified for the Schema. | ||
| * | ||
| * @return map of original to reassigned field ids | ||
| */ | ||
| public Map<Integer, Integer> idsToReassigned() { | ||
| return idsToReassigned != null ? idsToReassigned : Collections.emptyMap(); | ||
| } | ||
|
|
||
| /** | ||
| * The ID's of some fields will be re-assigned if GetID is specified for the Schema. | ||
| * | ||
| * @return map of reassigned to original field ids | ||
| */ | ||
| public Map<Integer, Integer> idsToOriginal() { | ||
| return idsToOriginal != null ? idsToOriginal : Collections.emptyMap(); | ||
| } | ||
|
|
||
| private List<NestedField> reassignIds(List<NestedField> columns, TypeUtil.GetID getID) { | ||
| if (getID == null) { | ||
| return columns; | ||
| } | ||
| Type res = | ||
| TypeUtil.assignIds( | ||
|
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. I think this may have some ordering issues ... I'm not sure if this is possible but say I see transformId = 1000 Won't I still have a problem?
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. Im not entirely sure I get your use case. But , we have two lists conceptually: idsToReassign, usedIds. We go through all the fields, and if find an idToReassign, we just find the next value that is not in usedIds. Are you asking, what if we both have idToReassign = usedIds = (1000). I think its ok, it will just pick the next one 1001, (even though it didnt have to..) |
||
| StructType.of(columns), | ||
| oldId -> { | ||
| int newId = getID.get(oldId); | ||
| if (newId != oldId) { | ||
| idsToReassigned.put(oldId, newId); | ||
| idsToOriginal.put(newId, oldId); | ||
| } | ||
| return newId; | ||
| }); | ||
| return res.asStructType().fields(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| /* | ||
| * Licensed to the Apache Software Foundation (ASF) under one | ||
| * or more contributor license agreements. See the NOTICE file | ||
| * distributed with this work for additional information | ||
| * regarding copyright ownership. The ASF licenses this file | ||
| * to you under the Apache License, Version 2.0 (the | ||
| * "License"); you may not use this file except in compliance | ||
| * with the License. You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, | ||
| * software distributed under the License is distributed on an | ||
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| * KIND, either express or implied. See the License for the | ||
| * specific language governing permissions and limitations | ||
| * under the License. | ||
| */ | ||
| package org.apache.iceberg.types; | ||
|
|
||
| import java.util.Iterator; | ||
| import java.util.List; | ||
| import java.util.function.Supplier; | ||
| import org.apache.iceberg.Schema; | ||
| import org.apache.iceberg.relocated.com.google.common.collect.Lists; | ||
|
|
||
| class AssignIds extends TypeUtil.CustomOrderSchemaVisitor<Type> { | ||
| private final TypeUtil.GetID getID; | ||
|
|
||
| AssignIds(TypeUtil.GetID getID) { | ||
| this.getID = getID; | ||
| } | ||
|
|
||
| private int idFor(int id) { | ||
| return getID.get(id); | ||
| } | ||
|
|
||
| @Override | ||
| public Type schema(Schema schema, Supplier<Type> future) { | ||
| return future.get(); | ||
| } | ||
|
|
||
| @Override | ||
| public Type struct(Types.StructType struct, Iterable<Type> futures) { | ||
| List<Types.NestedField> fields = struct.fields(); | ||
| int length = struct.fields().size(); | ||
|
|
||
| // assign IDs for this struct's fields first | ||
| List<Integer> newIds = Lists.newArrayListWithExpectedSize(length); | ||
| for (Types.NestedField field : fields) { | ||
| newIds.add(idFor(field.fieldId())); | ||
| } | ||
|
|
||
| List<Types.NestedField> newFields = Lists.newArrayListWithExpectedSize(length); | ||
| Iterator<Type> types = futures.iterator(); | ||
| for (int i = 0; i < length; i += 1) { | ||
| Types.NestedField field = fields.get(i); | ||
| Type type = types.next(); | ||
| if (field.isOptional()) { | ||
| newFields.add(Types.NestedField.optional(newIds.get(i), field.name(), type, field.doc())); | ||
| } else { | ||
| newFields.add(Types.NestedField.required(newIds.get(i), field.name(), type, field.doc())); | ||
| } | ||
| } | ||
|
|
||
| return Types.StructType.of(newFields); | ||
| } | ||
|
|
||
| @Override | ||
| public Type field(Types.NestedField field, Supplier<Type> future) { | ||
| return future.get(); | ||
| } | ||
|
|
||
| @Override | ||
| public Type list(Types.ListType list, Supplier<Type> future) { | ||
| int newId = idFor(list.elementId()); | ||
| if (list.isElementOptional()) { | ||
| return Types.ListType.ofOptional(newId, future.get()); | ||
| } else { | ||
| return Types.ListType.ofRequired(newId, future.get()); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public Type map(Types.MapType map, Supplier<Type> keyFuture, Supplier<Type> valueFuture) { | ||
| int newKeyId = idFor(map.keyId()); | ||
| int newValueId = idFor(map.valueId()); | ||
| if (map.isValueOptional()) { | ||
| return Types.MapType.ofOptional(newKeyId, newValueId, keyFuture.get(), valueFuture.get()); | ||
| } else { | ||
| return Types.MapType.ofRequired(newKeyId, newValueId, keyFuture.get(), valueFuture.get()); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public Type primitive(Type.PrimitiveType primitive) { | ||
| return primitive; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -475,6 +475,10 @@ public NestedField asRequired() { | |
| return new NestedField(false, id, name, type, doc); | ||
| } | ||
|
|
||
| public NestedField withFieldId(int newId) { | ||
|
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. As as a note for other reviewers this is a new Public API but I think it's very safe to include |
||
| return new NestedField(isOptional, newId, name, type, doc); | ||
| } | ||
|
|
||
| public int fieldId() { | ||
| return id; | ||
| } | ||
|
|
||
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.
Looks like adding a new ctor method as suggested changes the serializationVersionUID, is it ok? @RussellSpitzer
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.
+1 Yep, this would only be a concern if we were worried about folks using different Iceberg versions on client and server, this shouldn't be the case.