github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/colexec/select_in_test.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package colexec 12 13 import ( 14 "context" 15 "fmt" 16 "math/rand" 17 "testing" 18 19 "github.com/cockroachdb/cockroach/pkg/col/coldata" 20 "github.com/cockroachdb/cockroach/pkg/settings/cluster" 21 "github.com/cockroachdb/cockroach/pkg/sql/colexecbase" 22 "github.com/cockroachdb/cockroach/pkg/sql/execinfra" 23 "github.com/cockroachdb/cockroach/pkg/sql/execinfrapb" 24 "github.com/cockroachdb/cockroach/pkg/sql/parser" 25 "github.com/cockroachdb/cockroach/pkg/sql/rowexec" 26 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 27 "github.com/cockroachdb/cockroach/pkg/sql/types" 28 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 29 ) 30 31 func TestSelectInInt64(t *testing.T) { 32 defer leaktest.AfterTest(t)() 33 testCases := []struct { 34 desc string 35 inputTuples tuples 36 outputTuples tuples 37 filterRow []int64 38 hasNulls bool 39 negate bool 40 }{ 41 { 42 desc: "Simple in test", 43 inputTuples: tuples{{0}, {1}, {2}}, 44 outputTuples: tuples{{0}, {1}}, 45 filterRow: []int64{0, 1}, 46 hasNulls: false, 47 negate: false, 48 }, 49 { 50 desc: "Simple not in test", 51 inputTuples: tuples{{0}, {1}, {2}}, 52 outputTuples: tuples{{2}}, 53 filterRow: []int64{0, 1}, 54 hasNulls: false, 55 negate: true, 56 }, 57 { 58 desc: "In test with NULLs", 59 inputTuples: tuples{{nil}, {1}, {2}}, 60 outputTuples: tuples{{1}}, 61 filterRow: []int64{1}, 62 hasNulls: true, 63 negate: false, 64 }, 65 { 66 desc: "Not in test with NULLs", 67 inputTuples: tuples{{nil}, {1}, {2}}, 68 outputTuples: tuples{}, 69 filterRow: []int64{1}, 70 hasNulls: true, 71 negate: true, 72 }, 73 } 74 75 for _, c := range testCases { 76 t.Run(c.desc, func(t *testing.T) { 77 opConstructor := func(input []colexecbase.Operator) (colexecbase.Operator, error) { 78 op := selectInOpInt64{ 79 OneInputNode: NewOneInputNode(input[0]), 80 colIdx: 0, 81 filterRow: c.filterRow, 82 negate: c.negate, 83 hasNulls: c.hasNulls, 84 } 85 return &op, nil 86 } 87 if !c.hasNulls || !c.negate { 88 runTests(t, []tuples{c.inputTuples}, c.outputTuples, orderedVerifier, opConstructor) 89 } else { 90 // When the input tuples already have nulls and we have NOT IN 91 // operator, then the nulls injection might not change the output. For 92 // example, we have this test case "1 NOT IN (NULL, 1, 2)" with the 93 // output of length 0; similarly, we will get the same zero-length 94 // output for the corresponding nulls injection test case 95 // "1 NOT IN (NULL, NULL, NULL)". 96 runTestsWithoutAllNullsInjection(t, []tuples{c.inputTuples}, nil /* typs */, c.outputTuples, orderedVerifier, opConstructor) 97 } 98 }) 99 } 100 } 101 102 func benchmarkSelectInInt64(b *testing.B, useSelectionVector bool, hasNulls bool) { 103 ctx := context.Background() 104 typs := []*types.T{types.Int} 105 batch := testAllocator.NewMemBatch(typs) 106 col1 := batch.ColVec(0).Int64() 107 108 for i := 0; i < coldata.BatchSize(); i++ { 109 if float64(i) < float64(coldata.BatchSize())*selectivity { 110 col1[i] = -1 111 } else { 112 col1[i] = 1 113 } 114 } 115 116 if hasNulls { 117 for i := 0; i < coldata.BatchSize(); i++ { 118 if rand.Float64() < nullProbability { 119 batch.ColVec(0).Nulls().SetNull(i) 120 } 121 } 122 } 123 124 batch.SetLength(coldata.BatchSize()) 125 126 if useSelectionVector { 127 batch.SetSelection(true) 128 sel := batch.Selection() 129 for i := 0; i < coldata.BatchSize(); i++ { 130 sel[i] = i 131 } 132 } 133 134 source := colexecbase.NewRepeatableBatchSource(testAllocator, batch, typs) 135 source.Init() 136 inOp := &selectInOpInt64{ 137 OneInputNode: NewOneInputNode(source), 138 colIdx: 0, 139 filterRow: []int64{1, 2, 3}, 140 } 141 inOp.Init() 142 143 b.SetBytes(int64(8 * coldata.BatchSize())) 144 b.ResetTimer() 145 for i := 0; i < b.N; i++ { 146 inOp.Next(ctx) 147 } 148 } 149 150 func BenchmarkSelectInInt64(b *testing.B) { 151 for _, useSel := range []bool{true, false} { 152 for _, hasNulls := range []bool{true, false} { 153 b.Run(fmt.Sprintf("useSel=%t,hasNulls=%t", useSel, hasNulls), func(b *testing.B) { 154 benchmarkSelectInInt64(b, useSel, hasNulls) 155 }) 156 } 157 } 158 } 159 160 func TestProjectInInt64(t *testing.T) { 161 defer leaktest.AfterTest(t)() 162 ctx := context.Background() 163 st := cluster.MakeTestingClusterSettings() 164 evalCtx := tree.MakeTestingEvalContext(st) 165 defer evalCtx.Stop(ctx) 166 flowCtx := &execinfra.FlowCtx{ 167 EvalCtx: &evalCtx, 168 Cfg: &execinfra.ServerConfig{ 169 Settings: st, 170 }, 171 } 172 testCases := []struct { 173 desc string 174 inputTuples tuples 175 outputTuples tuples 176 inClause string 177 }{ 178 { 179 desc: "Simple in test", 180 inputTuples: tuples{{0}, {1}}, 181 outputTuples: tuples{{0, true}, {1, true}}, 182 inClause: "IN (0, 1)", 183 }, 184 { 185 desc: "Simple not in test", 186 inputTuples: tuples{{2}}, 187 outputTuples: tuples{{2, true}}, 188 inClause: "NOT IN (0, 1)", 189 }, 190 { 191 desc: "In test with NULLs", 192 inputTuples: tuples{{1}, {2}, {nil}}, 193 outputTuples: tuples{{1, true}, {2, nil}, {nil, nil}}, 194 inClause: "IN (1, NULL)", 195 }, 196 { 197 desc: "Not in test with NULLs", 198 inputTuples: tuples{{1}, {2}, {nil}}, 199 outputTuples: tuples{{1, false}, {2, nil}, {nil, nil}}, 200 inClause: "NOT IN (1, NULL)", 201 }, 202 { 203 desc: "Not in test with NULLs and no nulls in filter", 204 inputTuples: tuples{{1}, {2}, {nil}}, 205 outputTuples: tuples{{1, false}, {2, true}, {nil, nil}}, 206 inClause: "NOT IN (1)", 207 }, 208 { 209 desc: "Test with false values", 210 inputTuples: tuples{{1}, {2}}, 211 outputTuples: tuples{{1, false}, {2, false}}, 212 inClause: "IN (3)", 213 }, 214 } 215 216 for _, c := range testCases { 217 t.Run(c.desc, func(t *testing.T) { 218 runTests(t, []tuples{c.inputTuples}, c.outputTuples, orderedVerifier, 219 func(input []colexecbase.Operator) (colexecbase.Operator, error) { 220 expr, err := parser.ParseExpr(fmt.Sprintf("@1 %s", c.inClause)) 221 if err != nil { 222 return nil, err 223 } 224 p := &mockTypeContext{typs: []*types.T{types.Int, types.MakeTuple([]*types.T{types.Int})}} 225 semaCtx := tree.MakeSemaContext() 226 semaCtx.IVarContainer = p 227 typedExpr, err := tree.TypeCheck(ctx, expr, &semaCtx, types.Any) 228 if err != nil { 229 return nil, err 230 } 231 spec := &execinfrapb.ProcessorSpec{ 232 Input: []execinfrapb.InputSyncSpec{{ColumnTypes: []*types.T{types.Int}}}, 233 Core: execinfrapb.ProcessorCoreUnion{ 234 Noop: &execinfrapb.NoopCoreSpec{}, 235 }, 236 Post: execinfrapb.PostProcessSpec{ 237 RenderExprs: []execinfrapb.Expression{ 238 {Expr: "@1"}, 239 {LocalExpr: typedExpr}, 240 }, 241 }, 242 } 243 args := NewColOperatorArgs{ 244 Spec: spec, 245 Inputs: input, 246 StreamingMemAccount: testMemAcc, 247 // TODO(yuzefovich): figure out how to make the second 248 // argument of IN comparison as DTuple not Tuple. 249 // TODO(yuzefovich): reuse createTestProjectingOperator 250 // once we don't need to provide the processor 251 // constructor. 252 ProcessorConstructor: rowexec.NewProcessor, 253 } 254 args.TestingKnobs.UseStreamingMemAccountForBuffering = true 255 result, err := NewColOperator(ctx, flowCtx, args) 256 if err != nil { 257 return nil, err 258 } 259 return result.Op, nil 260 }) 261 }) 262 } 263 }