@@ -2294,4 +2294,70 @@ class CometIcebergNativeSuite extends CometTestBase with RESTCatalogHelper {
22942294 deleteRecursively(dir)
22952295 }
22962296 }
2297+
2298+ test(" runtime filtering - join with dynamic partition pruning" ) {
2299+ assume(icebergAvailable, " Iceberg not available" )
2300+ withTempIcebergDir { warehouseDir =>
2301+ val dimDir = new File (warehouseDir, " dim_parquet" )
2302+ withSQLConf(
2303+ " spark.sql.catalog.runtime_cat" -> " org.apache.iceberg.spark.SparkCatalog" ,
2304+ " spark.sql.catalog.runtime_cat.type" -> " hadoop" ,
2305+ " spark.sql.catalog.runtime_cat.warehouse" -> warehouseDir.getAbsolutePath,
2306+ CometConf .COMET_ENABLED .key -> " true" ,
2307+ CometConf .COMET_EXEC_ENABLED .key -> " true" ,
2308+ CometConf .COMET_ICEBERG_NATIVE_ENABLED .key -> " true" ) {
2309+
2310+ // Create partitioned Iceberg table (fact table)
2311+ spark.sql("""
2312+ CREATE TABLE runtime_cat.db.fact_table (
2313+ id BIGINT,
2314+ data STRING,
2315+ date DATE
2316+ ) USING iceberg
2317+ PARTITIONED BY (date)
2318+ """ )
2319+
2320+ // Insert data across multiple partitions
2321+ spark.sql("""
2322+ INSERT INTO runtime_cat.db.fact_table VALUES
2323+ (1, 'a', DATE '1970-01-01'),
2324+ (2, 'b', DATE '1970-01-02'),
2325+ (3, 'c', DATE '1970-01-02'),
2326+ (4, 'd', DATE '1970-01-03')
2327+ """ )
2328+
2329+ // Create dimension table (Parquet) in temp directory
2330+ spark
2331+ .createDataFrame(Seq ((1L , java.sql.Date .valueOf(" 1970-01-02" ))))
2332+ .toDF(" id" , " date" )
2333+ .write
2334+ .parquet(dimDir.getAbsolutePath)
2335+ spark.read.parquet(dimDir.getAbsolutePath).createOrReplaceTempView(" dim" )
2336+
2337+ // This join should trigger dynamic partition pruning
2338+ val query =
2339+ """ SELECT f.* FROM runtime_cat.db.fact_table f
2340+ |JOIN dim d ON f.date = d.date AND d.id = 1
2341+ |ORDER BY f.id""" .stripMargin
2342+
2343+ // Verify the initial plan contains dynamic pruning expression
2344+ val df = spark.sql(query)
2345+ val initialPlan = df.queryExecution.executedPlan
2346+ val planStr = initialPlan.toString
2347+ assert(
2348+ planStr.contains(" dynamicpruning" ),
2349+ s " Expected dynamic pruning in plan but got: \n $planStr" )
2350+
2351+ // Check results match Spark
2352+ // Note: AQE re-plans after subquery executes, converting dynamicpruningexpression(...)
2353+ // to dynamicpruningexpression(true), which allows native Iceberg scan to proceed.
2354+ // This is correct behavior - no actual subquery to wait for after AQE re-planning.
2355+ // However, the rest of the still contains non-native operators because CometExecRule
2356+ // doesn't run again.
2357+ checkSparkAnswer(df)
2358+
2359+ spark.sql(" DROP TABLE runtime_cat.db.fact_table" )
2360+ }
2361+ }
2362+ }
22972363}
0 commit comments