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


##########
fe/fe-connector/fe-connector-iceberg/src/main/java/org/apache/doris/connector/iceberg/IcebergPartitionUtils.java:
##########
@@ -138,6 +139,46 @@ static List<String> getIdentityPartitionColumns(Table 
table) {
         return new ArrayList<>(partitionColumns);
     }
 
+    /**
+     * Rewrites a static-partition spec ({@code INSERT [OVERWRITE] ... 
PARTITION (col = val)}) so every key is the
+     * matching partition FIELD name, the identifier both downstream consumers 
address: the BE sink matches
+     * {@code static_partition_values} against {@code PartitionField#name()}, 
and the commit row filter resolves
+     * the field to its source column.
+     *
+     * <p>The engine's PARTITION clause names the identity partition's SOURCE 
column instead — the identifier Spark
+     * resolves for a static partition clause ({@code 
Analyzer.partitionColumnNames} takes the identity transform's
+     * field reference) and the one {@code SHOW CREATE TABLE} renders — so the 
two spellings must be bridged once,
+     * here, before either consumer sees the spec. A key that matches no field 
of the current spec is kept verbatim
+     * and left to the callers' fail-loud guards.</p>
+     */
+    static Map<String, String> toPartitionFieldKeys(Map<String, String> 
staticPartitions, Schema schema,
+            PartitionSpec spec) {
+        if (staticPartitions == null || staticPartitions.isEmpty() || spec == 
null) {
+            return staticPartitions;
+        }
+        Map<String, String> canonical = new HashMap<>(staticPartitions.size());
+        for (Map.Entry<String, String> entry : staticPartitions.entrySet()) {
+            canonical.put(toPartitionFieldName(schema, spec, entry.getKey()), 
entry.getValue());
+        }
+        return canonical;
+    }
+
+    /**
+     * The partition field a static-partition name addresses: its SOURCE 
column name, or the field name itself.
+     * Iceberg refuses to name a partition field after a column other than its 
own source
+     * ({@code PartitionSpec.Builder#checkAndAddPartitionName}), so the two 
lookups cannot disagree.
+     */
+    private static String toPartitionFieldName(Schema schema, PartitionSpec 
spec, String name) {
+        for (PartitionField field : spec.fields()) {
+            NestedField source = schema.findField(field.sourceId());
+            if ((source != null && source.name().equalsIgnoreCase(name))

Review Comment:
   [P1] Resolve source columns before matching partition-field aliases
   
   The two lookups can disagree because [Iceberg's 
builder](https://github.com/apache/iceberg/blob/apache-iceberg-1.12.0/api/src/main/java/org/apache/iceberg/PartitionSpec.java#L358-L402)
 checks names case-sensitively by default. With schema `(id INT, other INT)` 
and spec `identity(other, "ID"), identity(id, "id_identity")`, 
`PARTITION(id=7)` validates against source `id`, but this loop returns the 
earlier field `ID`, whose source is `other`. An overwrite supplying `other=9` 
then writes row `(id=7, other=9)` while BE stamps `other`'s partition as 7; the 
commit also filters `other=7` instead of `id=7`. This can overwrite the wrong 
partitions and commit files whose rows contradict their partition metadata. 
Resolve the validated identity source first, and only consider field aliases as 
an unambiguous fallback.



##########
fe/fe-connector/fe-connector-iceberg/src/main/java/org/apache/doris/connector/iceberg/IcebergConnectorMetadata.java:
##########
@@ -1911,23 +1919,60 @@ public void 
validateStaticPartitionColumns(ConnectorSession session, ConnectorTa
             throw new DorisConnectorException(String.format(
                     "Table %s is not partitioned, cannot use static partition 
syntax", tableName));
         }
-        Map<String, PartitionField> partitionFieldMap = new HashMap<>();
+        for (String colName : staticPartitionColumnNames) {
+            validateStaticPartitionColumn(table.schema(), spec, colName, 
tableName);
+        }
+    }
+
+    /** The single-name rule of {@link #validateStaticPartitionColumns}. */
+    private static void validateStaticPartitionColumn(Schema schema, 
PartitionSpec spec, String colName,
+            String tableName) {
         for (PartitionField field : spec.fields()) {
-            partitionFieldMap.put(field.name(), field);
+            Types.NestedField source = schema.findField(field.sourceId());
+            if (source == null || !source.name().equalsIgnoreCase(colName)) {

Review Comment:
   [P1] Keep nested source fields distinct from top-level columns
   
   `NestedField.name()` is only the leaf name. For a supported table `(id INT, 
payload STRUCT<id:INT>)` partitioned by `identity(payload.id)` as `nested_id`, 
this accepts `PARTITION(id=2)` even though the top-level `id` is not a 
partition source. `BindSink` materializes the literal into that top-level 
column, while `toPartitionFieldKeys` rewrites it to `nested_id`. An overwrite 
supplying `payload.id=9` therefore writes that value into the file but stamps 
its nested partition as 2 through BE's static override. Even an initially empty 
table can commit this inconsistent file; the overwrite path does not validate 
added files against the filter. Resolve the source's full path/field identity 
and reject nested static targets unless the binder can materialize that same 
source.



##########
fe/fe-connector/fe-connector-iceberg/src/main/java/org/apache/doris/connector/iceberg/IcebergConnectorMetadata.java:
##########
@@ -1911,23 +1919,60 @@ public void 
validateStaticPartitionColumns(ConnectorSession session, ConnectorTa
             throw new DorisConnectorException(String.format(
                     "Table %s is not partitioned, cannot use static partition 
syntax", tableName));
         }
-        Map<String, PartitionField> partitionFieldMap = new HashMap<>();
+        for (String colName : staticPartitionColumnNames) {
+            validateStaticPartitionColumn(table.schema(), spec, colName, 
tableName);
+        }
+    }
+
+    /** The single-name rule of {@link #validateStaticPartitionColumns}. */
+    private static void validateStaticPartitionColumn(Schema schema, 
PartitionSpec spec, String colName,
+            String tableName) {
         for (PartitionField field : spec.fields()) {
-            partitionFieldMap.put(field.name(), field);
+            Types.NestedField source = schema.findField(field.sourceId());
+            if (source == null || !source.name().equalsIgnoreCase(colName)) {
+                continue;
+            }
+            // The SOURCE column of a partition field: a legal static target 
only for an identity transform,
+            // because a transformed partition value is not the column value 
and cannot be written through it.
+            if (field.transform().isIdentity()) {
+                return;
+            }
+            throw new DorisConnectorException(String.format(

Review Comment:
   [P2] Search all matching transforms before rejecting the source
   
   A valid Iceberg spec can contain `bucket("id", 8)` before `identity("id")`. 
`PARTITION(id=7)` previously resolved the identity field named `id`, but the 
new loop stops at the bucket field with the same source and throws, never 
reaching the identity field. This breaks static INSERT and OVERWRITE on such 
tables solely because of spec field order. Look for a matching identity source 
before reporting a non-identity error, and make `toPartitionFieldName` select 
that same identity field rather than the first source match.



##########
fe/fe-connector/fe-connector-iceberg/src/main/java/org/apache/doris/connector/iceberg/IcebergConnectorMetadata.java:
##########
@@ -1911,23 +1919,60 @@ public void 
validateStaticPartitionColumns(ConnectorSession session, ConnectorTa
             throw new DorisConnectorException(String.format(
                     "Table %s is not partitioned, cannot use static partition 
syntax", tableName));
         }
-        Map<String, PartitionField> partitionFieldMap = new HashMap<>();
+        for (String colName : staticPartitionColumnNames) {
+            validateStaticPartitionColumn(table.schema(), spec, colName, 
tableName);

Review Comment:
   [P2] Validate static names against the pinned branch writer schema
   
   Create an identity-partitioned `t(id INT, value STRING)`, create branch 
`old` from a snapshot, then rename main's source column `id` to `key`. `INSERT 
INTO t@branch(old) PARTITION(id=2) (value) VALUES('x')` still binds to the 
branch's old `id` schema, but this call validates against current 
`table.schema()`: it misses source `key` and rejects partition field `id`, 
advising the user to use `key`, which is absent from that branch. The previous 
field-keyed validation accepted this ordinary INSERT, and the existing writer 
materializes and appends it using the pinned branch schema. Use the already 
active `IcebergWriteSchemaContext` for validation so it agrees with binding and 
planning, and cover a branch static INSERT after a main-only rename.



-- 
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