github-actions[bot] commented on code in PR #69536:
URL: https://github.com/apache/doris/pull/69536#discussion_r4366960335


##########
fe/fe-connector/fe-connector-iceberg/src/main/java/org/apache/doris/connector/iceberg/IcebergScanPlanProvider.java:
##########
@@ -2327,8 +2341,55 @@ private static List<Schema> metadataSchemaHistory(Table 
table) {
         return new ArrayList<>(table.schemas().values());
     }
 
-    private static void addHistoricalEqualityFields(
-            List<NestedField> fields, Set<Integer> missingFieldIds, Schema 
historicalSchema) {
+    /**
+     * A writer commits with the schema that is current at commit, so a key 
dropped before its delete commits loses
+     * the schema-only changes made before the drop from every snapshot. Field 
IDs are never reused and a field
+     * only relaxes compatibly, by promotion or from required to optional, so 
each field of the selected definition
+     * takes the widest promotion of its type and the optionality it has in 
any retained schema; neither depends on
+     * the order of the schemas. A field required in every retained schema 
stays required.
+     */
+    private static List<NestedField> withRetainedRelaxations(List<NestedField> 
fields, List<Schema> schemas) {
+        List<NestedField> relaxed = new ArrayList<>(fields.size());
+        for (NestedField field : fields) {
+            Type type = field.type();
+            boolean optional = field.isOptional();
+            for (Schema schema : schemas) {
+                NestedField retained = schema.findField(field.fieldId());
+                if (retained == null) {
+                    continue;
+                }
+                optional |= retained.isOptional();
+                if (type.isPrimitiveType() && retained.type().isPrimitiveType()
+                        && isWideningPromotion(type.asPrimitiveType(), 
retained.type().asPrimitiveType())) {
+                    type = retained.type();
+                }
+            }
+            if (type.isStructType()) {
+                type = 
Types.StructType.of(withRetainedRelaxations(type.asStructType().fields(), 
schemas));
+            }
+            relaxed.add(type.equals(field.type()) && optional == 
field.isOptional()
+                    ? field : 
Types.NestedField.from(field).ofType(type).isOptional(optional).build());

Review Comment:
   [P2] Convert DATE defaults before rebuilding a promoted carrier
   
   A non-null DATE default makes this rebuild fail. Iceberg 1.12.0's 
`NestedField.from(field)` copies the typed default literals, and the 
constructor converts them to the new type; `DateLiteral.to` accepts only DATE, 
so TIMESTAMP/TIMESTAMP_NS produces `Cannot cast default value to timestamp...`. 
See the pinned [builder/default 
conversion](https://github.com/apache/iceberg/blob/apache-iceberg-1.12.0/api/src/main/java/org/apache/iceberg/types/Types.java)
 and 
[DateLiteral](https://github.com/apache/iceberg/blob/apache-iceberg-1.12.0/api/src/main/java/org/apache/iceberg/expressions/Literals.java).
   
   For an unpartitioned v3 table, give optional `k DATE` initial default 
`2024-01-02`, append S1, publish a schema-only TIMESTAMP promotion with the 
equivalent midnight default, prepare an equality delete, drop k, then commit 
the pending delete. As in the new temporal tests, publish the promotion through 
`TableMetadata.Builder`. The lineage selects S1's DATE definition; retained 
widening then reaches this builder and aborts FE planning before either reader 
runs. The previous metadata lookup selected the already-typed TIMESTAMP 
definition. A copied DATE write default also triggers this.
   
   Convert both defaults into the promoted temporal type before rebuilding and 
add a default-bearing test. This differs from the existing temporal thread's 
fixed no-default comparison failure.



##########
fe/fe-connector/fe-connector-iceberg/src/main/java/org/apache/doris/connector/iceberg/IcebergScanPlanProvider.java:
##########
@@ -2327,8 +2341,55 @@ private static List<Schema> metadataSchemaHistory(Table 
table) {
         return new ArrayList<>(table.schemas().values());
     }
 
-    private static void addHistoricalEqualityFields(
-            List<NestedField> fields, Set<Integer> missingFieldIds, Schema 
historicalSchema) {
+    /**
+     * A writer commits with the schema that is current at commit, so a key 
dropped before its delete commits loses
+     * the schema-only changes made before the drop from every snapshot. Field 
IDs are never reused and a field
+     * only relaxes compatibly, by promotion or from required to optional, so 
each field of the selected definition
+     * takes the widest promotion of its type and the optionality it has in 
any retained schema; neither depends on
+     * the order of the schemas. A field required in every retained schema 
stays required.
+     */
+    private static List<NestedField> withRetainedRelaxations(List<NestedField> 
fields, List<Schema> schemas) {
+        List<NestedField> relaxed = new ArrayList<>(fields.size());
+        for (NestedField field : fields) {
+            Type type = field.type();
+            boolean optional = field.isOptional();
+            for (Schema schema : schemas) {
+                NestedField retained = schema.findField(field.fieldId());
+                if (retained == null) {
+                    continue;
+                }
+                optional |= retained.isOptional();
+                if (type.isPrimitiveType() && retained.type().isPrimitiveType()
+                        && isWideningPromotion(type.asPrimitiveType(), 
retained.type().asPrimitiveType())) {
+                    type = retained.type();

Review Comment:
   [P2] Preserve the historical DATE domain across future nanos promotions
   
   Scanning every retained schema also imports promotions committed after a 
time-travel target. DATE-to-TIMESTAMP_NS has a smaller representable range: the 
[v3 
specification](https://github.com/apache/iceberg/blob/apache-iceberg-1.12.0/format/spec.md#schema-evolution)
 requires out-of-range values to fail at runtime, so this promotion is not 
harmless for an earlier DATE read.
   
   Use an unpartitioned v3 table with optional `k DATE`, a row at `1000-01-01`, 
and a DATE equality delete for `1001-01-01`, without column metrics. Drop k and 
append an id-only file to establish target T. Later restore k's old nullable 
schema with `TableMetadata.Builder.setCurrentSchema(existingId)` and publish a 
TIMESTAMP_NS schema. Nullable-field deletion rollback is allowed by the same 
spec, and the [metadata 
builder](https://github.com/apache/iceberg/blob/apache-iceberg-1.12.0/core/src/main/java/org/apache/iceberg/TableMetadata.java)
 accepts this history.
   
   Reading T should still compare DATEs and retain both rows. Instead, its 
lineage's DATE carrier is widened by that future schema. V2 
`_resolve_equality_delete_fields` chooses TIMESTAMP_NS and 
`_load_equality_delete_file` uses `EvolutionCast`, whose DATE-to-nanos strict 
conversion rejects the year-1001 tombstone. The previous lookup stopped at T's 
schema and kept DATE. Preserve historical comparison domains for 
range-restricting promotions and add this pinned-read case. This is distinct 
from the existing temporal thread: the problematic promoted schema is after T, 
and no defaults are needed.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to