diff --git a/pkg/sql/colexec/colbuilder/execplan.go b/pkg/sql/colexec/colbuilder/execplan.go index 8328b56b1de2..a243a122d62b 100644 --- a/pkg/sql/colexec/colbuilder/execplan.go +++ b/pkg/sql/colexec/colbuilder/execplan.go @@ -1976,7 +1976,7 @@ func planSelectionOperators( if !ok || useDefaultCmpOpForIn(datumTuple) { break } - op, err = colexec.GetInOperator(lTyp, leftOp, leftIdx, datumTuple, negate) + op, err = colexec.GetInOperator(evalCtx, lTyp, leftOp, leftIdx, datumTuple, negate) case tree.IsDistinctFrom, tree.IsNotDistinctFrom: if constArg != tree.DNull { // Optimized IsDistinctFrom and IsNotDistinctFrom are @@ -2405,7 +2405,7 @@ func planProjectionExpr( break } op, err = colexec.GetInProjectionOperator( - allocator, typs[leftIdx], input, leftIdx, resultIdx, datumTuple, negate, + evalCtx, allocator, typs[leftIdx], input, leftIdx, resultIdx, datumTuple, negate, ) case tree.IsDistinctFrom, tree.IsNotDistinctFrom: if right != tree.DNull { diff --git a/pkg/sql/colexec/execgen/cmd/execgen/select_in_gen.go b/pkg/sql/colexec/execgen/cmd/execgen/select_in_gen.go index b0e6e7587d11..50bae7a4f02f 100644 --- a/pkg/sql/colexec/execgen/cmd/execgen/select_in_gen.go +++ b/pkg/sql/colexec/execgen/cmd/execgen/select_in_gen.go @@ -31,8 +31,8 @@ func genSelectIn(inputFileContents string, wr io.Writer) error { ) s := r.Replace(inputFileContents) - assignEq := makeFunctionRegex("_COMPARE", 5) - s = assignEq.ReplaceAllString(s, makeTemplateFunctionCall("Compare", 5)) + compare := makeFunctionRegex("_COMPARE", 5) + s = compare.ReplaceAllString(s, makeTemplateFunctionCall("Compare", 5)) s = replaceManipulationFuncs(s) diff --git a/pkg/sql/colexec/select_in.eg.go b/pkg/sql/colexec/select_in.eg.go index cb6d6fd5040e..888980d976be 100644 --- a/pkg/sql/colexec/select_in.eg.go +++ b/pkg/sql/colexec/select_in.eg.go @@ -54,6 +54,7 @@ const ( ) func GetInProjectionOperator( + evalCtx *tree.EvalContext, allocator *colmem.Allocator, t *types.T, input colexecop.Operator, @@ -75,7 +76,7 @@ func GetInProjectionOperator( outputIdx: resultIdx, negate: negate, } - obj.filterRow, obj.hasNulls = fillDatumRowBool(t, datumTuple) + obj.filterRow, obj.hasNulls = fillDatumRowBool(evalCtx, t, datumTuple) return obj, nil } case types.BytesFamily: @@ -89,7 +90,7 @@ func GetInProjectionOperator( outputIdx: resultIdx, negate: negate, } - obj.filterRow, obj.hasNulls = fillDatumRowBytes(t, datumTuple) + obj.filterRow, obj.hasNulls = fillDatumRowBytes(evalCtx, t, datumTuple) return obj, nil } case types.DecimalFamily: @@ -103,7 +104,7 @@ func GetInProjectionOperator( outputIdx: resultIdx, negate: negate, } - obj.filterRow, obj.hasNulls = fillDatumRowDecimal(t, datumTuple) + obj.filterRow, obj.hasNulls = fillDatumRowDecimal(evalCtx, t, datumTuple) return obj, nil } case types.IntFamily: @@ -116,7 +117,7 @@ func GetInProjectionOperator( outputIdx: resultIdx, negate: negate, } - obj.filterRow, obj.hasNulls = fillDatumRowInt16(t, datumTuple) + obj.filterRow, obj.hasNulls = fillDatumRowInt16(evalCtx, t, datumTuple) return obj, nil case 32: obj := &projectInOpInt32{ @@ -126,7 +127,7 @@ func GetInProjectionOperator( outputIdx: resultIdx, negate: negate, } - obj.filterRow, obj.hasNulls = fillDatumRowInt32(t, datumTuple) + obj.filterRow, obj.hasNulls = fillDatumRowInt32(evalCtx, t, datumTuple) return obj, nil case -1: default: @@ -137,7 +138,7 @@ func GetInProjectionOperator( outputIdx: resultIdx, negate: negate, } - obj.filterRow, obj.hasNulls = fillDatumRowInt64(t, datumTuple) + obj.filterRow, obj.hasNulls = fillDatumRowInt64(evalCtx, t, datumTuple) return obj, nil } case types.FloatFamily: @@ -151,7 +152,7 @@ func GetInProjectionOperator( outputIdx: resultIdx, negate: negate, } - obj.filterRow, obj.hasNulls = fillDatumRowFloat64(t, datumTuple) + obj.filterRow, obj.hasNulls = fillDatumRowFloat64(evalCtx, t, datumTuple) return obj, nil } case types.TimestampTZFamily: @@ -165,7 +166,7 @@ func GetInProjectionOperator( outputIdx: resultIdx, negate: negate, } - obj.filterRow, obj.hasNulls = fillDatumRowTimestamp(t, datumTuple) + obj.filterRow, obj.hasNulls = fillDatumRowTimestamp(evalCtx, t, datumTuple) return obj, nil } case types.IntervalFamily: @@ -179,7 +180,7 @@ func GetInProjectionOperator( outputIdx: resultIdx, negate: negate, } - obj.filterRow, obj.hasNulls = fillDatumRowInterval(t, datumTuple) + obj.filterRow, obj.hasNulls = fillDatumRowInterval(evalCtx, t, datumTuple) return obj, nil } case types.JsonFamily: @@ -193,7 +194,7 @@ func GetInProjectionOperator( outputIdx: resultIdx, negate: negate, } - obj.filterRow, obj.hasNulls = fillDatumRowJSON(t, datumTuple) + obj.filterRow, obj.hasNulls = fillDatumRowJSON(evalCtx, t, datumTuple) return obj, nil } case typeconv.DatumVecCanonicalTypeFamily: @@ -207,7 +208,7 @@ func GetInProjectionOperator( outputIdx: resultIdx, negate: negate, } - obj.filterRow, obj.hasNulls = fillDatumRowDatum(t, datumTuple) + obj.filterRow, obj.hasNulls = fillDatumRowDatum(evalCtx, t, datumTuple) return obj, nil } } @@ -215,7 +216,12 @@ func GetInProjectionOperator( } func GetInOperator( - t *types.T, input colexecop.Operator, colIdx int, datumTuple *tree.DTuple, negate bool, + evalCtx *tree.EvalContext, + t *types.T, + input colexecop.Operator, + colIdx int, + datumTuple *tree.DTuple, + negate bool, ) (colexecop.Operator, error) { switch typeconv.TypeFamilyToCanonicalTypeFamily(t.Family()) { case types.BoolFamily: @@ -227,7 +233,7 @@ func GetInOperator( colIdx: colIdx, negate: negate, } - obj.filterRow, obj.hasNulls = fillDatumRowBool(t, datumTuple) + obj.filterRow, obj.hasNulls = fillDatumRowBool(evalCtx, t, datumTuple) return obj, nil } case types.BytesFamily: @@ -239,7 +245,7 @@ func GetInOperator( colIdx: colIdx, negate: negate, } - obj.filterRow, obj.hasNulls = fillDatumRowBytes(t, datumTuple) + obj.filterRow, obj.hasNulls = fillDatumRowBytes(evalCtx, t, datumTuple) return obj, nil } case types.DecimalFamily: @@ -251,7 +257,7 @@ func GetInOperator( colIdx: colIdx, negate: negate, } - obj.filterRow, obj.hasNulls = fillDatumRowDecimal(t, datumTuple) + obj.filterRow, obj.hasNulls = fillDatumRowDecimal(evalCtx, t, datumTuple) return obj, nil } case types.IntFamily: @@ -262,7 +268,7 @@ func GetInOperator( colIdx: colIdx, negate: negate, } - obj.filterRow, obj.hasNulls = fillDatumRowInt16(t, datumTuple) + obj.filterRow, obj.hasNulls = fillDatumRowInt16(evalCtx, t, datumTuple) return obj, nil case 32: obj := &selectInOpInt32{ @@ -270,7 +276,7 @@ func GetInOperator( colIdx: colIdx, negate: negate, } - obj.filterRow, obj.hasNulls = fillDatumRowInt32(t, datumTuple) + obj.filterRow, obj.hasNulls = fillDatumRowInt32(evalCtx, t, datumTuple) return obj, nil case -1: default: @@ -279,7 +285,7 @@ func GetInOperator( colIdx: colIdx, negate: negate, } - obj.filterRow, obj.hasNulls = fillDatumRowInt64(t, datumTuple) + obj.filterRow, obj.hasNulls = fillDatumRowInt64(evalCtx, t, datumTuple) return obj, nil } case types.FloatFamily: @@ -291,7 +297,7 @@ func GetInOperator( colIdx: colIdx, negate: negate, } - obj.filterRow, obj.hasNulls = fillDatumRowFloat64(t, datumTuple) + obj.filterRow, obj.hasNulls = fillDatumRowFloat64(evalCtx, t, datumTuple) return obj, nil } case types.TimestampTZFamily: @@ -303,7 +309,7 @@ func GetInOperator( colIdx: colIdx, negate: negate, } - obj.filterRow, obj.hasNulls = fillDatumRowTimestamp(t, datumTuple) + obj.filterRow, obj.hasNulls = fillDatumRowTimestamp(evalCtx, t, datumTuple) return obj, nil } case types.IntervalFamily: @@ -315,7 +321,7 @@ func GetInOperator( colIdx: colIdx, negate: negate, } - obj.filterRow, obj.hasNulls = fillDatumRowInterval(t, datumTuple) + obj.filterRow, obj.hasNulls = fillDatumRowInterval(evalCtx, t, datumTuple) return obj, nil } case types.JsonFamily: @@ -327,7 +333,7 @@ func GetInOperator( colIdx: colIdx, negate: negate, } - obj.filterRow, obj.hasNulls = fillDatumRowJSON(t, datumTuple) + obj.filterRow, obj.hasNulls = fillDatumRowJSON(evalCtx, t, datumTuple) return obj, nil } case typeconv.DatumVecCanonicalTypeFamily: @@ -339,7 +345,7 @@ func GetInOperator( colIdx: colIdx, negate: negate, } - obj.filterRow, obj.hasNulls = fillDatumRowDatum(t, datumTuple) + obj.filterRow, obj.hasNulls = fillDatumRowDatum(evalCtx, t, datumTuple) return obj, nil } } @@ -368,7 +374,12 @@ type projectInOpBool struct { var _ colexecop.Operator = &projectInOpBool{} -func fillDatumRowBool(t *types.T, datumTuple *tree.DTuple) ([]bool, bool) { +func fillDatumRowBool( + evalCtx *tree.EvalContext, t *types.T, datumTuple *tree.DTuple, +) ([]bool, bool) { + // Sort the contents of the tuple, if they are not already sorted. + datumTuple.Normalize(evalCtx) + conv := colconv.GetDatumToPhysicalFn(t) var result []bool hasNulls := false @@ -387,8 +398,8 @@ func fillDatumRowBool(t *types.T, datumTuple *tree.DTuple) ([]bool, bool) { func cmpInBool( targetElem bool, targetCol coldata.Bools, filterRow []bool, hasNulls bool, ) comparisonResult { - // Filter row input is already sorted due to normalization, so we can use a - // binary search right away. + // Filter row input was already sorted in fillDatumRowBool, so we can + // perform a binary search. lo := 0 hi := len(filterRow) for lo < hi { @@ -602,7 +613,12 @@ type projectInOpBytes struct { var _ colexecop.Operator = &projectInOpBytes{} -func fillDatumRowBytes(t *types.T, datumTuple *tree.DTuple) ([][]byte, bool) { +func fillDatumRowBytes( + evalCtx *tree.EvalContext, t *types.T, datumTuple *tree.DTuple, +) ([][]byte, bool) { + // Sort the contents of the tuple, if they are not already sorted. + datumTuple.Normalize(evalCtx) + conv := colconv.GetDatumToPhysicalFn(t) var result [][]byte hasNulls := false @@ -621,8 +637,8 @@ func fillDatumRowBytes(t *types.T, datumTuple *tree.DTuple) ([][]byte, bool) { func cmpInBytes( targetElem []byte, targetCol *coldata.Bytes, filterRow [][]byte, hasNulls bool, ) comparisonResult { - // Filter row input is already sorted due to normalization, so we can use a - // binary search right away. + // Filter row input was already sorted in fillDatumRowBytes, so we can + // perform a binary search. lo := 0 hi := len(filterRow) for lo < hi { @@ -824,7 +840,12 @@ type projectInOpDecimal struct { var _ colexecop.Operator = &projectInOpDecimal{} -func fillDatumRowDecimal(t *types.T, datumTuple *tree.DTuple) ([]apd.Decimal, bool) { +func fillDatumRowDecimal( + evalCtx *tree.EvalContext, t *types.T, datumTuple *tree.DTuple, +) ([]apd.Decimal, bool) { + // Sort the contents of the tuple, if they are not already sorted. + datumTuple.Normalize(evalCtx) + conv := colconv.GetDatumToPhysicalFn(t) var result []apd.Decimal hasNulls := false @@ -843,8 +864,8 @@ func fillDatumRowDecimal(t *types.T, datumTuple *tree.DTuple) ([]apd.Decimal, bo func cmpInDecimal( targetElem apd.Decimal, targetCol coldata.Decimals, filterRow []apd.Decimal, hasNulls bool, ) comparisonResult { - // Filter row input is already sorted due to normalization, so we can use a - // binary search right away. + // Filter row input was already sorted in fillDatumRowDecimal, so we can + // perform a binary search. lo := 0 hi := len(filterRow) for lo < hi { @@ -1050,7 +1071,12 @@ type projectInOpInt16 struct { var _ colexecop.Operator = &projectInOpInt16{} -func fillDatumRowInt16(t *types.T, datumTuple *tree.DTuple) ([]int16, bool) { +func fillDatumRowInt16( + evalCtx *tree.EvalContext, t *types.T, datumTuple *tree.DTuple, +) ([]int16, bool) { + // Sort the contents of the tuple, if they are not already sorted. + datumTuple.Normalize(evalCtx) + conv := colconv.GetDatumToPhysicalFn(t) var result []int16 hasNulls := false @@ -1069,8 +1095,8 @@ func fillDatumRowInt16(t *types.T, datumTuple *tree.DTuple) ([]int16, bool) { func cmpInInt16( targetElem int16, targetCol coldata.Int16s, filterRow []int16, hasNulls bool, ) comparisonResult { - // Filter row input is already sorted due to normalization, so we can use a - // binary search right away. + // Filter row input was already sorted in fillDatumRowInt16, so we can + // perform a binary search. lo := 0 hi := len(filterRow) for lo < hi { @@ -1287,7 +1313,12 @@ type projectInOpInt32 struct { var _ colexecop.Operator = &projectInOpInt32{} -func fillDatumRowInt32(t *types.T, datumTuple *tree.DTuple) ([]int32, bool) { +func fillDatumRowInt32( + evalCtx *tree.EvalContext, t *types.T, datumTuple *tree.DTuple, +) ([]int32, bool) { + // Sort the contents of the tuple, if they are not already sorted. + datumTuple.Normalize(evalCtx) + conv := colconv.GetDatumToPhysicalFn(t) var result []int32 hasNulls := false @@ -1306,8 +1337,8 @@ func fillDatumRowInt32(t *types.T, datumTuple *tree.DTuple) ([]int32, bool) { func cmpInInt32( targetElem int32, targetCol coldata.Int32s, filterRow []int32, hasNulls bool, ) comparisonResult { - // Filter row input is already sorted due to normalization, so we can use a - // binary search right away. + // Filter row input was already sorted in fillDatumRowInt32, so we can + // perform a binary search. lo := 0 hi := len(filterRow) for lo < hi { @@ -1524,7 +1555,12 @@ type projectInOpInt64 struct { var _ colexecop.Operator = &projectInOpInt64{} -func fillDatumRowInt64(t *types.T, datumTuple *tree.DTuple) ([]int64, bool) { +func fillDatumRowInt64( + evalCtx *tree.EvalContext, t *types.T, datumTuple *tree.DTuple, +) ([]int64, bool) { + // Sort the contents of the tuple, if they are not already sorted. + datumTuple.Normalize(evalCtx) + conv := colconv.GetDatumToPhysicalFn(t) var result []int64 hasNulls := false @@ -1543,8 +1579,8 @@ func fillDatumRowInt64(t *types.T, datumTuple *tree.DTuple) ([]int64, bool) { func cmpInInt64( targetElem int64, targetCol coldata.Int64s, filterRow []int64, hasNulls bool, ) comparisonResult { - // Filter row input is already sorted due to normalization, so we can use a - // binary search right away. + // Filter row input was already sorted in fillDatumRowInt64, so we can + // perform a binary search. lo := 0 hi := len(filterRow) for lo < hi { @@ -1761,7 +1797,12 @@ type projectInOpFloat64 struct { var _ colexecop.Operator = &projectInOpFloat64{} -func fillDatumRowFloat64(t *types.T, datumTuple *tree.DTuple) ([]float64, bool) { +func fillDatumRowFloat64( + evalCtx *tree.EvalContext, t *types.T, datumTuple *tree.DTuple, +) ([]float64, bool) { + // Sort the contents of the tuple, if they are not already sorted. + datumTuple.Normalize(evalCtx) + conv := colconv.GetDatumToPhysicalFn(t) var result []float64 hasNulls := false @@ -1780,8 +1821,8 @@ func fillDatumRowFloat64(t *types.T, datumTuple *tree.DTuple) ([]float64, bool) func cmpInFloat64( targetElem float64, targetCol coldata.Float64s, filterRow []float64, hasNulls bool, ) comparisonResult { - // Filter row input is already sorted due to normalization, so we can use a - // binary search right away. + // Filter row input was already sorted in fillDatumRowFloat64, so we can + // perform a binary search. lo := 0 hi := len(filterRow) for lo < hi { @@ -2006,7 +2047,12 @@ type projectInOpTimestamp struct { var _ colexecop.Operator = &projectInOpTimestamp{} -func fillDatumRowTimestamp(t *types.T, datumTuple *tree.DTuple) ([]time.Time, bool) { +func fillDatumRowTimestamp( + evalCtx *tree.EvalContext, t *types.T, datumTuple *tree.DTuple, +) ([]time.Time, bool) { + // Sort the contents of the tuple, if they are not already sorted. + datumTuple.Normalize(evalCtx) + conv := colconv.GetDatumToPhysicalFn(t) var result []time.Time hasNulls := false @@ -2025,8 +2071,8 @@ func fillDatumRowTimestamp(t *types.T, datumTuple *tree.DTuple) ([]time.Time, bo func cmpInTimestamp( targetElem time.Time, targetCol coldata.Times, filterRow []time.Time, hasNulls bool, ) comparisonResult { - // Filter row input is already sorted due to normalization, so we can use a - // binary search right away. + // Filter row input was already sorted in fillDatumRowTimestamp, so we can + // perform a binary search. lo := 0 hi := len(filterRow) for lo < hi { @@ -2239,7 +2285,12 @@ type projectInOpInterval struct { var _ colexecop.Operator = &projectInOpInterval{} -func fillDatumRowInterval(t *types.T, datumTuple *tree.DTuple) ([]duration.Duration, bool) { +func fillDatumRowInterval( + evalCtx *tree.EvalContext, t *types.T, datumTuple *tree.DTuple, +) ([]duration.Duration, bool) { + // Sort the contents of the tuple, if they are not already sorted. + datumTuple.Normalize(evalCtx) + conv := colconv.GetDatumToPhysicalFn(t) var result []duration.Duration hasNulls := false @@ -2258,8 +2309,8 @@ func fillDatumRowInterval(t *types.T, datumTuple *tree.DTuple) ([]duration.Durat func cmpInInterval( targetElem duration.Duration, targetCol coldata.Durations, filterRow []duration.Duration, hasNulls bool, ) comparisonResult { - // Filter row input is already sorted due to normalization, so we can use a - // binary search right away. + // Filter row input was already sorted in fillDatumRowInterval, so we can + // perform a binary search. lo := 0 hi := len(filterRow) for lo < hi { @@ -2465,7 +2516,12 @@ type projectInOpJSON struct { var _ colexecop.Operator = &projectInOpJSON{} -func fillDatumRowJSON(t *types.T, datumTuple *tree.DTuple) ([]json.JSON, bool) { +func fillDatumRowJSON( + evalCtx *tree.EvalContext, t *types.T, datumTuple *tree.DTuple, +) ([]json.JSON, bool) { + // Sort the contents of the tuple, if they are not already sorted. + datumTuple.Normalize(evalCtx) + conv := colconv.GetDatumToPhysicalFn(t) var result []json.JSON hasNulls := false @@ -2484,8 +2540,8 @@ func fillDatumRowJSON(t *types.T, datumTuple *tree.DTuple) ([]json.JSON, bool) { func cmpInJSON( targetElem json.JSON, targetCol *coldata.JSONs, filterRow []json.JSON, hasNulls bool, ) comparisonResult { - // Filter row input is already sorted due to normalization, so we can use a - // binary search right away. + // Filter row input was already sorted in fillDatumRowJSON, so we can + // perform a binary search. lo := 0 hi := len(filterRow) for lo < hi { @@ -2693,7 +2749,12 @@ type projectInOpDatum struct { var _ colexecop.Operator = &projectInOpDatum{} -func fillDatumRowDatum(t *types.T, datumTuple *tree.DTuple) ([]interface{}, bool) { +func fillDatumRowDatum( + evalCtx *tree.EvalContext, t *types.T, datumTuple *tree.DTuple, +) ([]interface{}, bool) { + // Sort the contents of the tuple, if they are not already sorted. + datumTuple.Normalize(evalCtx) + conv := colconv.GetDatumToPhysicalFn(t) var result []interface{} hasNulls := false @@ -2712,8 +2773,8 @@ func fillDatumRowDatum(t *types.T, datumTuple *tree.DTuple) ([]interface{}, bool func cmpInDatum( targetElem interface{}, targetCol coldata.DatumVec, filterRow []interface{}, hasNulls bool, ) comparisonResult { - // Filter row input is already sorted due to normalization, so we can use a - // binary search right away. + // Filter row input was already sorted in fillDatumRowDatum, so we can + // perform a binary search. lo := 0 hi := len(filterRow) for lo < hi { diff --git a/pkg/sql/colexec/select_in_test.go b/pkg/sql/colexec/select_in_test.go index 7535fdb1b9a6..d93d7d6eaacb 100644 --- a/pkg/sql/colexec/select_in_test.go +++ b/pkg/sql/colexec/select_in_test.go @@ -70,6 +70,14 @@ func TestSelectInInt64(t *testing.T) { hasNulls: true, negate: true, }, + { + desc: "In test with unordered filterRow", + inputTuples: colexectestutils.Tuples{{0}, {1}, {2}}, + outputTuples: colexectestutils.Tuples{{0}, {1}, {2}}, + filterRow: []int64{2, 0, 1}, + hasNulls: false, + negate: false, + }, } for _, c := range testCases { @@ -211,6 +219,12 @@ func TestProjectInInt64(t *testing.T) { outputTuples: colexectestutils.Tuples{{1, false}, {2, false}}, inClause: "IN (3)", }, + { + desc: "In test with unordered tuple", + inputTuples: colexectestutils.Tuples{{0}, {1}, {2}}, + outputTuples: colexectestutils.Tuples{{0, true}, {1, true}, {2, true}}, + inClause: "IN (2, 0, 1)", + }, } for _, c := range testCases { diff --git a/pkg/sql/colexec/select_in_tmpl.go b/pkg/sql/colexec/select_in_tmpl.go index 244070893ed8..0c20776649ac 100644 --- a/pkg/sql/colexec/select_in_tmpl.go +++ b/pkg/sql/colexec/select_in_tmpl.go @@ -78,6 +78,7 @@ const ( ) func GetInProjectionOperator( + evalCtx *tree.EvalContext, allocator *colmem.Allocator, t *types.T, input colexecop.Operator, @@ -100,7 +101,7 @@ func GetInProjectionOperator( outputIdx: resultIdx, negate: negate, } - obj.filterRow, obj.hasNulls = fillDatumRow_TYPE(t, datumTuple) + obj.filterRow, obj.hasNulls = fillDatumRow_TYPE(evalCtx, t, datumTuple) return obj, nil // {{end}} } @@ -110,7 +111,12 @@ func GetInProjectionOperator( } func GetInOperator( - t *types.T, input colexecop.Operator, colIdx int, datumTuple *tree.DTuple, negate bool, + evalCtx *tree.EvalContext, + t *types.T, + input colexecop.Operator, + colIdx int, + datumTuple *tree.DTuple, + negate bool, ) (colexecop.Operator, error) { switch typeconv.TypeFamilyToCanonicalTypeFamily(t.Family()) { // {{range .}} @@ -123,7 +129,7 @@ func GetInOperator( colIdx: colIdx, negate: negate, } - obj.filterRow, obj.hasNulls = fillDatumRow_TYPE(t, datumTuple) + obj.filterRow, obj.hasNulls = fillDatumRow_TYPE(evalCtx, t, datumTuple) return obj, nil // {{end}} } @@ -157,7 +163,12 @@ type projectInOp_TYPE struct { var _ colexecop.Operator = &projectInOp_TYPE{} -func fillDatumRow_TYPE(t *types.T, datumTuple *tree.DTuple) ([]_GOTYPE, bool) { +func fillDatumRow_TYPE( + evalCtx *tree.EvalContext, t *types.T, datumTuple *tree.DTuple, +) ([]_GOTYPE, bool) { + // Sort the contents of the tuple, if they are not already sorted. + datumTuple.Normalize(evalCtx) + conv := colconv.GetDatumToPhysicalFn(t) var result []_GOTYPE hasNulls := false @@ -176,8 +187,8 @@ func fillDatumRow_TYPE(t *types.T, datumTuple *tree.DTuple) ([]_GOTYPE, bool) { func cmpIn_TYPE( targetElem _GOTYPE, targetCol _GOTYPESLICE, filterRow []_GOTYPE, hasNulls bool, ) comparisonResult { - // Filter row input is already sorted due to normalization, so we can use a - // binary search right away. + // Filter row input was already sorted in fillDatumRow_TYPE, so we can + // perform a binary search. lo := 0 hi := len(filterRow) for lo < hi { diff --git a/pkg/sql/logictest/testdata/logic_test/vectorize b/pkg/sql/logictest/testdata/logic_test/vectorize index a1f5856c9570..ef6d84316a4d 100644 --- a/pkg/sql/logictest/testdata/logic_test/vectorize +++ b/pkg/sql/logictest/testdata/logic_test/vectorize @@ -1253,3 +1253,18 @@ query T SELECT c FROM t68040 WHERE c LIKE '%\\%' ---- string with \ backslash + +# Regression test for #68979. The IN operator should evaluate correctly when the +# tuple contents are not sorted by the optimizer. +statement ok +CREATE TABLE t68979 ( + a INT +) + +statement ok +INSERT INTO t68979 VALUES (0) + +query B +SELECT 'b' IN ('b', (SELECT NULL FROM t68979), 'a') FROM t68979 +---- +true diff --git a/pkg/sql/sem/tree/datum.go b/pkg/sql/sem/tree/datum.go index 7ea27c03772d..a21094d61b97 100644 --- a/pkg/sql/sem/tree/datum.go +++ b/pkg/sql/sem/tree/datum.go @@ -3745,9 +3745,16 @@ func (d *DTuple) Normalize(ctx *EvalContext) { func (d *DTuple) sort(ctx *EvalContext) { if !d.sorted { - sort.Slice(d.D, func(i, j int) bool { + lessFn := func(i, j int) bool { return d.D[i].Compare(ctx, d.D[j]) < 0 - }) + } + + // It is possible for the tuple to be sorted even though the sorted flag + // is not true. So before we perform the sort we check that it is not + // already sorted. + if !sort.SliceIsSorted(d.D, lessFn) { + sort.Slice(d.D, lessFn) + } d.SetSorted() } }