Skip to content

Commit 689716a

Browse files
committed
get subquery pushdown working
1 parent 3ead972 commit 689716a

10 files changed

Lines changed: 2718 additions & 157 deletions

src/deparse.c

Lines changed: 482 additions & 104 deletions
Large diffs are not rendered by default.

src/fdw.c.in

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1603,16 +1603,16 @@ foreign_join_ok(PlannerInfo * root, RelOptInfo * joinrel, JoinType jointype,
16031603
List *joinclauses;
16041604

16051605
/*
1606-
* We support pushing down INNER, LEFT, RIGHT, FULL OUTER and SEMI joins.
1607-
* ANTI joins are not supported.
1606+
* We support pushing down INNER, LEFT, RIGHT, FULL OUTER, SEMI, and ANTI
1607+
* joins. SEMI → LEFT SEMI JOIN, ANTI → LEFT ANTI JOIN.
16081608
*/
16091609
if (jointype != JOIN_INNER && jointype != JOIN_LEFT &&
16101610
jointype != JOIN_RIGHT && jointype != JOIN_FULL &&
1611-
jointype != JOIN_SEMI)
1611+
jointype != JOIN_SEMI && jointype != JOIN_ANTI)
16121612
return false;
16131613

1614-
/* Semi-join target can only reference the outer relation */
1615-
if (jointype == JOIN_SEMI &&
1614+
/* Semi/anti-join target can only reference the outer relation */
1615+
if ((jointype == JOIN_SEMI || jointype == JOIN_ANTI) &&
16161616
!semijoin_target_ok(root, joinrel, outerrel, innerrel))
16171617
return false;
16181618

@@ -1785,18 +1785,40 @@ foreign_join_ok(PlannerInfo * root, RelOptInfo * joinrel, JoinType jointype,
17851785
break;
17861786

17871787
case JOIN_SEMI:
1788+
case JOIN_ANTI:
17881789

17891790
/*
1790-
* For semi-join, inner's conditions go to joinclauses (ON),
1791+
* For semi/anti-join, inner's conditions go to joinclauses (ON),
17911792
* outer's conditions go to remote_conds (WHERE). Extract join key
1792-
* equalities to joinclauses for the ON clause.
1793+
* equalities to joinclauses for ON clause.
17931794
*/
17941795
fpinfo->joinclauses = list_concat(fpinfo->joinclauses,
17951796
list_copy(fpinfo_i->remote_conds));
17961797
fpinfo->remote_conds = list_concat(fpinfo->remote_conds,
17971798
list_copy(fpinfo_o->remote_conds));
17981799
fpinfo->remote_conds = extract_join_equals(fpinfo->remote_conds,
17991800
&fpinfo->joinclauses);
1801+
1802+
/*
1803+
* ClickHouse doesn't support inline nested joins like A JOIN B
1804+
* JOIN C ON ... ON ... When inner/outer is itself a join, wrap as
1805+
* subquery so it becomes (SELECT ... FROM B JOIN C ON ...) AS
1806+
* sub.
1807+
*/
1808+
if (IS_JOIN_REL(outerrel))
1809+
{
1810+
fpinfo->make_outerrel_subquery = true;
1811+
fpinfo->lower_subquery_rels =
1812+
bms_add_members(fpinfo->lower_subquery_rels,
1813+
outerrel->relids);
1814+
}
1815+
if (IS_JOIN_REL(innerrel))
1816+
{
1817+
fpinfo->make_innerrel_subquery = true;
1818+
fpinfo->lower_subquery_rels =
1819+
bms_add_members(fpinfo->lower_subquery_rels,
1820+
innerrel->relids);
1821+
}
18001822
break;
18011823

18021824
case JOIN_FULL:
@@ -1838,7 +1860,8 @@ foreign_join_ok(PlannerInfo * root, RelOptInfo * joinrel, JoinType jointype,
18381860
* XXX Change to use ClickHouse EXISTS in this case?
18391861
* https://clickhouse.com/docs/sql-reference/operators/exists
18401862
*/
1841-
if (jointype == JOIN_SEMI && fpinfo->joinclauses == NIL)
1863+
if ((jointype == JOIN_SEMI || jointype == JOIN_ANTI) &&
1864+
fpinfo->joinclauses == NIL)
18421865
return false;
18431866

18441867
/* Mark that this join can be pushed down safely */

test/expected/result_map.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,9 @@ subquery_pushdown.sql
180180
Postgres | File
181181
----------|-------------------------
182182
18 | subquery_pushdown.out
183-
13-17 | subquery_pushdown_1.out
183+
17 | subquery_pushdown_1.out
184+
14-16 | subquery_pushdown_2.out
185+
13 | subquery_pushdown_3.out
184186

185187
ClickHouse | File
186188
------------|-----------------------

test/expected/subquery_eq.out

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -239,28 +239,13 @@ order by
239239
s_name,
240240
p_partkey
241241
LIMIT 100;
242-
QUERY PLAN
243-
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
244-
Limit
242+
QUERY PLAN
243+
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
244+
Foreign Scan
245245
Output: supplier.s_acctbal, supplier.s_name, nation.n_name, part.p_partkey, part.p_mfgr, supplier.s_address, supplier.s_phone, supplier.s_comment
246-
-> Nested Loop
247-
Output: supplier.s_acctbal, supplier.s_name, nation.n_name, part.p_partkey, part.p_mfgr, supplier.s_address, supplier.s_phone, supplier.s_comment
248-
Join Filter: ((part.p_partkey = partsupp.ps_partkey) AND ((SubPlan 1) = partsupp.ps_supplycost))
249-
-> Foreign Scan
250-
Output: supplier.s_acctbal, supplier.s_name, supplier.s_address, supplier.s_phone, supplier.s_comment, partsupp.ps_partkey, partsupp.ps_supplycost, nation.n_name
251-
Relations: (((supplier) INNER JOIN (partsupp)) INNER JOIN (nation)) INNER JOIN (region)
252-
Remote SQL: SELECT r2.s_acctbal, r2.s_name, r2.s_address, r2.s_phone, r2.s_comment, r3.ps_partkey, r3.ps_supplycost, r4.n_name FROM sub_eq_test.supplier r2 ALL INNER JOIN sub_eq_test.partsupp r3 ON (((r2.s_suppkey = r3.ps_suppkey))) ALL INNER JOIN sub_eq_test.nation r4 ON (((r2.s_nationkey = r4.n_nationkey))) ALL INNER JOIN sub_eq_test.region r5 ON (((r4.n_regionkey = r5.r_regionkey))) WHERE ((r5.r_name = 'EUROPE')) ORDER BY r2.s_acctbal DESC NULLS FIRST, r4.n_name ASC NULLS LAST, r2.s_name ASC NULLS LAST, r3.ps_partkey ASC NULLS LAST
253-
-> Materialize
254-
Output: part.p_partkey, part.p_mfgr
255-
-> Foreign Scan on sub_eq_test.part
256-
Output: part.p_partkey, part.p_mfgr
257-
Remote SQL: SELECT p_partkey, p_mfgr FROM sub_eq_test.part WHERE ((p_type LIKE '%BRASS')) AND ((p_size = 15))
258-
SubPlan 1
259-
-> Foreign Scan
260-
Output: (min(partsupp_1.ps_supplycost))
261-
Relations: Aggregate on ((((partsupp) INNER JOIN (supplier)) INNER JOIN (nation)) INNER JOIN (region))
262-
Remote SQL: SELECT min(r1.ps_supplycost) FROM sub_eq_test.partsupp r1 ALL INNER JOIN sub_eq_test.supplier r2 ON (((r1.ps_suppkey = r2.s_suppkey))) ALL INNER JOIN sub_eq_test.nation r3 ON (((r2.s_nationkey = r3.n_nationkey))) ALL INNER JOIN sub_eq_test.region r4 ON (((r3.n_regionkey = r4.r_regionkey))) WHERE ((r4.r_name = 'EUROPE')) AND (({p1:Int32} = r1.ps_partkey))
263-
(19 rows)
246+
Relations: ((((part) INNER JOIN (partsupp)) INNER JOIN (supplier)) INNER JOIN (nation)) INNER JOIN (region)
247+
Remote SQL: SELECT r2.s_acctbal, r2.s_name, r4.n_name, r1.p_partkey, r1.p_mfgr, r2.s_address, r2.s_phone, r2.s_comment FROM sub_eq_test.part r1 ALL INNER JOIN sub_eq_test.partsupp r3 ON (((r1.p_partkey = r3.ps_partkey))) ALL INNER JOIN sub_eq_test.supplier r2 ON (((r2.s_suppkey = r3.ps_suppkey))) ALL INNER JOIN sub_eq_test.nation r4 ON (((r2.s_nationkey = r4.n_nationkey))) ALL INNER JOIN sub_eq_test.region r5 ON (((r4.n_regionkey = r5.r_regionkey))) WHERE ((r5.r_name = 'EUROPE')) AND (((SELECT min(r1.ps_supplycost) FROM sub_eq_test.partsupp r1, sub_eq_test.supplier r2, sub_eq_test.nation r3, sub_eq_test.region r4 WHERE ((r1.p_partkey = r1.ps_partkey)) AND ((r2.s_suppkey = r1.ps_suppkey)) AND ((r2.s_nationkey = r3.n_nationkey)) AND ((r3.n_regionkey = r4.r_regionkey)) AND ((r4.r_name = 'EUROPE'))) = r3.ps_supplycost)) AND ((r1.p_type LIKE '%BRASS')) AND ((r1.p_size = 15)) ORDER BY r2.s_acctbal DESC NULLS FIRST, r4.n_name ASC NULLS LAST, r2.s_name ASC NULLS LAST, r1.p_partkey ASC NULLS LAST LIMIT 100
248+
(4 rows)
264249

265250
-- Cleanup
266251
SELECT clickhouse_raw_query('DROP DATABASE sub_eq_test');

test/expected/subquery_eq_1.out

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -239,28 +239,13 @@ order by
239239
s_name,
240240
p_partkey
241241
LIMIT 100;
242-
QUERY PLAN
243-
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
244-
Limit
242+
QUERY PLAN
243+
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
244+
Foreign Scan
245245
Output: supplier.s_acctbal, supplier.s_name, nation.n_name, part.p_partkey, part.p_mfgr, supplier.s_address, supplier.s_phone, supplier.s_comment
246-
-> Nested Loop
247-
Output: supplier.s_acctbal, supplier.s_name, nation.n_name, part.p_partkey, part.p_mfgr, supplier.s_address, supplier.s_phone, supplier.s_comment
248-
Join Filter: ((partsupp.ps_partkey = part.p_partkey) AND (partsupp.ps_supplycost = (SubPlan 1)))
249-
-> Foreign Scan
250-
Output: supplier.s_acctbal, supplier.s_name, supplier.s_address, supplier.s_phone, supplier.s_comment, partsupp.ps_partkey, partsupp.ps_supplycost, nation.n_name
251-
Relations: (((supplier) INNER JOIN (partsupp)) INNER JOIN (nation)) INNER JOIN (region)
252-
Remote SQL: SELECT r2.s_acctbal, r2.s_name, r2.s_address, r2.s_phone, r2.s_comment, r3.ps_partkey, r3.ps_supplycost, r4.n_name FROM sub_eq_test.supplier r2 ALL INNER JOIN sub_eq_test.partsupp r3 ON (((r2.s_suppkey = r3.ps_suppkey))) ALL INNER JOIN sub_eq_test.nation r4 ON (((r2.s_nationkey = r4.n_nationkey))) ALL INNER JOIN sub_eq_test.region r5 ON (((r4.n_regionkey = r5.r_regionkey))) WHERE ((r5.r_name = 'EUROPE')) ORDER BY r2.s_acctbal DESC NULLS FIRST, r4.n_name ASC NULLS LAST, r2.s_name ASC NULLS LAST, r3.ps_partkey ASC NULLS LAST
253-
-> Materialize
254-
Output: part.p_partkey, part.p_mfgr
255-
-> Foreign Scan on sub_eq_test.part
256-
Output: part.p_partkey, part.p_mfgr
257-
Remote SQL: SELECT p_partkey, p_mfgr FROM sub_eq_test.part WHERE ((p_type LIKE '%BRASS')) AND ((p_size = 15))
258-
SubPlan 1
259-
-> Foreign Scan
260-
Output: (min(partsupp_1.ps_supplycost))
261-
Relations: Aggregate on ((((partsupp) INNER JOIN (supplier)) INNER JOIN (nation)) INNER JOIN (region))
262-
Remote SQL: SELECT min(r1.ps_supplycost) FROM sub_eq_test.partsupp r1 ALL INNER JOIN sub_eq_test.supplier r2 ON (((r1.ps_suppkey = r2.s_suppkey))) ALL INNER JOIN sub_eq_test.nation r3 ON (((r2.s_nationkey = r3.n_nationkey))) ALL INNER JOIN sub_eq_test.region r4 ON (((r3.n_regionkey = r4.r_regionkey))) WHERE ((r4.r_name = 'EUROPE')) AND (({p1:Int32} = r1.ps_partkey))
263-
(19 rows)
246+
Relations: ((((part) INNER JOIN (partsupp)) INNER JOIN (supplier)) INNER JOIN (nation)) INNER JOIN (region)
247+
Remote SQL: SELECT r2.s_acctbal, r2.s_name, r4.n_name, r1.p_partkey, r1.p_mfgr, r2.s_address, r2.s_phone, r2.s_comment FROM sub_eq_test.part r1 ALL INNER JOIN sub_eq_test.partsupp r3 ON (((r1.p_partkey = r3.ps_partkey))) ALL INNER JOIN sub_eq_test.supplier r2 ON (((r3.ps_suppkey = r2.s_suppkey))) ALL INNER JOIN sub_eq_test.nation r4 ON (((r2.s_nationkey = r4.n_nationkey))) ALL INNER JOIN sub_eq_test.region r5 ON (((r4.n_regionkey = r5.r_regionkey))) WHERE ((r5.r_name = 'EUROPE')) AND (((SELECT min(r1.ps_supplycost) FROM sub_eq_test.partsupp r1, sub_eq_test.supplier r2, sub_eq_test.nation r3, sub_eq_test.region r4 WHERE ((r1.p_partkey = r1.ps_partkey)) AND ((r2.s_suppkey = r1.ps_suppkey)) AND ((r2.s_nationkey = r3.n_nationkey)) AND ((r3.n_regionkey = r4.r_regionkey)) AND ((r4.r_name = 'EUROPE'))) = r3.ps_supplycost)) AND ((r1.p_type LIKE '%BRASS')) AND ((r1.p_size = 15)) ORDER BY r2.s_acctbal DESC NULLS FIRST, r4.n_name ASC NULLS LAST, r2.s_name ASC NULLS LAST, r1.p_partkey ASC NULLS LAST LIMIT 100
248+
(4 rows)
264249

265250
-- Cleanup
266251
SELECT clickhouse_raw_query('DROP DATABASE sub_eq_test');

0 commit comments

Comments
 (0)