Skip to content

Commit 2a2adfe

Browse files
craig[bot]DrewKimball
andcommitted
Merge #143733
143733: opt: do not hoist outer columns from the right input of a left-join r=mgartner a=DrewKimball Recently in #142251 we improved the `HoistJoinProjectRight` norm rule to allow hoisting a `Project` with remapping projections, which simply map from one column ID to another. However, there is an issue when the remapped column is an outer column and the join is a left-join: after the `Project` is hoisted, the projection will no longer be `NULL` for rows that were null-extended by the join. This causes incorrect results. The fix is simply to check that only input columns are remapped, since they will properly reflect the effects of null-extension. There is no release note because the rule change with the bug is only on master. Fixes #142531 Release note: None Co-authored-by: Drew Kimball <[email protected]>
2 parents 7a3bd6b + da630eb commit 2a2adfe

File tree

5 files changed

+166
-24
lines changed

5 files changed

+166
-24
lines changed

pkg/sql/opt/exec/execbuilder/testdata/vectorize_local

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -499,8 +499,9 @@ EXPLAIN (VEC)
499499
└ Node 1
500500
└ *colexec.caseOp
501501
├ *colexec.bufferOp
502-
│ └ *colfetcher.ColIndexJoin
503-
│ └ *colfetcher.ColBatchScan
502+
│ └ *sql.planNodeToRowSource
503+
│ └ *colfetcher.ColIndexJoin
504+
│ └ *colfetcher.ColBatchScan
504505
├ *colexec.bufferOp
505506
└ *colexec.bufferOp
506507

pkg/sql/opt/memo/testdata/logprops/join

