szehon-ho commented on code in PR #57722:
URL: https://github.com/apache/spark/pull/57722#discussion_r3717384097


##########
sql/pipelines/src/main/scala/org/apache/spark/sql/pipelines/graph/DataflowGraph.scala:
##########
@@ -170,15 +171,27 @@ case class DataflowGraph(
   /**
    * A map of the inferred schema of each table, computed by merging the 
analyzed schemas
    * of all flows writing to that table.
+   *
+   * The merge honors the session's `spark.sql.caseSensitive`: under 
case-insensitive analysis two
+   * flows emitting column names that differ only in case contribute a single 
column rather than
+   * both, which would otherwise produce a target schema the engine's own 
resolver cannot
+   * disambiguate. Which of the two spellings survives follows the order the 
flows are merged in,
+   * which this map does not define, so callers should not depend on a 
particular casing.

Review Comment:
   This gives up a guarantee the code still provides, and there is now a 
consumer that depends on it.
   
   `flowsTo` is `flows.groupBy(_.destinationIdentifier)`, which preserves 
encounter order within each group, and the fold below is a left `reduce` -- so 
the first flow's spelling does win deterministically, which is what this 
sentence said before this commit.
   
   The consumer: for a table with no declared schema `outputSchema` is this 
map's value (`DatasetManager.scala:315`), and on the non-merging paths 
(materialized views and any full refresh) it reaches `diffSchemas` unchanged, 
where column identity is keyed on the exact field name. So if the surviving 
spelling ever changes -- reordering flow definitions in source is enough -- the 
diff emits `deleteColumn` plus `addColumn` for a column that only changed case. 
Those paths replace the data anyway, so this is schema churn rather than data 
loss, but it is churn driven by something this doc declares undefined.
   
   Suggest restoring the "the first flow's spelling wins" wording, and putting 
the inference assertion back to `toSeq` from `toSet`, rather than documenting 
the casing as arbitrary while a consumer keys on it.
   
   Separately, line 176 above still says the merge honors "the session's 
`spark.sql.caseSensitive`" -- that is the part this commit changed, since it 
now honors `effectiveCaseSensitivity` with the session only as a fallback.



##########
sql/pipelines/src/main/scala/org/apache/spark/sql/pipelines/graph/DatasetManager.scala:
##########
@@ -626,20 +656,36 @@ object DatasetManager extends Logging {
    * @param mergeWithExistingSchema whether the effective schema is the merge 
of the existing and
    *                                desired schemas (additive evolution) 
rather than the desired
    *                                schema as-is.
+   * @param caseSensitive           whether the additive schema merge treats 
field names differing
+   *                                only in case as distinct columns. Threaded 
from the session's

Review Comment:
   Stale after this commit: line 405 now passes 
`effectiveCaseSensitivityFor(...)`, which reads the flows' value and falls back 
to the session only when no flow declares one, so this is no longer "threaded 
from the session's `spark.sql.caseSensitive`". The `effectiveCaseSensitivity` 
scaladoc already describes the new behavior accurately.



##########
sql/pipelines/src/main/scala/org/apache/spark/sql/pipelines/util/SchemaInferenceUtils.scala:
##########
@@ -110,6 +184,19 @@ object SchemaInferenceUtils {
    * 1. New columns that need to be added
    * 2. Existing columns that need type updates
    *
+   * Column identity is keyed on the exact field name, deliberately NOT on a 
case-normalized one.

Review Comment:
   The "since both were considered and rejected" framing here, and the `NOTE: 
caseSensitive deliberately does not reach diffSchemas` in `evolveTable`, are 
review context rather than invariants the next reader needs -- `AGENTS.md` asks 
for the latter specifically. The substance is worth keeping: identity is keyed 
on exact names, and case folding belongs to the merge. The record of what was 
rejected isn't.



##########
sql/pipelines/src/main/scala/org/apache/spark/sql/pipelines/graph/DataflowGraph.scala:
##########
@@ -170,15 +171,27 @@ case class DataflowGraph(
   /**
    * A map of the inferred schema of each table, computed by merging the 
analyzed schemas
    * of all flows writing to that table.
+   *
+   * The merge honors the session's `spark.sql.caseSensitive`: under 
case-insensitive analysis two
+   * flows emitting column names that differ only in case contribute a single 
column rather than
+   * both, which would otherwise produce a target schema the engine's own 
resolver cannot
+   * disambiguate. Which of the two spellings survives follows the order the 
flows are merged in,
+   * which this map does not define, so callers should not depend on a 
particular casing.
    */
   lazy val inferredSchema: Map[TableIdentifier, StructType] = {
-    flowsTo.view.mapValues { flows =>
-      flows
+    val sessionCaseSensitive = 
SparkSession.active.sessionState.conf.caseSensitiveAnalysis

Review Comment:
   This reads `SparkSession.active`, while the other caller of 
`effectiveCaseSensitivity` reads `context.spark.sessionState.conf` 
(`DatasetManager.scala:598`) -- the same fallback, for the same table, from two 
different sources. `PipelineUpdateContextImpl.spark` is captured from 
`SparkSession.getActiveSession` at construction so they normally agree, but 
nothing enforces it, and a divergence would have inference and evolution fold 
case differently for one table. Deriving both from a single source removes the 
possibility entirely.
   
   The `caseSensitive` default on `inferSchemaFromFlows` is still 
`SparkSession.active` as well.



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