Skip to content

Commit d5628e3

Browse files
committed
Improve slt test coverage
1 parent b9f160b commit d5628e3

1 file changed

Lines changed: 220 additions & 4 deletions

File tree

‎datafusion/sqllogictest/test_files/push_down_topk_through_join.slt‎

Lines changed: 220 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,9 @@
1717

1818
# Tests for pushing a TopK (Sort with fetch) through an outer join.
1919
#
20-
# These queries exercise the scenarios handled by the PushDownTopKThroughJoin
21-
# rule. That rule lands in a follow-up PR; the EXPLAIN plans below capture
22-
# current behavior, so the follow-up's diff shows exactly which plans change.
23-
# The query-result checks hold whether or not the rule is enabled.
20+
# These queries exercise the scenarios handled by the `push_down_limit`
21+
# rule's TopK-through-join rewrite (see
22+
# `datafusion/optimizer/src/push_down_limit/topk_through_join.rs`).
2423

2524
statement ok
2625
set datafusion.execution.target_partitions = 1;
@@ -1139,6 +1138,223 @@ logical_plan
11391138
09)----------------TableScan: t1 projection=[a]
11401139
10)--------TableScan: t2 projection=[x]
11411140

1141+
###
1142+
### Degenerate cases: LIMIT 0, sort keys with no column references
1143+
###
1144+
1145+
# LIMIT 0 is folded away to an EmptyRelation by an earlier optimizer pass
1146+
# before the TopK-through-join rewrite ever sees it.
1147+
query TT
1148+
EXPLAIN SELECT t1.a, t1.b, t2.x
1149+
FROM t1 LEFT JOIN t2 ON t1.a = t2.x
1150+
ORDER BY t1.b ASC LIMIT 0;
1151+
----
1152+
logical_plan EmptyRelation: rows=0
1153+
1154+
query III
1155+
SELECT t1.a, t1.b, t2.x
1156+
FROM t1 LEFT JOIN t2 ON t1.a = t2.x
1157+
ORDER BY t1.b ASC LIMIT 0;
1158+
----
1159+
1160+
# A sort key with no column references at all is trivially "entirely from"
1161+
# either side, so it pushes to the first candidate side (left, for LEFT
1162+
# JOIN). `1 + 1` is constant-folded to `2` (aliased back to its original
1163+
# display name) by an earlier optimizer pass.
1164+
query TT
1165+
EXPLAIN SELECT t1.a, t1.b, t2.x
1166+
FROM t1 LEFT JOIN t2 ON t1.a = t2.x
1167+
ORDER BY 1 + 1 LIMIT 3;
1168+
----
1169+
logical_plan
1170+
01)Sort: Int64(2) AS Int64(1) + Int64(1) ASC NULLS LAST, fetch=3
1171+
02)--Left Join: t1.a = t2.x
1172+
03)----Sort: Int64(2) AS Int64(1) + Int64(1) ASC NULLS LAST, fetch=3
1173+
04)------TableScan: t1 projection=[a, b]
1174+
05)----TableScan: t2 projection=[x]
1175+
1176+
query III
1177+
SELECT t1.a, t1.b, t2.x
1178+
FROM t1 LEFT JOIN t2 ON t1.a = t2.x
1179+
ORDER BY 1 + 1 LIMIT 3;
1180+
----
1181+
1 10 1
1182+
2 20 2
1183+
3 30 3
1184+
1185+
###
1186+
### JoinConstraint::Using
1187+
###
1188+
1189+
statement ok
1190+
CREATE TABLE t_using1 (a INT, b INT) AS VALUES (1, 10), (2, 20), (3, 30);
1191+
1192+
statement ok
1193+
CREATE TABLE t_using2 (a INT, y INT) AS VALUES (1, 100), (2, 200);
1194+
1195+
# A USING join wraps the join in a Projection (to coalesce the shared join
1196+
# column); the rewrite still peels through that Projection to reach the Join.
1197+
query TT
1198+
EXPLAIN SELECT t_using1.a, t_using1.b, t_using2.y
1199+
FROM t_using1 LEFT JOIN t_using2 USING (a)
1200+
ORDER BY t_using1.b ASC LIMIT 2;
1201+
----
1202+
logical_plan
1203+
01)Sort: t_using1.b ASC NULLS LAST, fetch=2
1204+
02)--Projection: t_using1.a, t_using1.b, t_using2.y
1205+
03)----Left Join: Using t_using1.a = t_using2.a
1206+
04)------Sort: t_using1.b ASC NULLS LAST, fetch=2
1207+
05)--------TableScan: t_using1 projection=[a, b]
1208+
06)------TableScan: t_using2 projection=[a, y]
1209+
1210+
query III
1211+
SELECT t_using1.a, t_using1.b, t_using2.y
1212+
FROM t_using1 LEFT JOIN t_using2 USING (a)
1213+
ORDER BY t_using1.b ASC LIMIT 2;
1214+
----
1215+
1 10 100
1216+
2 20 200
1217+
1218+
statement ok
1219+
DROP TABLE t_using1;
1220+
1221+
statement ok
1222+
DROP TABLE t_using2;
1223+
1224+
###
1225+
### Mark joins produced from EXISTS combined with OR
1226+
###
1227+
1228+
# `t1.b > 25 OR EXISTS (...)` decorrelates to a LeftMark join, but — unlike
1229+
# a bare `EXISTS` decorrelating to LeftSemi — the OR needs the mark's boolean
1230+
# value, so a Filter always sits directly above the LeftMark join to combine
1231+
# it with the other predicate. That Filter is not one of the transparent
1232+
# nodes this rewrite peels through, so pushdown is (currently) blocked for
1233+
# every mark join reachable this way from SQL, even though the LeftMark
1234+
# preserved-side guarantee itself holds (see the unit tests
1235+
# `topk_pushed_to_left_of_left_mark_join` / `..._right_mark_join`).
1236+
query TT
1237+
EXPLAIN SELECT t1.a, t1.b
1238+
FROM t1
1239+
WHERE t1.b > 25 OR EXISTS (SELECT 1 FROM t2 WHERE t2.x = t1.a)
1240+
ORDER BY t1.b ASC LIMIT 3;
1241+
----
1242+
logical_plan
1243+
01)Sort: t1.b ASC NULLS LAST, fetch=3
1244+
02)--Projection: t1.a, t1.b
1245+
03)----Filter: t1.b > Int32(25) OR __correlated_sq_1.mark
1246+
04)------LeftMark Join: t1.a = __correlated_sq_1.x
1247+
05)--------TableScan: t1 projection=[a, b]
1248+
06)--------SubqueryAlias: __correlated_sq_1
1249+
07)----------TableScan: t2 projection=[x]
1250+
1251+
query II
1252+
SELECT t1.a, t1.b
1253+
FROM t1
1254+
WHERE t1.b > 25 OR EXISTS (SELECT 1 FROM t2 WHERE t2.x = t1.a)
1255+
ORDER BY t1.b ASC LIMIT 3;
1256+
----
1257+
1 10
1258+
2 20
1259+
3 30
1260+
1261+
###
1262+
### Sort key is a projected expression, not a plain column
1263+
###
1264+
1265+
# The outer ORDER BY references a projected expression built only from
1266+
# preserved-side columns. The rule resolves neg_b through the Projection to
1267+
# (- t1.b) and pushes a Sort on that expression below the join.
1268+
query TT
1269+
EXPLAIN SELECT * FROM (
1270+
SELECT t1.a, -t1.b AS neg_b, t2.y
1271+
FROM t1 LEFT JOIN t2 ON t1.a = t2.x
1272+
) sub
1273+
ORDER BY neg_b ASC LIMIT 3;
1274+
----
1275+
logical_plan
1276+
01)Sort: sub.neg_b ASC NULLS LAST, fetch=3
1277+
02)--SubqueryAlias: sub
1278+
03)----Projection: t1.a, (- t1.b) AS neg_b, t2.y
1279+
04)------Left Join: t1.a = t2.x
1280+
05)--------Sort: (- t1.b) ASC NULLS LAST, fetch=3
1281+
06)----------TableScan: t1 projection=[a, b]
1282+
07)--------TableScan: t2 projection=[x, y]
1283+
1284+
query III
1285+
SELECT * FROM (
1286+
SELECT t1.a, -t1.b AS neg_b, t2.y
1287+
FROM t1 LEFT JOIN t2 ON t1.a = t2.x
1288+
) sub
1289+
ORDER BY neg_b ASC LIMIT 3;
1290+
----
1291+
5 -50 NULL
1292+
4 -40 NULL
1293+
3 -30 300
1294+
1295+
# Negative case: the projected expression mixes both sides. After resolving
1296+
# through the Projection the key references t2.y, so nothing is pushed.
1297+
query TT
1298+
EXPLAIN SELECT * FROM (
1299+
SELECT t1.a, t1.b + coalesce(t2.y, 0) AS mixed
1300+
FROM t1 LEFT JOIN t2 ON t1.a = t2.x
1301+
) sub
1302+
ORDER BY mixed DESC LIMIT 3;
1303+
----
1304+
logical_plan
1305+
01)Sort: sub.mixed DESC NULLS FIRST, fetch=3
1306+
02)--SubqueryAlias: sub
1307+
03)----Projection: t1.a, CAST(t1.b AS Int64) + CASE WHEN __common_expr_1 IS NOT NULL THEN __common_expr_1 ELSE Int64(0) END AS mixed
1308+
04)------Projection: CAST(t2.y AS Int64) AS __common_expr_1, t1.a, t1.b
1309+
05)--------Left Join: t1.a = t2.x
1310+
06)----------TableScan: t1 projection=[a, b]
1311+
07)----------TableScan: t2 projection=[x, y]
1312+
1313+
query II
1314+
SELECT * FROM (
1315+
SELECT t1.a, t1.b + coalesce(t2.y, 0) AS mixed
1316+
FROM t1 LEFT JOIN t2 ON t1.a = t2.x
1317+
) sub
1318+
ORDER BY mixed DESC LIMIT 3;
1319+
----
1320+
3 330
1321+
2 220
1322+
1 110
1323+
1324+
# Two stacked Projections: the key is computed in the inner one and re-aliased
1325+
# in the outer one. Resolution must substitute through both layers.
1326+
query TT
1327+
EXPLAIN SELECT a, k2 FROM (
1328+
SELECT a, k1 AS k2 FROM (
1329+
SELECT t1.a, t1.b * 2 AS k1, t2.y
1330+
FROM t1 LEFT JOIN t2 ON t1.a = t2.x
1331+
) s1
1332+
) s2
1333+
ORDER BY k2 DESC LIMIT 2;
1334+
----
1335+
logical_plan
1336+
01)Sort: s2.k2 DESC NULLS FIRST, fetch=2
1337+
02)--SubqueryAlias: s2
1338+
03)----Projection: s1.a, s1.k1 AS k2
1339+
04)------SubqueryAlias: s1
1340+
05)--------Projection: t1.a, CAST(t1.b AS Int64) * Int64(2) AS k1
1341+
06)----------Left Join: t1.a = t2.x
1342+
07)------------Sort: CAST(t1.b AS Int64) * Int64(2) DESC NULLS FIRST, fetch=2
1343+
08)--------------TableScan: t1 projection=[a, b]
1344+
09)------------TableScan: t2 projection=[x]
1345+
1346+
query II
1347+
SELECT a, k2 FROM (
1348+
SELECT a, k1 AS k2 FROM (
1349+
SELECT t1.a, t1.b * 2 AS k1, t2.y
1350+
FROM t1 LEFT JOIN t2 ON t1.a = t2.x
1351+
) s1
1352+
) s2
1353+
ORDER BY k2 DESC LIMIT 2;
1354+
----
1355+
5 100
1356+
4 80
1357+
11421358
statement ok
11431359
set datafusion.execution.target_partitions = 4;
11441360

0 commit comments

Comments
 (0)