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


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtils.java:
##########
@@ -477,15 +491,67 @@ private static Plan normalizePlanWithoutLock(LogicalPlan 
plan, TableIf table,
     }
 
     static List<Column> connectorWriteSchema(TableIf table, boolean full) {
+        return pinnedConnectorWriteSchema(table).orElseGet(() -> 
table.getBaseSchema(full));
+    }
+
+    private static Optional<List<Column>> pinnedConnectorWriteSchema(TableIf 
table) {
         ConnectContext context = ConnectContext.get();
-        if (context != null && context.getStatementContext() != null) {
-            Optional<List<Column>> pinned =
-                    
context.getStatementContext().getConnectorWriteSchema(table.getId());
-            if (pinned.isPresent()) {
-                return pinned.get();
-            }
+        if (context == null || context.getStatementContext() == null) {
+            return Optional.empty();
         }
-        return table.getBaseSchema(full);
+        return 
context.getStatementContext().getConnectorWriteSchema(table.getId());
+    }
+
+    /**
+     * The pinned writer schema of a target whose column defaults apply to 
writes only. The read schema of such a
+     * table carries no defaults, so DEFAULT(column) in an INSERT into it must 
resolve against this schema.
+     */
+    private static Optional<List<Column>> writeOnlyDefaultColumns(TableIf 
table) {
+        if (!(table instanceof PluginDrivenExternalTable)
+                || !((PluginDrivenExternalTable) 
table).hasWriteOnlyDefaults()) {
+            return Optional.empty();
+        }
+        return pinnedConnectorWriteSchema(table);
+    }
+
+    /** Resolve DEFAULT(column) in one inserted value by name against the 
target's pinned writer schema. */
+    private static NamedExpression resolveDefaultReferences(NamedExpression 
value,
+            Optional<List<Column>> writeOnlyDefaultColumns, TableIf table, 
List<String> targetNameParts) {
+        if (!writeOnlyDefaultColumns.isPresent()) {
+            return value;
+        }
+        return (NamedExpression) 
ConnectorWriteSchemaUtils.resolveDefaultReferences(value,
+                writeOnlyDefaultColumns.get(), (ExternalTable) table, 
ConnectContext.get(), targetNameParts, null);
+    }
+
+    /**
+     * Resolve DEFAULT(column) in the select lists of an INSERT ... SELECT by 
name against the target's pinned
+     * writer schema, before the source relations are bound, so it names a 
target column whatever the source is.
+     * A derived table in FROM is a query of its own: DEFAULT(column) in it 
names a column of its own FROM clause,
+     * as without this resolution, so it is left alone.
+     */
+    private static Plan resolveSelectDefaultReferences(Plan query, 
Optional<List<Column>> writeOnlyDefaultColumns,
+            TableIf table, List<String> targetNameParts) {
+        if (query instanceof LogicalSubQueryAlias) {
+            return query;
+        }
+        Plan plan = query.withChildren(child -> resolveSelectDefaultReferences(
+                child, writeOnlyDefaultColumns, table, targetNameParts));
+        if (plan instanceof LogicalProject) {
+            LogicalProject<?> project = (LogicalProject<?>) plan;
+            return project.withProjects(project.getProjects().stream()
+                    .map(projection -> resolveDefaultReferences(

Review Comment:
   [P2] Resolve defaults inside SELECT star replacements
   
   Let OLAP `src(c INT DEFAULT 7)` contain one row and Iceberg target `t(c 
INT)` have pinned write-default `9`:
   
   ```sql
   INSERT INTO t(c) SELECT * REPLACE(DEFAULT(c) AS c) FROM src;
   ```
   
   The reduced parsed plan is `Sink(t.c) -> 
Project(UnboundStar{replacedAlias=[DEFAULT(c) AS c]}) -> Scan(src.c)`. 
`UnboundStar` is a leaf and stores replacement aliases outside its expression 
children, so `resolveDefaultReferences` never visits this DEFAULT. 
`BindExpression.bindProject` later binds the replacement against `src`, and 
`RewriteDefaultExpression` produces `Project(7 AS c)`, silently writing 7; the 
equivalent direct `SELECT DEFAULT(c) FROM src` now writes the target's 9. 
Normalize `getReplacedAlias()` against the pinned target before binding and add 
coverage with different source/target defaults. This is distinct from the 
existing aggregate/CTE/subquery omission: the outer LogicalProject is visited, 
but its star-replacement payload is skipped.



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