hubgeter commented on code in PR #69188:
URL: https://github.com/apache/doris/pull/69188#discussion_r4292806162


##########
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:
   Fixed in 1ff3d44d061. `toPartitionFieldName` now resolves the identity 
partition over the top-level SOURCE column first and only falls back to a 
field-name match, so the alias cannot capture the key. Your example is pinned 
by the new tests 
`validateStaticPartitionColumnsPrefersTheIdentitySourceOverACaseFoldedAlias` 
and `staticPartitionKeysPreferTheIdentitySourceOverACaseFoldedFieldAlias` (the 
spec builder does accept `identity(other, "ID")` next to `identity(id, 
"id_identity")`, as you noted).



##########
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:
   Fixed in 1ff3d44d061. Static targets must now resolve to a TOP-LEVEL source 
column (`IcebergPartitionUtils#topLevelSourceColumnName`); `NestedField#name()` 
is no longer treated as a column name, so `PARTITION(id=2)` over 
`identity(payload.id)` reads as unknown and the field name itself reports "its 
source is a nested field ... the engine can only materialize the literal into a 
top-level column". Covered by 
`validateStaticPartitionColumnsRejectsNestedPartitionSources`.



##########
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:
   Fixed in 1ff3d44d061. The identity partition over the source is searched 
across ALL fields before any non-identity error is raised, and the key rewrite 
shares that lookup, so a spec that declares `bucket(8, id)` before 
`identity(id)` keeps its static writes. Pinned by 
`validateStaticPartitionColumnsFindsIdentityAfterANonIdentityFieldOnTheSameSource`
 and `staticPartitionKeysFindTheIdentityFieldRegardlessOfFieldOrder`.



##########
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:
   Fixed in 1ff3d44d061. Validation now uses the statement's pinned writer 
schema (`IcebergStatementScope.activeWriteSchema`) - the same copy binding, 
planning and the BE sink consume, registered by `pinConnectorWriteSchema` 
before analysis - and only falls back to the loaded table when no pin exists. 
`validateStaticPartitionColumnsUsesThePinnedBranchWriterSchema` pins your 
branch case: the pinned (`@branch(old)`) statement accepts `id`, while 
validating against the renamed current schema would reject it and advise `key`.



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