Skip to content

Commit

Permalink
Merge #44156
Browse files Browse the repository at this point in the history
44156: opt: fix incorrect empty key for full outer join r=RaduBerinde a=RaduBerinde

When joining two single-row expressions, the cross product has an
empty key, but the outer join does not. We need to remove the key in
this case.

Fixes #44029.

Release note (bug fix): fixed planning bug related to FULL joins
between single-row relations.

Co-authored-by: Radu Berinde <radu@cockroachlabs.com>
  • Loading branch information
craig[bot] and RaduBerinde committed Jan 22, 2020
2 parents a309cac + 7387d89 commit ab3c1f9
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 3 deletions.
8 changes: 8 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/join
Original file line number Diff line number Diff line change
Expand Up @@ -1074,3 +1074,11 @@ SELECT * FROM foo JOIN bar ON generate_series(0, 1) < 2

query error aggregate functions are not allowed in ON
SELECT * FROM foo JOIN bar ON max(foo.c) < 2

# Regression test for #44029 (outer join on two single-row clauses, with two
# results).
query IIII
SELECT * FROM (VALUES (1, 2)) a(a1,a2) FULL JOIN (VALUES (3, 4)) b(b1,b2) ON a1=b1 ORDER BY a2
----
NULL NULL 3 4
1 2 NULL NULL
4 changes: 4 additions & 0 deletions pkg/sql/opt/memo/logical_props_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -1883,6 +1883,10 @@ func (h *joinPropsHelper) setFuncDeps(rel *props.Relational) {
inputCols := h.leftProps.OutputCols.Union(h.rightProps.OutputCols)
if !inputCols.Intersects(notNullInputCols) {
rel.FuncDeps.DowngradeKey()
} else if key, ok := rel.FuncDeps.StrictKey(); ok && key.Empty() {
// The cross-product has an empty key when both sides have an empty key;
// but the outer join can have two rows so the empty key doesn't hold.
rel.FuncDeps.DowngradeKey()
}
rel.FuncDeps.MakeOuter(h.leftProps.OutputCols, notNullInputCols)
rel.FuncDeps.MakeOuter(h.rightProps.OutputCols, notNullInputCols)
Expand Down
36 changes: 33 additions & 3 deletions pkg/sql/opt/memo/testdata/logprops/join
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,38 @@ full-join (cross)
└── filters
└── true [type=bool]

# Calculate full-join cardinality when both sides have an empty key (#44029).
build
SELECT * FROM (VALUES (1, 2)) a(a1,a2) FULL JOIN (VALUES (3, 4)) b(b1,b2) ON a1=b1
----
full-join (hash)
├── columns: a1:1(int) a2:2(int) b1:3(int) b2:4(int)
├── cardinality: [1 - 2]
├── prune: (2,4)
├── reject-nulls: (1-4)
├── values
│ ├── columns: column1:1(int!null) column2:2(int!null)
│ ├── cardinality: [1 - 1]
│ ├── key: ()
│ ├── fd: ()-->(1,2)
│ ├── prune: (1,2)
│ └── tuple [type=tuple{int, int}]
│ ├── const: 1 [type=int]
│ └── const: 2 [type=int]
├── values
│ ├── columns: column1:3(int!null) column2:4(int!null)
│ ├── cardinality: [1 - 1]
│ ├── key: ()
│ ├── fd: ()-->(3,4)
│ ├── prune: (3,4)
│ └── tuple [type=tuple{int, int}]
│ ├── const: 3 [type=int]
│ └── const: 4 [type=int]
└── filters
└── eq [type=bool, outer=(1,3), constraints=(/1: (/NULL - ]; /3: (/NULL - ]), fd=(1)==(3), (3)==(1)]
├── variable: column1 [type=int]
└── variable: column1 [type=int]

# Calculate full-join cardinality with false filter.
build
SELECT * FROM (VALUES (NULL), (NULL)) a FULL JOIN (VALUES (NULL), (NULL)) b ON a.column1=b.column1
Expand Down Expand Up @@ -949,9 +981,7 @@ SELECT * FROM (SELECT * FROM xysd LIMIT 1) FULL JOIN (SELECT * FROM xysd LIMIT 1
----
full-join (cross)
├── columns: x:1(int) y:2(int) s:3(string) d:4(decimal) x:5(int) y:6(int) s:7(string) d:8(decimal)
├── cardinality: [0 - 1]
├── key: ()
├── fd: ()-->(1-8)
├── cardinality: [0 - 2]
├── prune: (1-8)
├── reject-nulls: (1-8)
├── interesting orderings: (+1) (-3,+4,+1) (+5) (-7,+8,+5)
Expand Down

0 comments on commit ab3c1f9

Please sign in to comment.