Lines changed: 80 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3352,10 +3352,85 @@ SELECT (
33523352
FROM table3_119441 AS baz;
33533353
----
33543354
project
3355-
├── columns: col3_5:28(varchar!null)
3355+
├── columns: col3_5:28(varchar)
33563356
├── prune: (28)
3357-
├── scan table3_119441 [as=baz]
3358-
│ ├── columns: baz.col3_5:1(varchar!null)
3359-
│ └── prune: (1)
3357+
├── reject-nulls: (28)
3358+
├── left-join-apply
3359+
│ ├── columns: baz.col3_5:1(varchar!null) baz.col3_11:2(char) col3_5:27(varchar)
3360+
│ ├── prune: (27)
3361+
│ ├── reject-nulls: (27)
3362+
│ ├── scan table3_119441 [as=baz]
3363+
│ │ ├── columns: baz.col3_5:1(varchar!null) baz.col3_11:2(char)
3364+
│ │ ├── prune: (1,2)
3365+
│ │ └── unfiltered-cols: (1-6)
3366+
│ ├── project
3367+
│ │ ├── columns: col3_5:27(varchar)
3368+
│ │ ├── outer: (1,2)
3369+
│ │ ├── cardinality: [0 - 1]
3370+
│ │ ├── key: ()
3371+
│ │ ├── fd: ()-->(27)
3372+
│ │ ├── prune: (27)
3373+
│ │ ├── limit
3374+
│ │ │ ├── columns: col1_8:8(bytes!null) col1_11:9(string!null) col1_12:10(string!null) col2_1:14(int4!null) col2_2:15(string!null) col2_3:16(bytes!null) table3_119441.col3_16:22(int!null)
3375+
│ │ │ ├── outer: (2)
3376+
│ │ │ ├── cardinality: [0 - 1]
3377+
│ │ │ ├── key: ()
3378+
│ │ │ ├── fd: ()-->(8-10,14-16,22), (9)==(10,15), (10)==(9,15), (15)==(9,10), (8)==(16), (16)==(8), (14)==(22), (22)==(14)
3379+
│ │ │ ├── interesting orderings: (+(9|10)) (+22)
3380+
│ │ │ ├── inner-join (hash)
3381+
│ │ │ │ ├── columns: col1_8:8(bytes!null) col1_11:9(string!null) col1_12:10(string!null) col2_1:14(int4!null) col2_2:15(string!null) col2_3:16(bytes!null) table3_119441.col3_16:22(int!null)
3382+
│ │ │ │ ├── outer: (2)
3383+
│ │ │ │ ├── fd: ()-->(9,10,15), (9)==(10,15), (10)==(9,15), (15)==(9,10), (8)==(16), (16)==(8), (14)==(22), (22)==(14)
3384+
│ │ │ │ ├── limit hint: 1.00
3385+
│ │ │ │ ├── interesting orderings: (+(9|10)) (+22)
3386+
│ │ │ │ ├── top-k
3387+
│ │ │ │ │ ├── columns: table3_119441.col3_16:22(int)
3388+
│ │ │ │ │ ├── internal-ordering: +22
3389+
│ │ │ │ │ ├── k: 6
3390+
│ │ │ │ │ ├── cardinality: [0 - 6]
3391+
│ │ │ │ │ ├── interesting orderings: (+22)
3392+
│ │ │ │ │ └── scan table3_119441
3393+
│ │ │ │ │ ├── columns: table3_119441.col3_16:22(int)
3394+
│ │ │ │ │ └── prune: (22)
3395+
│ │ │ │ ├── inner-join (hash)
3396+
│ │ │ │ │ ├── columns: col1_8:8(bytes!null) col1_11:9(string!null) col1_12:10(string!null) col2_1:14(int4) col2_2:15(string!null) col2_3:16(bytes!null)
3397+
│ │ │ │ │ ├── fd: (9)==(10,15), (10)==(9,15), (15)==(9,10), (8)==(16), (16)==(8)
3398+
│ │ │ │ │ ├── prune: (14)
3399+
│ │ │ │ │ ├── interesting orderings: (+(9|10))
3400+
│ │ │ │ │ ├── scan table2_119441
3401+
│ │ │ │ │ │ ├── columns: col2_1:14(int4) col2_2:15(string) col2_3:16(bytes)
3402+
│ │ │ │ │ │ ├── prune: (14-16)
3403+
│ │ │ │ │ │ └── unfiltered-cols: (14-19)
3404+
│ │ │ │ │ ├── select
3405+
│ │ │ │ │ │ ├── columns: col1_8:8(bytes) col1_11:9(string!null) col1_12:10(string!null)
3406+
│ │ │ │ │ │ ├── fd: (9)==(10), (10)==(9)
3407+
│ │ │ │ │ │ ├── prune: (8)
3408+
│ │ │ │ │ │ ├── interesting orderings: (+(9|10))
3409+
│ │ │ │ │ │ ├── scan table1_119441
3410+
│ │ │ │ │ │ │ ├── columns: col1_8:8(bytes) col1_11:9(string) col1_12:10(string)
3411+
│ │ │ │ │ │ │ ├── prune: (8-10)
3412+
│ │ │ │ │ │ │ └── interesting orderings: (+9)
3413+
│ │ │ │ │ │ └── filters
3414+
│ │ │ │ │ │ └── eq [type=bool, outer=(9,10), constraints=(/9: (/NULL - ]; /10: (/NULL - ]), fd=(9)==(10), (10)==(9)]
3415+
│ │ │ │ │ │ ├── variable: col1_11:9 [type=string]
3416+
│ │ │ │ │ │ └── variable: col1_12:10 [type=string]
3417+
│ │ │ │ │ └── filters
3418+
│ │ │ │ │ ├── eq [type=bool, outer=(8,16), constraints=(/8: (/NULL - ]; /16: (/NULL - ]), fd=(8)==(16), (16)==(8)]
3419+
│ │ │ │ │ │ ├── variable: col1_8:8 [type=bytes]
3420+
│ │ │ │ │ │ └── variable: col2_3:16 [type=bytes]
3421+
│ │ │ │ │ └── eq [type=bool, outer=(9,15), constraints=(/9: (/NULL - ]; /15: (/NULL - ]), fd=(9)==(15), (15)==(9)]
3422+
│ │ │ │ │ ├── variable: col1_11:9 [type=string]
3423+
│ │ │ │ │ └── variable: col2_2:15 [type=string]
3424+
│ │ │ │ └── filters
3425+
│ │ │ │ ├── eq [type=bool, outer=(14,22), constraints=(/14: (/NULL - ]; /22: (/NULL - ]), fd=(14)==(22), (22)==(14)]
3426+
│ │ │ │ │ ├── variable: col2_1:14 [type=int4]
3427+
│ │ │ │ │ └── variable: table3_119441.col3_16:22 [type=int]
3428+
│ │ │ │ └── eq [type=bool, outer=(2,9), constraints=(/2: (/NULL - ]; /9: (/NULL - ]), fd=(2)==(9), (9)==(2)]
3429+
│ │ │ │ ├── variable: col1_11:9 [type=string]
3430+
│ │ │ │ └── variable: baz.col3_11:2 [type=char]
3431+
│ │ │ └── const: 1 [type=int]
3432+
│ │ └── projections
3433+
│ │ └── variable: baz.col3_5:1 [as=col3_5:27, type=varchar, outer=(1)]
3434+
│ └── filters (true)
33603435
└── projections
3361-
└── variable: baz.col3_5:1 [as=col3_5:28, type=varchar, outer=(1)]
3436+
└── variable: col3_5:27 [as=col3_5:28, type=varchar, outer=(27)]

pkg/sql/opt/norm/rules/join.opt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,16 @@ $left
427427
$right:(Project
428428
$input:*
429429
$projections:* &
430-
(AllAreRemappingProjections $projections)
430+
(AllAreRemappingProjections $projections) &
431+
432+
# Ensure that there are no outer-column references in the
433+
# projections, since otherwise hoisting the Project could change
434+
# the result of a left-join due to the NULL-extended rows.
435+
# TODO(drewk): we could allow this for inner-joins.
436+
(ColsAreSubset
437+
(ProjectionOuterCols $projections)
438+
(OutputCols $input)
439+
)
431440
$passThrough:*
432441
)
433442
$on:*

pkg/sql/opt/norm/testdata/rules/join

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2878,6 +2878,56 @@ project
28782878
└── projections
28792879
└── c.y:6 [as=k:10, outer=(6)]
28802880

2881+
# Regression test for #142531 - do not hoist outer-column references from the
2882+
# right input of a left-join.
2883+
exec-ddl
2884+
CREATE TABLE table_142531 (col1_1 INT NOT NULL);
2885+
----
2886+
2887+
norm expect-not=HoistJoinProjectRight
2888+
SELECT (SELECT t.col1_1 FROM table_142531 WHERE (NOT EXISTS (SELECT (0)))) FROM table_142531 AS t;
2889+
----
2890+
project
2891+
├── columns: col1_1:13
2892+
├── ensure-distinct-on
2893+
│ ├── columns: t.rowid:2!null col1_1:12
2894+
│ ├── grouping columns: t.rowid:2!null
2895+
│ ├── error: "more than one row returned by a subquery used as an expression"
2896+
│ ├── key: (2)
2897+
│ ├── fd: (2)-->(12)
2898+
│ ├── left-join-apply
2899+
│ │ ├── columns: t.col1_1:1!null t.rowid:2!null col1_1:12
2900+
│ │ ├── fd: (2)-->(1)
2901+
│ │ ├── scan table_142531 [as=t]
2902+
│ │ │ ├── columns: t.col1_1:1!null t.rowid:2!null
2903+
│ │ │ ├── key: (2)
2904+
│ │ │ └── fd: (2)-->(1)
2905+
│ │ ├── project
2906+
│ │ │ ├── columns: col1_1:12
2907+
│ │ │ ├── outer: (1)
2908+
│ │ │ ├── fd: ()-->(12)
2909+
│ │ │ ├── select
2910+
│ │ │ │ ├── scan table_142531
2911+
│ │ │ │ └── filters
2912+
│ │ │ │ └── not [subquery]
2913+
│ │ │ │ └── coalesce
2914+
│ │ │ │ ├── subquery
2915+
│ │ │ │ │ └── values
2916+
│ │ │ │ │ ├── columns: column11:11!null
2917+
│ │ │ │ │ ├── cardinality: [1 - 1]
2918+
│ │ │ │ │ ├── key: ()
2919+
│ │ │ │ │ ├── fd: ()-->(11)
2920+
│ │ │ │ │ └── (true,)
2921+
│ │ │ │ └── false
2922+
│ │ │ └── projections
2923+
│ │ │ └── t.col1_1:1 [as=col1_1:12, outer=(1)]
2924+
│ │ └── filters (true)
2925+
│ └── aggregations
2926+
│ └── const-agg [as=col1_1:12, outer=(12)]
2927+
│ └── col1_1:12
2928+
└── projections
2929+
└── col1_1:12 [as=col1_1:13, outer=(12)]
2930+
28812931
# --------------------------------------------------
28822932
# HoistJoinProjectLeft
28832933
# --------------------------------------------------

pkg/sql/opt/norm/testdata/rules/reject_nulls

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1673,21 +1673,22 @@ project
16731673
├── sort
16741674
│ ├── columns: tab_3663.col2_3:2 tab_3663.col2_5:4 tab_3663.col2_6:5!null tab_3663.col2_9:6 tab_3663.crdb_internal_mvcc_timestamp:11 tab_3663.tableoid:12
16751675
│ ├── ordering: -11,-12,+4
1676-
│ └── semi-join (hash)
1676+
│ └── semi-join-apply
16771677
│ ├── columns: tab_3663.col2_3:2 tab_3663.col2_5:4 tab_3663.col2_6:5!null tab_3663.col2_9:6 tab_3663.crdb_internal_mvcc_timestamp:11 tab_3663.tableoid:12
16781678
│ ├── scan table2_89986 [as=tab_3663]
16791679
│ │ └── columns: tab_3663.col2_3:2 tab_3663.col2_5:4 tab_3663.col2_6:5!null tab_3663.col2_9:6 tab_3663.crdb_internal_mvcc_timestamp:11 tab_3663.tableoid:12
1680-
│ ├── inner-join (cross)
1681-
│ │ ├── columns: tab_3664.col2_12:21!null col1_3:28!null col1_8:33!null tab_3666.col2_9:43 tab_3666.col2_11:45!null
1682-
│ │ ├── fd: (33)==(45), (45)==(33), (21)==(28), (28)==(21)
1680+
│ ├── inner-join (hash)
1681+
│ │ ├── columns: tab_3664.col2_12:21!null col1_3:28!null col1_8:33 tab_3666.col2_9:43!null tab_3666.col2_11:45 col2_9:62!null
1682+
│ │ ├── outer: (6)
1683+
│ │ ├── fd: ()-->(43,62), (43)==(62), (62)==(43), (21)==(28), (28)==(21)
1684+
│ │ ├── scan table2_89986 [as=tab_3664]
1685+
│ │ │ └── columns: tab_3664.col2_12:21
16831686
│ │ ├── inner-join (hash)
1684-
│ │ │ ├── columns: tab_3664.col2_12:21!null col1_3:28!null col1_8:33!null tab_3666.col2_9:43 tab_3666.col2_11:45!null
1685-
│ │ │ ├── fd: (33)==(45), (45)==(33), (21)==(28), (28)==(21)
1686-
│ │ │ ├── scan table2_89986 [as=tab_3664]
1687-
│ │ │ │ └── columns: tab_3664.col2_12:21
1688-
│ │ │ ├── inner-join (hash)
1689-
│ │ │ │ ├── columns: col1_3:28!null col1_8:33!null tab_3666.col2_9:43 tab_3666.col2_11:45!null
1690-
│ │ │ │ ├── fd: (33)==(45), (45)==(33)
1687+
│ │ │ ├── columns: col1_3:28 col1_8:33 tab_3666.col2_9:43!null tab_3666.col2_11:45 col2_9:62!null
1688+
│ │ │ ├── outer: (6)
1689+
│ │ │ ├── fd: ()-->(43,62), (43)==(62), (62)==(43)
1690+
│ │ │ ├── left-join (hash)
1691+
│ │ │ │ ├── columns: col1_3:28 col1_8:33 tab_3666.col2_9:43 tab_3666.col2_11:45
16911692
│ │ │ │ ├── scan table2_89986 [as=tab_3666]
16921693
│ │ │ │ │ └── columns: tab_3666.col2_9:43 tab_3666.col2_11:45
16931694
│ │ │ │ ├── scan table1_89986
@@ -1699,12 +1700,18 @@ project
16991700
│ │ │ │ │ └── col1_0:25 + col1_1:26
17001701
│ │ │ │ └── filters
17011702
│ │ │ │ └── col1_8:33 = tab_3666.col2_11:45 [outer=(33,45), constraints=(/33: (/NULL - ]; /45: (/NULL - ]), fd=(33)==(45), (45)==(33)]
1703+
│ │ │ ├── project
1704+
│ │ │ │ ├── columns: col2_9:62
1705+
│ │ │ │ ├── outer: (6)
1706+
│ │ │ │ ├── fd: ()-->(62)
1707+
│ │ │ │ ├── scan table2_89986 [as=tab_3667]
1708+
│ │ │ │ └── projections
1709+
│ │ │ │ └── tab_3663.col2_9:6 [as=col2_9:62, outer=(6)]
17021710
│ │ │ └── filters
1703-
│ │ │ └── tab_3664.col2_12:21 = col1_3:28 [outer=(21,28), constraints=(/21: (/NULL - ]; /28: (/NULL - ]), fd=(21)==(28), (28)==(21)]
1704-
│ │ ├── scan table2_89986 [as=tab_3667]
1705-
│ │ └── filters (true)
1706-
│ └── filters
1707-
│ └── tab_3666.col2_9:43 = tab_3663.col2_9:6 [outer=(6,43), constraints=(/6: (/NULL - ]; /43: (/NULL - ]), fd=(6)==(43), (43)==(6)]
1711+
│ │ │ └── tab_3666.col2_9:43 = col2_9:62 [outer=(43,62), constraints=(/43: (/NULL - ]; /62: (/NULL - ]), fd=(43)==(62), (62)==(43)]
1712+
│ │ └── filters
1713+
│ │ └── tab_3664.col2_12:21 = col1_3:28 [outer=(21,28), constraints=(/21: (/NULL - ]; /28: (/NULL - ]), fd=(21)==(28), (28)==(21)]
1714+
│ └── filters (true)
17081715
└── projections
17091716
├── 0 [as="?column?":67]
17101717
├── '23:43:20-08:00:00' [as="?column?":68]

0 commit comments

Comments
 (0)