dongjoon-hyun commented on code in PR #55518:
URL: https://github.com/apache/spark/pull/55518#discussion_r4315467526


##########
sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/v2/V2Writes.scala:
##########
@@ -109,22 +111,75 @@ object V2Writes extends Rule[LogicalPlan] with 
PredicateHelper {
       WriteToDataSourceV2(Some(r), microBatchWrite, newQuery, customMetrics)
 
     case rd @ ReplaceData(r: DataSourceV2Relation, _, query, _, projections, 
_, None) =>
-      val rowSchema = projections.rowProjection.schema
+      val (rowSchema, columnUpdateSchema) =
+        writeSchemas(projections.rowProjection.schema, hasNarrowRows(rd))
       val metadataSchema = projections.metadataProjection.map(_.schema)
       val writeOptions = mergeOptions(Map.empty, 
r.options.asCaseSensitiveMap.asScala.toMap)
-      val writeBuilder = newWriteBuilder(r.table, writeOptions, rowSchema, 
metadataSchema)
+      val writeBuilder = newWriteBuilder(r.table, writeOptions, rowSchema, 
metadataSchema,
+        columnUpdateSchema)
       val write = writeBuilder.build()
+      validateColumnUpdateWrite(rd, write)
       val newQuery = DistributionAndOrderingUtils.prepareQuery(write, query, 
r.funCatalog)
       rd.copy(write = Some(write), query = newQuery)
 
     case wd @ WriteDelta(r: DataSourceV2Relation, _, query, _, projections, _, 
None) =>
       val writeOptions = mergeOptions(Map.empty, 
r.options.asCaseSensitiveMap.asScala.toMap)
-      val deltaWriteBuilder = newDeltaWriteBuilder(r.table, writeOptions, 
projections)
+      val deltaWriteBuilder =
+        newDeltaWriteBuilder(r.table, writeOptions, projections, 
hasNarrowRows(wd))
       val deltaWrite = deltaWriteBuilder.build()
+      validateColumnUpdateWrite(wd, deltaWrite)
       val newQuery = DistributionAndOrderingUtils.prepareQuery(deltaWrite, 
query, r.funCatalog)
       wd.copy(write = Some(deltaWrite), query = newQuery)
   }
 
+  /**
+   * Whether a row-level write delivers narrow rows for updated, copied, and 
reinserted records.
+   * This must match the writes whose write relation RewriteUpdateTable 
narrows, even when the
+   * declared columns cover the whole table.
+   */
+  private[v2] def hasNarrowRows(write: RowLevelWrite): Boolean = {
+    write.operation.isInstanceOf[SupportsColumnUpdates] && 
write.operation.command == UPDATE

Review Comment:
   **6. Whether a write delivers narrow rows is decided twice and recorded 
nowhere in the plan.** `RewriteUpdateTable.resolveWriteAttrs` narrows the write 
relation for any `SupportsColumnUpdates` operation, while `hasNarrowRows` 
re-derives the decision later from whatever operation sits in the write 
relation at that point, plus `operation.command == UPDATE`, at five call sites 
(L115, L128, L151, `GroupBasedRowLevelOperationScanPlanning.scala:70` and 
`DataSourceV2Strategy.scala:594`). Nothing re-validates the narrowed write 
relation against the current operation either: `metadataAttrsResolved` 
re-derives its expected columns from `operation.requiredMetadataAttributes`, 
but `rowAttrsResolved` compares the narrow projection with the narrow 
`table.output`.
   
   If the two decisions disagree, for example after a post-hoc rule swaps in a 
rebuilt operation (Iceberg's `ResolveBranch` does this after 
`RewriteUpdateTable` and keeps the narrow output and projections, see item 9), 
this rule reports the narrow row schema as `schema()` with no 
`columnUpdateSchema()`, and `ReplaceDataExec` calls `write()`. A copy-on-write 
connector that follows the documented contract then rewrites groups with only 
the declared columns, and nothing fails at analysis or execution.
   
   Could you fail fast here, e.g. with an internal error when 
`!hasNarrowRows(w) && w.table.output != w.originalTable.output`? Every write 
Spark builds without narrowing keeps `table.output == originalTable.output`, so 
existing plans are not affected.



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