@@ -2860,12 +2860,13 @@ class CometIcebergNativeSuite extends CometTestBase with RESTCatalogHelper {
28602860 // Additional integration tests for non-identity transform residuals
28612861 // =========================================================================
28622862
2863- // Test A: Non-identity transform with delete files - verify correctness.
2864- // When delete files and non-identity transforms coexist, query results must
2865- // be correct regardless of whether the native scan or Spark handles it.
2863+ // Test A: Non-identity transform with delete files must fall back to Spark.
2864+ // When the table has a non-identity partition transform (truncate) AND MOR delete
2865+ // files are present, the native Iceberg scan must fall back to Spark to ensure
2866+ // correct delete processing. Detection uses PartitionSpec inspection.
28662867 // Uses truncate(3, name) so the query and deleted row share the same partition
28672868 // (truncate(3, 'alpha') = truncate(3, 'alpine') = 'alp').
2868- test(" non-identity transform residual - correct results with delete files present" ) {
2869+ test(" non-identity transform residual - falls back with delete files present" ) {
28692870 assume(icebergAvailable, " Iceberg not available in classpath" )
28702871
28712872 withTempIcebergDir { warehouseDir =>
@@ -2878,7 +2879,7 @@ class CometIcebergNativeSuite extends CometTestBase with RESTCatalogHelper {
28782879 CometConf .COMET_ICEBERG_NATIVE_ENABLED .key -> " true" ) {
28792880
28802881 spark.sql("""
2881- CREATE TABLE test_cat.db.truncate_delete_test (
2882+ CREATE TABLE test_cat.db.truncate_delete_fallback (
28822883 id INT,
28832884 name STRING,
28842885 value DOUBLE
@@ -2892,38 +2893,43 @@ class CometIcebergNativeSuite extends CometTestBase with RESTCatalogHelper {
28922893 """ )
28932894
28942895 spark.sql("""
2895- INSERT INTO test_cat.db.truncate_delete_test VALUES
2896+ INSERT INTO test_cat.db.truncate_delete_fallback VALUES
28962897 (1, 'alpha', 10.0), (2, 'alpine', 20.0), (3, 'bravo', 30.0),
28972898 (4, 'bridge', 40.0), (5, 'charlie', 50.0), (6, 'cherry', 60.0)
28982899 """ )
28992900
29002901 // Delete 'alpine' which shares truncate(3)='alp' partition with 'alpha'.
2901- spark.sql(" DELETE FROM test_cat.db.truncate_delete_test WHERE name = 'alpine'" )
2902+ spark.sql(" DELETE FROM test_cat.db.truncate_delete_fallback WHERE name = 'alpine'" )
29022903
2903- // Query for 'alpha' creates a residual on the truncate transform.
2904- // The deleted row 'alpine' must not appear, and 'alpha' must be returned .
2904+ // Query with filter. Because the table has truncate transform AND delete files,
2905+ // native scan must fall back to Spark .
29052906 val query =
2906- " SELECT * FROM test_cat.db.truncate_delete_test WHERE name = 'alpha' ORDER BY id"
2907+ " SELECT * FROM test_cat.db.truncate_delete_fallback WHERE name = 'alpha' ORDER BY id"
29072908 val (_, cometPlan) = checkSparkAnswer(query)
29082909
2910+ // Assert fallback: no CometIcebergNativeScanExec in plan
2911+ val icebergScans = collectIcebergNativeScans(cometPlan)
2912+ assert(
2913+ icebergScans.isEmpty,
2914+ " Expected fallback to Spark (no CometIcebergNativeScanExec) when " +
2915+ s " non-identity transform has delete files. Plan: \n $cometPlan" )
2916+
29092917 // Verify correct results: only 'alpha' returned, 'alpine' is deleted
29102918 val result = spark.sql(query).collect()
29112919 assert(result.length == 1 , s " Expected 1 row, got ${result.length}" )
29122920 assert(result(0 ).getInt(0 ) == 1 , s " Expected id=1, got ${result(0 ).getInt(0 )}" )
29132921 assert(result(0 ).getString(1 ) == " alpha" )
29142922
29152923 // Verify 'alpine' is truly gone from broader query
2916- val allAlpResult = spark
2917- .sql(" SELECT * FROM test_cat.db.truncate_delete_test ORDER BY id" )
2924+ val allResult = spark
2925+ .sql(" SELECT * FROM test_cat.db.truncate_delete_fallback ORDER BY id" )
29182926 .collect()
2927+ assert(allResult.length == 5 , s " Expected 5 rows after delete, got ${allResult.length}" )
29192928 assert(
2920- allAlpResult.length == 5 ,
2921- s " Expected 5 rows after delete, got ${allAlpResult.length}" )
2922- assert(
2923- ! allAlpResult.exists(_.getString(1 ) == " alpine" ),
2929+ ! allResult.exists(_.getString(1 ) == " alpine" ),
29242930 " Deleted row 'alpine' should not appear in results" )
29252931
2926- spark.sql(" DROP TABLE test_cat.db.truncate_delete_test " )
2932+ spark.sql(" DROP TABLE test_cat.db.truncate_delete_fallback " )
29272933 }
29282934 }
29292935 }
0 commit comments