wirybeaver commented on code in PR #22988:
URL: https://github.com/apache/datafusion/pull/22988#discussion_r3614960602


##########
datafusion/expr/src/logical_plan/dml.rs:
##########
@@ -299,12 +299,129 @@ impl Display for InsertOp {
 }
 
 /// Describes a MERGE INTO operation's parameters.
-#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct MergeIntoOp {
     /// The join condition from `ON <expr>`.
     pub on: Expr,
     /// The WHEN clauses, in the order they appeared in the SQL.
     pub clauses: Vec<MergeIntoClause>,
+    /// Schema of the target table (qualified with the alias or table name used
+    /// in the SQL). Stored here so the analyzer can build a combined
+    /// target+source schema for type coercion and function rewrites.
+    pub target_schema: DFSchemaRef,

Review Comment:
   Good call — done. I removed the `target_schema` field from `MergeIntoOp`, so 
it's back to `{ on, clauses }` and the struct is no longer SemVer-breaking to 
construct with a struct literal.
   
   The target schema is now **derived on demand** in the analyzer passes (type 
coercion and function rewrites) from the existing `DmlStatement { table_name, 
target }`, mirroring how `TableScan` already merges its source schema in the 
same functions:
   
   ```rust
   if let LogicalPlan::Dml(DmlStatement {
       op: WriteOp::MergeInto(_),
       table_name,
       target,
       ..
   }) = &plan
   {
       let target_schema =
           DFSchema::try_from_qualified_schema(table_name.clone(), 
&target.schema())?;
       schema.merge(&target_schema);
   }
   ```
   
   To make that derivation sufficient, SQL planning now canonicalizes target 
alias-qualified column references (`t.col`) to the real target table qualifier 
before storing the plan, so no alias/planning state leaks onto the public 
struct. Added a planner test (`plan_merge_into_canonicalizes_target_alias`) 
verifying `MERGE INTO j1 AS t USING j2 AS s ON t.j1_id = s.j2_id ...` produces 
`j1.j1_id = s.j2_id` (target alias rewritten, source alias preserved).
   



##########
datafusion/proto/src/logical_plan/mod.rs:
##########
@@ -1258,11 +1258,21 @@ impl AsLogicalPlan for LogicalPlanNode {
                 .build()
             }
             LogicalPlanType::Dml(dml_node) => {
-                let write_op =
+                let table_name =
+                    from_table_reference(dml_node.table_name.as_ref(), "DML 
")?;
+                let target = to_table_source(&dml_node.target, ctx, 
extension_codec)?;
+                let mut write_op =
                     from_proto::parse_write_op(dml_node, ctx, 
extension_codec)?;
+                if let WriteOp::MergeInto(ref mut merge_op) = write_op {
+                    merge_op.target_schema =

Review Comment:
   Agreed, and I went with your preferred approach: canonicalize target alias 
references to the real target table qualifier during SQL planning, then avoid 
serializing any extra state.
   
   `merge_to_plan` now rewrites target-side columns so `t.id` is stored as 
`target.id` (source aliases like `s.id` are left untouched). Because the stored 
expressions always use the `table_name` qualifier, there's nothing 
alias-specific left to lose across a proto roundtrip — so I removed this 
reconstruction block entirely. Proto deserialization just rebuilds the 
`DmlStatement` from `table_name` + `target`, and the analyzer derives the 
target schema from those on the fly when it needs to resolve MERGE expressions.
   



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