huan233usc commented on code in PR #18383:
URL: https://github.com/apache/iceberg/pull/18383#discussion_r4275757814


##########
spark/v4.2/spark/src/test/java/org/apache/iceberg/spark/sql/TestStoragePartitionedJoins.java:
##########
@@ -724,6 +725,68 @@ public void testAggregates() throws NoSuchTableException {
         tableName(OTHER_TABLE_NAME));
   }
 
+  @TestTemplate
+  public void testAggregateOverPartiallyClusteredJoin() throws 
NoSuchTableException {
+    // SPARK-55848: an aggregate placed directly on a partially-clustered 
storage-partitioned join
+    // output must return the same rows with SPJ enabled as without it. 
Partial clustering splits
+    // the skewed side's partitions, and before SPARK-55848 a DISTINCT / GROUP 
BY on that output
+    // trusted the split partitioning and over-counted by the split factor 
(1200 rows instead of
+    // 200). The existing testAggregates covers an aggregate over a single 
scan, not over a
+    // partially-clustered join, so this pins the join case. It fails on Spark 
4.0.2 and passes on
+    // 4.0.3, the SPARK-55848 fix version, through the Iceberg Spark runtime.
+    String createTableStmt =
+        "CREATE TABLE %s (id BIGINT, salary INT)"
+            + "USING iceberg "
+            + "PARTITIONED BY (bucket(8, id))"
+            + "TBLPROPERTIES (%s)";
+
+    sql(createTableStmt, tableName, tablePropsAsString(TABLE_PROPERTIES));
+    configurePlanningMode(tableName, planningMode);
+    sql(createTableStmt, tableName(OTHER_TABLE_NAME), 
tablePropsAsString(TABLE_PROPERTIES));
+    configurePlanningMode(tableName(OTHER_TABLE_NAME), planningMode);
+
+    // Skew the probe side: append the same rows six times so each bucket of 
the first table holds
+    // six files. With split-per-file (16 MB split size and open-file cost) 
and partial clustering
+    // enabled, that side's partitions split while the single-file side is 
replicated to match.
+    Table table = validationCatalog.loadTable(tableIdent);
+    Dataset<Row> dataDF = randomDataDF(table.schema(), 200);
+    for (int i = 0; i < 6; i++) {
+      append(tableName, dataDF);
+    }
+    append(tableName(OTHER_TABLE_NAME), dataDF);
+
+    Map<String, String> partiallyClusteredSpjConf = 
Maps.newHashMap(ENABLED_SPJ_SQL_CONF);
+    partiallyClusteredSpjConf.put(
+        SQLConf.V2_BUCKETING_PARTIALLY_CLUSTERED_DISTRIBUTION_ENABLED().key(), 
"true");
+
+    String distinctAfterJoin =
+        "SELECT count(*) AS c FROM ("
+            + "SELECT DISTINCT t1.id FROM %s t1 INNER JOIN %s t2 ON t1.id = 
t2.id)";
+    String aggAfterJoin =
+        "SELECT count(*) AS c FROM ("
+            + "SELECT t1.id, count(*) AS cnt FROM %s t1 INNER JOIN %s t2 ON 
t1.id = t2.id "
+            + "GROUP BY t1.id)";
+
+    for (String query : new String[] {distinctAfterJoin, aggAfterJoin}) {
+      AtomicReference<List<Object[]>> rowsWithSPJ = new AtomicReference<>();
+      AtomicReference<List<Object[]>> rowsWithoutSPJ = new AtomicReference<>();
+
+      withSQLConf(
+          partiallyClusteredSpjConf,
+          () -> rowsWithSPJ.set(sql(query, tableName, 
tableName(OTHER_TABLE_NAME))));

Review Comment:
   Could we also assert that the SPJ-enabled plan actually uses a partially 
clustered join? A fallback shuffle join, or SPJ without partial clustering, 
could still match the baseline and let this regression test pass. A plan check 
similar to `assertPartitioningAwarePlan`, scoped to the join so it allows the 
aggregate’s required exchange, would verify that the intended path is exercised.



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