Peter Toth created SPARK-60232:
----------------------------------

             Summary: Fix wrong results when a storage-partitioned join is 
planned over another storage-partitioned join
                 Key: SPARK-60232
                 URL: https://issues.apache.org/jira/browse/SPARK-60232
             Project: Spark
          Issue Type: Bug
          Components: SQL
    Affects Versions: 4.1.3, 4.0.4
            Reporter: Peter Toth


On 4.0 and 4.1, a storage-partitioned join planned over another one can return 
wrong results or fail. The outer join pushes its SPJ parameters into the scans 
below, and it overwrites what the inner join pushed there instead of combining 
with it. 4.2 and later regroup above the inner join with 
{{GroupPartitionsExec}} (SPARK-55535, SPARK-55092), so they are not affected.

Three shapes, measured on branch-4.0 and branch-4.1, with AQE on and off:

1. Join key positions, with 
{{spark.sql.sources.v2.bucketing.allowJoinKeysSubsetOfPartitionKeys.enabled=true}}
 and {{spark.sql.requireAllClusterKeysForCoPartition=false}}:
{code:sql}
-- all tables: (store_id INT, dept_id INT, data STRING)
-- t4 PARTITIONED BY (store_id, dept_id): (1, 2, 'a'), (2, 3, 'b'), (3, 1, 'c')
-- t5, t6 PARTITIONED BY (dept_id): (0, 1, 'x1'), (0, 2, 'x2'), (0, 3, 'x3')
SELECT /*+ MERGE(t4, t5, t6) */ t4.store_id, t4.dept_id, t4.data, t5.data, 
t6.data
FROM t4 JOIN t5 ON t4.dept_id = t5.dept_id
JOIN t6 ON t4.dept_id = t6.dept_id
{code}
It returns no rows instead of 3. The inner join pushes the positions {{[1]}} 
into the {{t4}} scan. The outer join computes {{[0]}} against the already 
projected {{[dept_id]}}, and {{populateCommonPartitionInfo}} writes it over 
{{[1]}}. So {{t4}} is grouped by {{store_id}}. With partition keys of different 
types, e.g. {{identity(date)}} and {{bucket(32, id)}} joined on {{id}}, the 
same shape throws {{ClassCastException}}.

2. Reducers, with 
{{spark.sql.sources.v2.bucketing.allowCompatibleTransforms.enabled=true}}:
{code:sql}
-- b4 PARTITIONED BY (bucket(4, id)): 4
-- b12 PARTITIONED BY (bucket(12, id)): 4
-- b8 PARTITIONED BY (bucket(8, id)): 1, 4
SELECT /*+ MERGE(b4, b12, b8) */ b4.id, b12.id, b8.id
FROM b4 JOIN b12 ON b4.id = b12.id
JOIN b8 ON b4.id = b8.id
{code}
It returns no rows instead of 1. The inner join reduces {{b12}} onto 4 buckets. 
The outer join then pushes {{reducers = None}} into every scan on its left 
side, so {{b12}} is no longer reduced.

3. Join key positions over common partition values, with 
{{spark.sql.sources.v2.bucketing.shuffle.enabled=true}} and 
{{allowJoinKeysSubsetOfPartitionKeys.enabled=true}}. {{items}} and {{items2}} 
are partitioned by {{(id, name)}}, and {{purchases}} is not partitioned:
{code:sql}
SELECT /*+ MERGE(i, j, p) */ i.id, i.name, i.price, p.price
FROM items i JOIN items2 j ON i.id = j.id AND i.name = j.name
JOIN purchases p ON i.id = p.item_id
{code}
Planning fails:
* with {{PartitioningCollection requires all of its partitionings have the same 
numPartitions}} when an id has two names;
* with {{ArrayIndexOutOfBoundsException}} otherwise.
The outer join pushes positions into scans that already carry the inner join's 
common partition values. Those values keep both keys, while the scan's 
expressions are cut down to {{id}}.

The same queries return the right rows on branch-4.2 and master.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to