Skip to content

Commit 52d519e

Browse files
committed
opt: don't remap cols with non-identical types during decorrelation
The decorrelation rules `TryRemapJoinOuterColsRight`, `TryRemapJoinOuterColsLeft`, and `TryRemapSelectOuterCols` attempt to decorrelate an expression by replacing an outer-column reference with an equivalent non-outer column reference. However, this includes columns that are _equivalent_ but not _identical_, the difference being that equivalent columns can have different width or precision, while identical types are exactly the same. This could cause incorrect results, like the following example: ``` statement ok CREATE TABLE xy108057 (x DECIMAL(10, 2) PRIMARY KEY, y DECIMAL(10, 2)); CREATE TABLE ab108057 (a DECIMAL(10, 0), b DECIMAL(10, 0)); statement ok INSERT INTO xy108057 VALUES (1.00, 1.00); INSERT INTO ab108057 VALUES (1, 1); query RRRR SELECT * FROM xy108057 INNER JOIN LATERAL (SELECT a, a+x FROM ab108057) ON a = x ---- 1.00 1.00 1 2.00 ``` This patch fixes this bug by checking that the replacement column is identical, not just equivalent, to the outer column. Fixes cockroachdb#108057 Release note (bug fix): Fixed a bug introduced in 23.1 that could cause the precision of some values to be incorrectly truncated for a query with a correlated subquery and an equality between a column from the subquery and the outer query. This applies to types that are "equivalent" but have different precision levels, e.g. `DECIMAL(10, 0)` vs `DECIMAL(10, 2)` or `NAME` vs `CHAR`.
1 parent e145cc2 commit 52d519e

File tree

3 files changed

+63
-5
lines changed

3 files changed

+63
-5
lines changed

pkg/sql/logictest/testdata/logic_test/subquery_correlated

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,3 +1332,22 @@ SELECT (2, 20) = ANY (
13321332
----
13331333
NULL
13341334
NULL
1335+
1336+
# Regression test for #108057 - maintain the precision of the a+x operation.
1337+
statement ok
1338+
CREATE TABLE xy108057 (x DECIMAL(10, 2) PRIMARY KEY, y DECIMAL(10, 2));
1339+
CREATE TABLE ab108057 (a DECIMAL(10, 0), b DECIMAL(10, 0));
1340+
1341+
statement ok
1342+
INSERT INTO xy108057 VALUES (1.00, 1.00);
1343+
INSERT INTO ab108057 VALUES (1, 1);
1344+
1345+
query RRRR
1346+
SELECT * FROM xy108057 INNER JOIN LATERAL (SELECT a, a+x FROM ab108057) ON a = x
1347+
----
1348+
1.00 1.00 1 2.00
1349+
1350+
query RRRR
1351+
SELECT * FROM ab108057 INNER JOIN LATERAL (SELECT x, x+a FROM xy108057) ON a = x
1352+
----
1353+
1 1 1.00 2.00

pkg/sql/opt/norm/decorrelate_funcs.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,8 +1147,8 @@ func (c *CustomFuncs) TryRemapOuterCols(
11471147
}
11481148

11491149
// tryRemapOuterCols handles the traversal and outer-column replacement for
1150-
// TryRemapOuterCols. It returns the replacement expression and whether an
1151-
// outer-column reference was successfully remapped.
1150+
// TryRemapOuterCols. It returns the replacement expression, which may be
1151+
// unchanged if remapping was not possible.
11521152
func (c *CustomFuncs) tryRemapOuterCols(
11531153
expr opt.Expr, outerCol opt.ColumnID, substituteCols opt.ColSet,
11541154
) opt.Expr {
@@ -1160,9 +1160,15 @@ func (c *CustomFuncs) tryRemapOuterCols(
11601160
switch t := expr.(type) {
11611161
case *memo.VariableExpr:
11621162
if t.Col == outerCol {
1163-
if replaceCol, ok := substituteCols.Next(0); ok {
1164-
// This outer-column reference can be remapped.
1165-
return c.f.ConstructVariable(replaceCol)
1163+
md := c.mem.Metadata()
1164+
outerColTyp := md.ColumnMeta(outerCol).Type
1165+
replaceCol, ok := substituteCols.Next(0)
1166+
for ; ok; replaceCol, ok = substituteCols.Next(replaceCol + 1) {
1167+
if outerColTyp.Identical(md.ColumnMeta(replaceCol).Type) {
1168+
// Only perform the replacement if the types are identical.
1169+
// This outer-column reference can be remapped.
1170+
return c.f.ConstructVariable(replaceCol)
1171+
}
11661172
}
11671173
}
11681174
case memo.RelExpr:

pkg/sql/opt/norm/testdata/rules/decorrelate

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ exec-ddl
1818
CREATE TABLE ab (a INT, b INT)
1919
----
2020

21+
exec-ddl
22+
CREATE TABLE xy_decimal (x DECIMAL(10, 2) PRIMARY KEY, y DECIMAL(10, 2));
23+
----
24+
25+
exec-ddl
26+
CREATE TABLE ab_decimal (a DECIMAL(10, 0), b DECIMAL(10, 0));
27+
----
28+
2129
# --------------------------------------------------
2230
# DecorrelateJoin
2331
# --------------------------------------------------
@@ -6925,6 +6933,31 @@ project
69256933
└── filters
69266934
└── corr = x
69276935

6936+
# Regression test for #108057. Do not remap a column into an equivalent, but not
6937+
# identically typed column. The a + x projection should remain.
6938+
norm expect-not=(TryRemapJoinOuterColsRight,TryRemapJoinOuterColsLeft,TryRemapSelectOuterCols)
6939+
SELECT * FROM xy_decimal INNER JOIN LATERAL (SELECT a, a+x FROM ab_decimal) ON a = x
6940+
----
6941+
project
6942+
├── columns: x:1!null y:2 a:5!null "?column?":10!null
6943+
├── immutable
6944+
├── fd: (1)-->(2), (1)==(5), (5)==(1), (5)-->(10)
6945+
├── inner-join (hash)
6946+
│ ├── columns: x:1!null y:2 a:5!null
6947+
│ ├── multiplicity: left-rows(zero-or-more), right-rows(zero-or-one)
6948+
│ ├── immutable
6949+
│ ├── fd: (1)-->(2), (1)==(5), (5)==(1)
6950+
│ ├── scan xy_decimal
6951+
│ │ ├── columns: x:1!null y:2
6952+
│ │ ├── key: (1)
6953+
│ │ └── fd: (1)-->(2)
6954+
│ ├── scan ab_decimal
6955+
│ │ └── columns: a:5
6956+
│ └── filters
6957+
│ └── a:5 = x:1 [outer=(1,5), immutable, constraints=(/1: (/NULL - ]; /5: (/NULL - ]), fd=(1)==(5), (5)==(1)]
6958+
└── projections
6959+
└── a:5 + x:1 [as="?column?":10, outer=(1,5), immutable]
6960+
69286961
# --------------------------------------------------
69296962
# TryRemapJoinOuterColsLeft
69306963
# --------------------------------------------------

0 commit comments

Comments
 (0)