github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/colexec/and_or_projection_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 "testing" 17 18 "github.com/cockroachdb/cockroach/pkg/col/coldata" 19 "github.com/cockroachdb/cockroach/pkg/settings/cluster" 20 "github.com/cockroachdb/cockroach/pkg/sql/colexecbase" 21 "github.com/cockroachdb/cockroach/pkg/sql/execinfra" 22 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 23 "github.com/cockroachdb/cockroach/pkg/sql/types" 24 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 25 "github.com/cockroachdb/cockroach/pkg/util/randutil" 26 "github.com/stretchr/testify/require" 27 ) 28 29 type andOrTestCase struct { 30 tuples []tuple 31 expected []tuple 32 skipAllNullsInjection bool 33 } 34 35 var ( 36 andTestCases []andOrTestCase 37 orTestCases []andOrTestCase 38 ) 39 40 func init() { 41 andTestCases = []andOrTestCase{ 42 // All variations of pairs separately first. 43 { 44 tuples: tuples{{false, true}}, 45 expected: tuples{{false}}, 46 }, 47 { 48 tuples: tuples{{false, nil}}, 49 expected: tuples{{false}}, 50 }, 51 { 52 tuples: tuples{{false, false}}, 53 expected: tuples{{false}}, 54 }, 55 { 56 tuples: tuples{{true, true}}, 57 expected: tuples{{true}}, 58 }, 59 { 60 tuples: tuples{{true, false}}, 61 expected: tuples{{false}}, 62 }, 63 { 64 tuples: tuples{{true, nil}}, 65 expected: tuples{{nil}}, 66 // The case of {nil, nil} is explicitly tested below. 67 skipAllNullsInjection: true, 68 }, 69 { 70 tuples: tuples{{nil, true}}, 71 expected: tuples{{nil}}, 72 // The case of {nil, nil} is explicitly tested below. 73 skipAllNullsInjection: true, 74 }, 75 { 76 tuples: tuples{{nil, false}}, 77 expected: tuples{{false}}, 78 }, 79 { 80 tuples: tuples{{nil, nil}}, 81 expected: tuples{{nil}}, 82 }, 83 // Now all variations of pairs combined together to make sure that nothing 84 // funky going on with multiple tuples. 85 { 86 tuples: tuples{ 87 {false, true}, {false, nil}, {false, false}, 88 {true, true}, {true, false}, {true, nil}, 89 {nil, true}, {nil, false}, {nil, nil}, 90 }, 91 expected: tuples{ 92 {false}, {false}, {false}, 93 {true}, {false}, {nil}, 94 {nil}, {false}, {nil}, 95 }, 96 }, 97 } 98 99 orTestCases = []andOrTestCase{ 100 // All variations of pairs separately first. 101 { 102 tuples: tuples{{false, true}}, 103 expected: tuples{{true}}, 104 }, 105 { 106 tuples: tuples{{false, nil}}, 107 expected: tuples{{nil}}, 108 // The case of {nil, nil} is explicitly tested below. 109 skipAllNullsInjection: true, 110 }, 111 { 112 tuples: tuples{{false, false}}, 113 expected: tuples{{false}}, 114 }, 115 { 116 tuples: tuples{{true, true}}, 117 expected: tuples{{true}}, 118 }, 119 { 120 tuples: tuples{{true, false}}, 121 expected: tuples{{true}}, 122 }, 123 { 124 tuples: tuples{{true, nil}}, 125 expected: tuples{{true}}, 126 }, 127 { 128 tuples: tuples{{nil, true}}, 129 expected: tuples{{true}}, 130 }, 131 { 132 tuples: tuples{{nil, false}}, 133 expected: tuples{{nil}}, 134 // The case of {nil, nil} is explicitly tested below. 135 skipAllNullsInjection: true, 136 }, 137 { 138 tuples: tuples{{nil, nil}}, 139 expected: tuples{{nil}}, 140 }, 141 // Now all variations of pairs combined together to make sure that nothing 142 // funky going on with multiple tuples. 143 { 144 tuples: tuples{ 145 {false, true}, {false, nil}, {false, false}, 146 {true, true}, {true, false}, {true, nil}, 147 {nil, true}, {nil, false}, {nil, nil}, 148 }, 149 expected: tuples{ 150 {true}, {nil}, {false}, 151 {true}, {true}, {true}, 152 {true}, {nil}, {nil}, 153 }, 154 }, 155 } 156 } 157 158 func TestAndOrOps(t *testing.T) { 159 defer leaktest.AfterTest(t)() 160 ctx := context.Background() 161 st := cluster.MakeTestingClusterSettings() 162 evalCtx := tree.MakeTestingEvalContext(st) 163 defer evalCtx.Stop(ctx) 164 flowCtx := &execinfra.FlowCtx{ 165 EvalCtx: &evalCtx, 166 Cfg: &execinfra.ServerConfig{ 167 Settings: st, 168 }, 169 } 170 171 for _, test := range []struct { 172 operation string 173 cases []andOrTestCase 174 }{ 175 { 176 operation: "AND", 177 cases: andTestCases, 178 }, 179 { 180 operation: "OR", 181 cases: orTestCases, 182 }, 183 } { 184 t.Run(test.operation, func(t *testing.T) { 185 for _, tc := range test.cases { 186 var runner testRunner 187 if tc.skipAllNullsInjection { 188 // We're omitting all nulls injection test. See comments for each such 189 // test case. 190 runner = runTestsWithoutAllNullsInjection 191 } else { 192 runner = runTestsWithTyps 193 } 194 runner( 195 t, 196 []tuples{tc.tuples}, 197 [][]*types.T{{types.Bool, types.Bool}}, 198 tc.expected, 199 orderedVerifier, 200 func(input []colexecbase.Operator) (colexecbase.Operator, error) { 201 projOp, err := createTestProjectingOperator( 202 ctx, flowCtx, input[0], []*types.T{types.Bool, types.Bool}, 203 fmt.Sprintf("@1 %s @2", test.operation), false, /* canFallbackToRowexec */ 204 ) 205 if err != nil { 206 return nil, err 207 } 208 // We will project out the first two columns in order 209 // to have test cases be less verbose. 210 return NewSimpleProjectOp(projOp, 3 /* numInputCols */, []uint32{2}), nil 211 }) 212 } 213 }) 214 } 215 } 216 217 func benchmarkLogicalProjOp( 218 b *testing.B, operation string, useSelectionVector bool, hasNulls bool, 219 ) { 220 ctx := context.Background() 221 st := cluster.MakeTestingClusterSettings() 222 evalCtx := tree.MakeTestingEvalContext(st) 223 defer evalCtx.Stop(ctx) 224 flowCtx := &execinfra.FlowCtx{ 225 EvalCtx: &evalCtx, 226 Cfg: &execinfra.ServerConfig{ 227 Settings: st, 228 }, 229 } 230 rng, _ := randutil.NewPseudoRand() 231 232 batch := testAllocator.NewMemBatch([]*types.T{types.Bool, types.Bool}) 233 col1 := batch.ColVec(0).Bool() 234 col2 := batch.ColVec(0).Bool() 235 for i := 0; i < coldata.BatchSize(); i++ { 236 col1[i] = rng.Float64() < 0.5 237 col2[i] = rng.Float64() < 0.5 238 } 239 if hasNulls { 240 nulls1 := batch.ColVec(0).Nulls() 241 nulls2 := batch.ColVec(0).Nulls() 242 for i := 0; i < coldata.BatchSize(); i++ { 243 if rng.Float64() < nullProbability { 244 nulls1.SetNull(i) 245 } 246 if rng.Float64() < nullProbability { 247 nulls2.SetNull(i) 248 } 249 } 250 } 251 batch.SetLength(coldata.BatchSize()) 252 if useSelectionVector { 253 batch.SetSelection(true) 254 sel := batch.Selection() 255 for i := 0; i < coldata.BatchSize(); i++ { 256 sel[i] = i 257 } 258 } 259 typs := []*types.T{types.Bool, types.Bool} 260 input := colexecbase.NewRepeatableBatchSource(testAllocator, batch, typs) 261 logicalProjOp, err := createTestProjectingOperator( 262 ctx, flowCtx, input, typs, 263 fmt.Sprintf("@1 %s @2", operation), false, /* canFallbackToRowexec */ 264 ) 265 require.NoError(b, err) 266 logicalProjOp.Init() 267 268 b.SetBytes(int64(8 * coldata.BatchSize())) 269 for i := 0; i < b.N; i++ { 270 logicalProjOp.Next(ctx) 271 } 272 } 273 274 func BenchmarkLogicalProjOp(b *testing.B) { 275 for _, operation := range []string{"AND", "OR"} { 276 for _, useSel := range []bool{true, false} { 277 for _, hasNulls := range []bool{true, false} { 278 b.Run(fmt.Sprintf("%s,useSel=%t,hasNulls=%t", operation, useSel, hasNulls), func(b *testing.B) { 279 benchmarkLogicalProjOp(b, operation, useSel, hasNulls) 280 }) 281 } 282 } 283 } 284 }