Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

release-21.1: opt: do not generate unnecessary cross-joins on join input #79508

Merged
merged 1 commit into from
Apr 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions pkg/sql/logictest/testdata/logic_test/inverted_join_multi_column
Original file line number Diff line number Diff line change
Expand Up @@ -494,8 +494,8 @@ ORDER BY (lk, rk)
5 12
5 16

# Regression test for #59615. Ensure that invalid inverted joins are not created
# for left and anti joins.
# Regression test for #59615 and #78681. Ensure that invalid inverted joins are
# not created for left, semi, and anti joins.
statement ok
CREATE TABLE t59615_inv (
x INT NOT NULL CHECK (x in (1, 3)),
Expand All @@ -517,3 +517,13 @@ SELECT * FROM (VALUES ('"a"'::jsonb), ('"b"'::jsonb)) AS u(y) WHERE NOT EXISTS (
----
"a"
"b"

statement ok
INSERT INTO t59615_inv VALUES (1, '"a"'::JSONB), (3, '"a"'::JSONB)

query T rowsort
SELECT * FROM (VALUES ('"a"'::jsonb), ('"b"'::jsonb)) AS u(y) WHERE EXISTS (
SELECT * FROM t59615_inv t WHERE t.y @> u.y
)
----
"a"
14 changes: 9 additions & 5 deletions pkg/sql/opt/xform/join_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -811,11 +811,15 @@ func (c *CustomFuncs) GenerateInvertedJoins(
return
}

if len(foundVals) > 1 && (joinType == opt.LeftJoinOp || joinType == opt.AntiJoinOp) {
// We cannot create an inverted join in this case, because constructing
// a cross join with foundVals will increase the size of the input. As a
// result, non-matching input rows will show up more than once in the
// output, which is incorrect (see #59615).
if len(foundVals) > 1 &&
(joinType == opt.LeftJoinOp || joinType == opt.SemiJoinOp || joinType == opt.AntiJoinOp) {
// We cannot create an inverted join in this case, because
// constructing a cross join with foundVals will increase the
// size of the input. As a result, matching input rows will show
// up more than once in the output of a semi-join, and
// non-matching input rows will show up more than once in the
// output of a left or anti join, which is incorrect (see #59615
// and #78681).
// TODO(rytaft,mgartner): find a way to create an inverted join for this
// case.
return
Expand Down
100 changes: 70 additions & 30 deletions pkg/sql/opt/xform/testdata/rules/join
Original file line number Diff line number Diff line change
Expand Up @@ -7598,38 +7598,53 @@ WHERE EXISTS (
SELECT * FROM json_arr1 AS t1 WHERE t1.j @> t2.j AND t1.i IN (3, 4)
)
----
semi-join (lookup json_arr1 [as=t1])
project
├── columns: k:1!null l:2 j:3 a:4
├── key columns: [20] = [6]
├── lookup columns are key
├── second join in paired joiner
├── immutable
├── key: (1)
├── fd: (1)-->(2-4)
├── inner-join (inverted json_arr1@j_idx [as=t1])
│ ├── columns: t2.k:1!null l:2 t2.j:3 t2.a:4 t1.k:20!null i:21!null continuation:33
│ ├── prefix key columns: [19] = [21]
│ ├── first join in paired joiner; continuation column: continuation:33
│ ├── inverted-expr
│ │ └── t1.j:22 @> t2.j:3
│ ├── fd: (1)-->(2-4), (20)-->(21,33)
│ ├── inner-join (cross)
│ │ ├── columns: t2.k:1!null l:2 t2.j:3 t2.a:4 "inverted_join_const_col_@7":19!null
│ │ ├── multiplicity: left-rows(one-or-more), right-rows(zero-or-more)
│ │ ├── fd: (1)-->(2-4)
│ │ ├── scan json_arr2 [as=t2]
│ │ │ ├── columns: t2.k:1!null l:2 t2.j:3 t2.a:4
│ │ │ ├── key: (1)
│ │ │ └── fd: (1)-->(2-4)
│ │ ├── values
│ │ │ ├── columns: "inverted_join_const_col_@7":19!null
│ │ │ ├── cardinality: [2 - 2]
│ │ │ ├── (3,)
│ │ │ └── (4,)
│ │ └── filters (true)
│ └── filters (true)
└── filters
└── t1.j:8 @> t2.j:3 [outer=(3,8), immutable]
└── distinct-on
├── columns: t2.k:1!null l:2 t2.j:3 t2.a:4
├── grouping columns: t2.k:1!null
├── immutable
├── key: (1)
├── fd: (1)-->(2-4)
├── inner-join (lookup json_arr1 [as=t1])
│ ├── columns: t2.k:1!null l:2 t2.j:3 t2.a:4 i:7!null t1.j:8
│ ├── key columns: [20] = [6]
│ ├── lookup columns are key
│ ├── immutable
│ ├── fd: (1)-->(2-4)
│ ├── inner-join (inverted json_arr1@j_idx [as=t1])
│ │ ├── columns: t2.k:1!null l:2 t2.j:3 t2.a:4 t1.k:20!null i:21!null
│ │ ├── prefix key columns: [19] = [21]
│ │ ├── inverted-expr
│ │ │ └── t1.j:22 @> t2.j:3
│ │ ├── fd: (1)-->(2-4), (20)-->(21)
│ │ ├── inner-join (cross)
│ │ │ ├── columns: t2.k:1!null l:2 t2.j:3 t2.a:4 "inverted_join_const_col_@7":19!null
│ │ │ ├── multiplicity: left-rows(one-or-more), right-rows(zero-or-more)
│ │ │ ├── fd: (1)-->(2-4)
│ │ │ ├── scan json_arr2 [as=t2]
│ │ │ │ ├── columns: t2.k:1!null l:2 t2.j:3 t2.a:4
│ │ │ │ ├── key: (1)
│ │ │ │ └── fd: (1)-->(2-4)
│ │ │ ├── values
│ │ │ │ ├── columns: "inverted_join_const_col_@7":19!null
│ │ │ │ ├── cardinality: [2 - 2]
│ │ │ │ ├── (3,)
│ │ │ │ └── (4,)
│ │ │ └── filters (true)
│ │ └── filters (true)
│ └── filters
│ └── t1.j:8 @> t2.j:3 [outer=(3,8), immutable]
└── aggregations
├── const-agg [as=l:2, outer=(2)]
│ └── l:2
├── const-agg [as=t2.j:3, outer=(3)]
│ └── t2.j:3
└── const-agg [as=t2.a:4, outer=(4)]
└── t2.a:4

# Generate an inverted semi-join on a multi-column inverted index with the
# prefix column constrained by an equality constraint.
Expand Down Expand Up @@ -7721,8 +7736,8 @@ anti-join (lookup json_arr1 [as=t1])
└── filters
└── t1.j:8 @> t2.j:3 [outer=(3,8), immutable]

# Regression test for #59615. Ensure that invalid inverted joins are not created
# for left and anti joins.
# Regression test for #59615 and #78681. Ensure that invalid inverted joins are
# not created for left, semi, and anti joins.
exec-ddl
CREATE TABLE t59615_inv (
x INT NOT NULL CHECK (x in (1, 3)),
Expand Down Expand Up @@ -7751,6 +7766,31 @@ right-join (cross)
└── filters
└── y:3 @> column1:1 [outer=(1,3), immutable]

# Disable ConvertSemiToInnerJoin to prevent GenerateInvertedJoins from firing
# for the converted inner join. With the expect-not option, we get added
# assurance that GenerateInvertedJoins is not incorrectly firing for the
# semi-join.
opt disable=ConvertSemiToInnerJoin expect-not=GenerateInvertedJoins
SELECT * FROM (VALUES ('"a"'::jsonb), ('"b"'::jsonb)) AS u(y) WHERE EXISTS (
SELECT * FROM t59615_inv t WHERE t.y @> u.y
)
----
semi-join (cross)
├── columns: y:1!null
├── cardinality: [0 - 2]
├── immutable
├── values
│ ├── columns: column1:1!null
│ ├── cardinality: [2 - 2]
│ ├── ('"a"',)
│ └── ('"b"',)
├── scan t59615_inv [as=t]
│ ├── columns: y:3
│ └── check constraint expressions
│ └── x:2 IN (1, 3) [outer=(2), constraints=(/2: [/1 - /1] [/3 - /3]; tight)]
└── filters
└── y:3 @> column1:1 [outer=(1,3), immutable]

opt expect-not=GenerateInvertedJoins
SELECT * FROM (VALUES ('"a"'::jsonb), ('"b"'::jsonb)) AS u(y) WHERE NOT EXISTS (
SELECT * FROM t59615_inv t WHERE t.y @> u.y
Expand Down