anew commented on code in PR #57669: URL: https://github.com/apache/spark/pull/57669#discussion_r3688555331
########## sql/pipelines/src/test/scala/org/apache/spark/sql/pipelines/graph/AutoCdcScd2ColumnEvolutionSuite.scala: ########## @@ -0,0 +1,241 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.spark.sql.pipelines.graph + +import org.apache.spark.sql.Row +import org.apache.spark.sql.execution.streaming.runtime.MemoryStream +import org.apache.spark.sql.functions +import org.apache.spark.sql.pipelines.autocdc.{ColumnSelection, ScdType, UnqualifiedColumnName} +import org.apache.spark.sql.pipelines.utils.{ExecutionTest, TestGraphRegistrationContext} +import org.apache.spark.sql.test.SharedSparkSession + +/** + * End-to-end tests for SCD Type 2 AutoCDC column-schema evolution across runs: a microbatch that + * is narrower than the already-evolved target (a source column dropped, a nested struct/array field + * dropped, or the `COLUMNS` selection narrowed) must reconcile correctly instead of failing the + * internal union. + * + * These exercise the fix for SPARK-58418. Before it, `Scd2ForeachBatchHandler.reconcileMicrobatch` + * unioned the microbatch with the affected target/aux rows without `allowMissingColumns`, so a + * narrower microbatch failed with NUM_COLUMNS_MISMATCH (top-level) or INCOMPATIBLE_COLUMN_TYPE + * (nested). The contract asserted here is additive-tolerant, matching SCD1: records already written + * keep their values for the no-longer-emitted column, and only records opened by the narrower + * microbatch carry null for it. + * + * Changing the effective *tracked-history* column set is a distinct, separately-scoped concern + * (SPARK-58452) and is not exercised here; every flow below uses default tracking with an unchanged + * effective tracked set. + */ +class AutoCdcScd2ColumnEvolutionSuite + extends ExecutionTest + with SharedSparkSession + with AutoCdcGraphExecutionTestMixin { + + /** The SCD2 target's `_cdc_metadata` struct value for a given recordStartAt. */ + private def scd2Meta(recordStartAt: Long): Row = Row(recordStartAt) + + test("a source column dropped between runs is preserved on existing records and null on new " + Review Comment: all tests here use the default tracking set == all selected input columns. But after SPARK-58391, any change to the input selection will change the tracked set, and require a full refresh. In the tests here, you should test the behavior when a non-tracking column is removed from the column selection. That is, you should configure your auto CDC wth an explicit column selection that does not include the column that will be removed in the second run. ########## sql/pipelines/src/main/scala/org/apache/spark/sql/pipelines/autocdc/Scd2ForeachBatchHandler.scala: ########## @@ -87,11 +87,19 @@ case class Scd2ForeachBatchHandler( perKeyMinimumSequenceInMicrobatchDf = perKeyMinimumSequenceInMicrobatchDf ) - // All three share the canonical schema; findAffectedRowsFromAuxiliaryTable drops the aux-only - // deletedByBatchId column. + // The three inputs share the canonical SCD2 row schema by name, but not necessarily by column + // set: after cross-run schema evolution the target (and the aux table, which mirrors it) can + // carry user columns that the current microbatch no longer emits -- a dropped source column, a + // narrowed COLUMNS selection, or a dropped nested struct/array field. `allowMissingColumns` + // pads such columns with null on the side that lacks them (recursing into structs and arrays; + // map types are not supported) instead of failing the union with NUM_COLUMNS_MISMATCH / + // INCOMPATIBLE_COLUMN_TYPE. Records already written to the target keep their values -- the Review Comment: This comment is very verbose. Can you trm it down? It could certainly end here. -- 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]
