@@ -22,6 +22,13 @@ import org.apache.paimon.Snapshot.CommitKind
2222import org .apache .paimon .spark .PaimonSparkTestBase
2323
2424import org .apache .spark .sql .Row
25+ import org .apache .spark .sql .catalyst .plans .logical .{Join , LogicalPlan }
26+ import org .apache .spark .sql .execution .{QueryExecution , SparkPlan }
27+ import org .apache .spark .sql .execution .adaptive .AdaptiveSparkPlanExec
28+ import org .apache .spark .sql .execution .joins .BaseJoinExec
29+ import org .apache .spark .sql .util .QueryExecutionListener
30+
31+ import scala .collection .mutable
2532
2633abstract class RowTrackingTestBase extends PaimonSparkTestBase {
2734
@@ -360,6 +367,55 @@ abstract class RowTrackingTestBase extends PaimonSparkTestBase {
360367 }
361368 }
362369
370+ test(" Data Evolution: merge into table with data-evolution with _ROW_ID shortcut" ) {
371+ withTable(" source" , " target" ) {
372+ sql(" CREATE TABLE source (target_ROW_ID BIGINT, b INT, c STRING)" )
373+ sql(
374+ " INSERT INTO source VALUES (0, 100, 'c11'), (2, 300, 'c33'), (4, 500, 'c55'), (6, 700, 'c77'), (8, 900, 'c99')" )
375+
376+ sql(
377+ " CREATE TABLE target (a INT, b INT, c STRING) TBLPROPERTIES ('row-tracking.enabled' = 'true', 'data-evolution.enabled' = 'true')" )
378+ sql(
379+ " INSERT INTO target values (1, 10, 'c1'), (2, 20, 'c2'), (3, 30, 'c3'), (4, 40, 'c4'), (5, 50, 'c5')" )
380+
381+ val capturedPlans : mutable.ListBuffer [LogicalPlan ] = mutable.ListBuffer .empty
382+ val listener = new QueryExecutionListener {
383+ override def onSuccess (funcName : String , qe : QueryExecution , durationNs : Long ): Unit = {
384+ capturedPlans += qe.analyzed
385+ }
386+ override def onFailure (funcName : String , qe : QueryExecution , exception : Exception ): Unit = {
387+ capturedPlans += qe.analyzed
388+ }
389+ }
390+ spark.listenerManager.register(listener)
391+ sql(s """
392+ |MERGE INTO target
393+ |USING source
394+ |ON target._ROW_ID = source.target_ROW_ID
395+ |WHEN MATCHED AND target.a = 5 THEN UPDATE SET b = source.b + target.b
396+ |WHEN MATCHED AND source.c > 'c2' THEN UPDATE SET b = source.b, c = source.c
397+ |WHEN NOT MATCHED AND c > 'c9' THEN INSERT (a, b, c) VALUES (target_ROW_ID, b * 1.1, c)
398+ |WHEN NOT MATCHED THEN INSERT (a, b, c) VALUES (target_ROW_ID, b, c)
399+ | """ .stripMargin)
400+ // Assert that no Join operator was used during
401+ // `org.apache.paimon.spark.commands.MergeIntoPaimonDataEvolutionTable.targetRelatedSplits`
402+ assert(capturedPlans.head.collect { case plan : Join => plan }.isEmpty)
403+ spark.listenerManager.unregister(listener)
404+
405+ checkAnswer(
406+ sql(" SELECT *, _ROW_ID, _SEQUENCE_NUMBER FROM target ORDER BY a" ),
407+ Seq (
408+ Row (1 , 10 , " c1" , 0 , 2 ),
409+ Row (2 , 20 , " c2" , 1 , 2 ),
410+ Row (3 , 300 , " c33" , 2 , 2 ),
411+ Row (4 , 40 , " c4" , 3 , 2 ),
412+ Row (5 , 550 , " c5" , 4 , 2 ),
413+ Row (6 , 700 , " c77" , 5 , 2 ),
414+ Row (8 , 990 , " c99" , 6 , 2 ))
415+ )
416+ }
417+ }
418+
363419 test(" Data Evolution: update table throws exception" ) {
364420 withTable(" t" ) {
365421 sql(
0 commit comments