diff --git a/pkg/sql/logictest/testdata/logic_test/zigzag_join b/pkg/sql/logictest/testdata/logic_test/zigzag_join index 10b6e6988f1d..8986ec6b834b 100644 --- a/pkg/sql/logictest/testdata/logic_test/zigzag_join +++ b/pkg/sql/logictest/testdata/logic_test/zigzag_join @@ -340,3 +340,21 @@ CREATE TABLE t71271(a INT, b INT, c INT, d INT, INDEX (c), INDEX (d)) statement ok SELECT d FROM t71271 WHERE c = 3 AND d = 4 + +# Regression test for #97090. We should not plan a zigzag join when the +# direction of the equality columns doesn't match. +statement ok +CREATE TABLE t97090 ( + c INT NOT NULL, + l INT NOT NULL, + r INT NOT NULL, + INDEX (l ASC, c DESC), + INDEX (r ASC, c ASC) +); +INSERT INTO t97090 VALUES (1, 1, -1), (2, 1, -2) + +# This query should return one row. +query III +SELECT * FROM t97090 WHERE l = 1 AND r = -1 +---- +1 1 -1 diff --git a/pkg/sql/opt/exec/execbuilder/testdata/zigzag_join b/pkg/sql/opt/exec/execbuilder/testdata/zigzag_join index 1d158df95a27..56d879155d34 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/zigzag_join +++ b/pkg/sql/opt/exec/execbuilder/testdata/zigzag_join @@ -74,3 +74,17 @@ vectorized: true right table: t71271@t71271_d_idx right columns: (d, rowid) right fixed values: 1 column + +# Regression test for #97090. We should not plan a zigzag join when the +# direction of the equality columns doesn't match. +statement ok +CREATE TABLE t97090 ( + c INT NOT NULL, + l INT NOT NULL, + r INT NOT NULL, + INDEX (l ASC, c DESC), + INDEX (r ASC, c ASC) +) + +statement error could not produce a query plan conforming to the FORCE_ZIGZAG hint +EXPLAIN SELECT * FROM t97090@{FORCE_ZIGZAG} WHERE l = 1 AND r = -1 diff --git a/pkg/sql/opt/xform/select_funcs.go b/pkg/sql/opt/xform/select_funcs.go index 8c81a273e282..b53d9a7acbe8 100644 --- a/pkg/sql/opt/xform/select_funcs.go +++ b/pkg/sql/opt/xform/select_funcs.go @@ -1345,6 +1345,12 @@ func eqColsForZigzag( } for i < leftCnt && j < rightCnt { + // The zigzag joiner cannot handle equality columns that are not in the + // same direction. See #97090. + if leftIndex.Column(i).Descending != rightIndex.Column(j).Descending { + break + } + leftColID := tabID.IndexColumnID(leftIndex, i) rightColID := tabID.IndexColumnID(rightIndex, j) i++ diff --git a/pkg/sql/opt/xform/testdata/rules/select b/pkg/sql/opt/xform/testdata/rules/select index e5725f9057aa..c69c8760c26c 100644 --- a/pkg/sql/opt/xform/testdata/rules/select +++ b/pkg/sql/opt/xform/testdata/rules/select @@ -6971,6 +6971,35 @@ inner-join (zigzag pqr@q pqr@s) ├── r:3 = 1 [outer=(3), constraints=(/3: [/1 - /1]; tight), fd=()-->(3)] └── s:4 = 'foo' [outer=(4), constraints=(/4: [/'foo' - /'foo']; tight), fd=()-->(4)] +# Regression test for #97090. We should not plan a zigzag join when the +# direction of the equality columns doesn't match. +exec-ddl +CREATE TABLE t97090 ( + c INT NOT NULL, + l INT NOT NULL, + r INT NOT NULL, + INDEX (l ASC, c DESC), + INDEX (r ASC, c ASC) +) +---- + +opt expect-not=GenerateZigzagJoins +SELECT * FROM t97090 WHERE l = 1 AND r = -1 +---- +select + ├── columns: c:1!null l:2!null r:3!null + ├── fd: ()-->(2,3) + ├── index-join t97090 + │ ├── columns: c:1!null l:2!null r:3!null + │ ├── fd: ()-->(2) + │ └── scan t97090@t97090_l_c_idx + │ ├── columns: c:1!null l:2!null rowid:4!null + │ ├── constraint: /2/-1/4: [/1 - /1] + │ ├── key: (4) + │ └── fd: ()-->(2), (4)-->(1) + └── filters + └── r:3 = -1 [outer=(3), constraints=(/3: [/-1 - /-1]; tight), fd=()-->(3)] + # Zigzag join should not be produced when there is a NO_ZIGZAG_JOIN hint. opt expect-not=GenerateZigzagJoins SELECT q,r FROM pqr@{NO_ZIGZAG_JOIN} WHERE q = 1 AND r = 2 diff --git a/pkg/sql/sem/tree/datum.go b/pkg/sql/sem/tree/datum.go index f65fc2643138..3352728b18eb 100644 --- a/pkg/sql/sem/tree/datum.go +++ b/pkg/sql/sem/tree/datum.go @@ -6144,16 +6144,6 @@ func DatumPrev( return nil, false } return NewDString(prev), true - case *DCollatedString: - prev, ok := prevString(d.Contents) - if !ok { - return nil, false - } - c, err := NewDCollatedString(prev, d.Locale, collationEnv) - if err != nil { - return nil, false - } - return c, true case *DBytes: prev, ok := prevString(string(*d)) if !ok { @@ -6166,8 +6156,8 @@ func DatumPrev( return NewDInterval(prev, types.DefaultIntervalTypeMetadata), true default: // TODO(yuzefovich): consider adding support for other datums that don't - // have Datum.Prev implementation (DBitArray, DGeography, DGeometry, - // DBox2D, DJSON, DArray). + // have Datum.Prev implementation (DCollatedString, DBitArray, + // DGeography, DGeometry, DBox2D, DJSON, DArray). return datum.Prev(cmpCtx) } }