anew opened a new pull request, #57722:
URL: https://github.com/apache/spark/pull/57722

   ### What changes were proposed in this pull request?
     
     Declarative Pipelines' table schema evolution in 
`DatasetManager.evolveTable` compared schemas **case-sensitively regardless of 
the session's `spark.sql.caseSensitive`**. Under the default (case-insensitive) 
session, a target column `value` and an incoming column `Value` were treated as 
distinct, so the target was evolved to carry *both* columns -- a schema that is 
internally inconsistent under the resolver the rest of the engine uses.
     
     The root cause is on two utilities on the evolution path, each taking the 
case-sensitive default and never forwarding the session conf:
     
     - `SchemaMergingUtils.mergeSchemas` called `StructType.merge(tableSchema, 
dataSchema)` positionally, leaving `StructType.merge`'s `caseSensitive: Boolean 
= true` parameter at its default.
     - `SchemaInferenceUtils.diffSchemas` built `name -> field` maps and 
computed `targetFields.keySet.diff(currentFields.keySet)` -- an exact-string 
set difference, also case-sensitive.
     
     This PR threads case-sensitivity through both:
     
     - `mergeSchemas` and `diffSchemas` gain a `caseSensitive: Boolean = true` 
parameter. The default preserves existing behavior for the schema-*inference* 
callers (`inferSchemaFromFlows`, `DataflowGraph.inferredSchema`), which 
intentionally merge case-sensitively.
     - `DatasetManager.evolveTable` forwards 
`context.spark.sessionState.conf.caseSensitiveAnalysis` at both call sites (the 
target table and the AutoCDC auxiliary table).
     - With case-insensitivity, `StructType.merge` folds the incoming `Value` 
onto the existing `value` (the left field's name/position  wins), so no 
duplicate column is added; `diffSchemas` matches on the normalized name while 
emitting the column's already-persisted name, so a case-only difference is an 
in-place no-op rather than a spurious drop-then-add, and no rename is produced.
     
     This is not AutoCDC-specific -- `SchemaMergingUtils` / 
`SchemaInferenceUtils` are general pipeline-table utilities, so the fix covers 
any SDP table that evolves its schema under a case-insensitive session.
     
     ### Why are the changes needed?
     
     It is a correctness bug. Evolving a table under the default 
case-insensitive session with a column that differs only in case from an 
existing one produced a target schema with two columns (`value` and `Value`) 
that the engine's own case-insensitive resolver cannot tell apart. The corrupt 
schema then failed downstream where that resolver runs:
     
     - AutoCDC SCD2: `COLUMN_ALREADY_EXISTS`, raised by 
`ResolveUnion.checkColumnNames` over the `unionByName` in 
`Scd2ForeachBatchHandler.reconcileMicrobatch` (the affected-rows union reads 
the now-two-column target back).
     - AutoCDC SCD1: `AMBIGUOUS_REFERENCE`, deeper in the MERGE plan (no 
reconcile union).
     
     The same user mistake thus surfaced as two different, confusing error 
conditions -- both downstream symptoms of the corrupt evolved schema rather 
than the root cause.
     
     ### Does this PR introduce _any_ user-facing change?
     
     Yes, a bug fix (only within the unreleased `master` / `branch-4.x`; the 
buggy behavior was never in a release). Under case-insensitive resolution 
(Spark's default), a pipeline that emits a column differing only in case from 
an existing target column now evolves the table as a no-op (the incoming value 
is written to the existing column) instead of corrupting the schema and failing 
with  `COLUMN_ALREADY_EXISTS` (SCD2) or `AMBIGUOUS_REFERENCE` (SCD1). Under 
`spark.sql.caseSensitive=true`, the two names remain distinct and  the new 
column is added, as before.
     
     ### How was this patch tested?
     
     Added and updated tests:
     
     - `SchemaInferenceUtilsSuite` -- new unit tests for `diffSchemas`: 
case-only difference is distinct under case-sensitive, a no-op under  
case-insensitive, and a case-insensitive type change addresses the current 
(persisted) column name.
     - `MaterializeTablesSuite` -- an end-to-end pair (non-AutoCDC path) 
asserting no `alterTable` is issued and the persisted column keeps  its 
original case under case-insensitive resolution, versus a column being added 
under case-sensitive.
     - `AutoCdcScd1SchemaEvolutionSuite` -- rewrote the case-only-difference 
test from asserting the old `AMBIGUOUS_REFERENCE` failure to asserting the 
fixed no-op-merge success; refreshed the suite header and a now-stale comment 
in `AutoCdcScd1KeyDriftSuite`.
     
     Ran the full `pipelines` module test suite. All pass except two failures 
(`TriggeredGraphExecutionSuite`: "stream failure on deletes and updates", 
"stopping a pipeline mid-execution") that I confirmed are pre-existing on clean 
`master` (they fail identically with this change stashed), so this PR 
introduces no regressions.
     
     ### Was this patch authored or co-authored using generative AI tooling?
     
     Generated-by: Claude Opus 4.8
   


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