github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/colexec/and_or_projection_tmpl.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 // {{/* 12 // +build execgen_template 13 // 14 // This file is the execgen template for and_or_projection.eg.go. It's 15 // formatted in a special way, so it's both valid Go and a valid text/template 16 // input. This permits editing this file with editor support. 17 // 18 // */}} 19 20 package colexec 21 22 import ( 23 "context" 24 "fmt" 25 26 "github.com/cockroachdb/cockroach/pkg/col/coldata" 27 "github.com/cockroachdb/cockroach/pkg/sql/colexecbase" 28 "github.com/cockroachdb/cockroach/pkg/sql/colexecbase/colexecerror" 29 "github.com/cockroachdb/cockroach/pkg/sql/colmem" 30 "github.com/cockroachdb/cockroach/pkg/sql/execinfra" 31 ) 32 33 // {{range .}} 34 35 type _OP_LOWERProjOp struct { 36 allocator *colmem.Allocator 37 input colexecbase.Operator 38 39 leftProjOpChain colexecbase.Operator 40 rightProjOpChain colexecbase.Operator 41 leftFeedOp *feedOperator 42 rightFeedOp *feedOperator 43 44 leftIdx int 45 rightIdx int 46 outputIdx int 47 48 // origSel is a buffer used to keep track of the original selection vector of 49 // the input batch. We need to do this because we're going to modify the 50 // selection vector in order to do the short-circuiting of logical operators. 51 origSel []int 52 } 53 54 // New_OP_TITLEProjOp returns a new projection operator that logical-_OP_TITLE's 55 // the boolean columns at leftIdx and rightIdx, returning the result in 56 // outputIdx. 57 func New_OP_TITLEProjOp( 58 allocator *colmem.Allocator, 59 input, leftProjOpChain, rightProjOpChain colexecbase.Operator, 60 leftFeedOp, rightFeedOp *feedOperator, 61 leftIdx, rightIdx, outputIdx int, 62 ) colexecbase.Operator { 63 return &_OP_LOWERProjOp{ 64 allocator: allocator, 65 input: input, 66 leftProjOpChain: leftProjOpChain, 67 rightProjOpChain: rightProjOpChain, 68 leftFeedOp: leftFeedOp, 69 rightFeedOp: rightFeedOp, 70 leftIdx: leftIdx, 71 rightIdx: rightIdx, 72 outputIdx: outputIdx, 73 origSel: make([]int, coldata.BatchSize()), 74 } 75 } 76 77 func (o *_OP_LOWERProjOp) ChildCount(verbose bool) int { 78 return 3 79 } 80 81 func (o *_OP_LOWERProjOp) Child(nth int, verbose bool) execinfra.OpNode { 82 switch nth { 83 case 0: 84 return o.input 85 case 1: 86 return o.leftProjOpChain 87 case 2: 88 return o.rightProjOpChain 89 default: 90 colexecerror.InternalError(fmt.Sprintf("invalid idx %d", nth)) 91 // This code is unreachable, but the compiler cannot infer that. 92 return nil 93 } 94 } 95 96 func (o *_OP_LOWERProjOp) Init() { 97 o.input.Init() 98 } 99 100 // Next is part of the Operator interface. 101 // The idea to handle the short-circuiting logic is similar to what caseOp 102 // does: a logical operator has an input and two projection chains. First, 103 // it runs the left chain on the input batch. Then, it "subtracts" the 104 // tuples for which we know the result of logical operation based only on 105 // the left side projection (e.g. if the left side is false and we're 106 // doing AND operation, then the result is also false) and runs the right 107 // side projection only on the remaining tuples (i.e. those that were not 108 // "subtracted"). Next, it restores the original selection vector and 109 // populates the result of the logical operation. 110 func (o *_OP_LOWERProjOp) Next(ctx context.Context) coldata.Batch { 111 batch := o.input.Next(ctx) 112 origLen := batch.Length() 113 if origLen == 0 { 114 return coldata.ZeroBatch 115 } 116 usesSel := false 117 if sel := batch.Selection(); sel != nil { 118 copy(o.origSel[:origLen], sel[:origLen]) 119 usesSel = true 120 } 121 122 // In order to support the short-circuiting logic, we need to be quite tricky 123 // here. First, we set the input batch for the left projection to run and 124 // actually run the projection. 125 o.leftFeedOp.batch = batch 126 batch = o.leftProjOpChain.Next(ctx) 127 128 // Now we need to populate a selection vector on the batch in such a way that 129 // those tuples that we already know the result of logical operation for do 130 // not get the projection for the right side. 131 // 132 // knownResult indicates the boolean value which if present on the left side 133 // fully determines the result of the logical operation. 134 var ( 135 knownResult bool 136 isLeftNull, isRightNull bool 137 ) 138 // {{if _IS_OR_OP}} 139 knownResult = true 140 // {{end}} 141 leftCol := batch.ColVec(o.leftIdx) 142 leftColVals := leftCol.Bool() 143 var curIdx int 144 if usesSel { 145 sel := batch.Selection() 146 origSel := o.origSel[:origLen] 147 if leftCol.MaybeHasNulls() { 148 leftNulls := leftCol.Nulls() 149 for _, i := range origSel { 150 _ADD_TUPLE_FOR_RIGHT(true) 151 } 152 } else { 153 for _, i := range origSel { 154 _ADD_TUPLE_FOR_RIGHT(false) 155 } 156 } 157 } else { 158 batch.SetSelection(true) 159 sel := batch.Selection() 160 if leftCol.MaybeHasNulls() { 161 leftNulls := leftCol.Nulls() 162 for i := 0; i < origLen; i++ { 163 _ADD_TUPLE_FOR_RIGHT(true) 164 } 165 } else { 166 for i := 0; i < origLen; i++ { 167 _ADD_TUPLE_FOR_RIGHT(false) 168 } 169 } 170 } 171 172 var ranRightSide bool 173 if curIdx > 0 { 174 // We only run the right-side projection if there are non-zero number of 175 // remaining tuples. 176 batch.SetLength(curIdx) 177 o.rightFeedOp.batch = batch 178 batch = o.rightProjOpChain.Next(ctx) 179 ranRightSide = true 180 } 181 182 // Now we need to restore the original selection vector and length. 183 if usesSel { 184 sel := batch.Selection() 185 copy(sel[:origLen], o.origSel[:origLen]) 186 } else { 187 batch.SetSelection(false) 188 } 189 batch.SetLength(origLen) 190 191 var ( 192 rightCol coldata.Vec 193 rightColVals []bool 194 ) 195 if ranRightSide { 196 rightCol = batch.ColVec(o.rightIdx) 197 rightColVals = rightCol.Bool() 198 } 199 outputCol := batch.ColVec(o.outputIdx) 200 outputColVals := outputCol.Bool() 201 outputNulls := outputCol.Nulls() 202 if outputCol.MaybeHasNulls() { 203 // We need to make sure that there are no left over null values in the 204 // output vector. 205 outputNulls.UnsetNulls() 206 } 207 // This is where we populate the output - do the actual evaluation of the 208 // logical operation. 209 if leftCol.MaybeHasNulls() { 210 leftNulls := leftCol.Nulls() 211 if rightCol != nil && rightCol.MaybeHasNulls() { 212 rightNulls := rightCol.Nulls() 213 _SET_VALUES(_IS_OR_OP, true, true) 214 } else { 215 _SET_VALUES(_IS_OR_OP, true, false) 216 } 217 } else { 218 if rightCol != nil && rightCol.MaybeHasNulls() { 219 rightNulls := rightCol.Nulls() 220 _SET_VALUES(_IS_OR_OP, false, true) 221 } else { 222 _SET_VALUES(_IS_OR_OP, false, false) 223 } 224 } 225 226 return batch 227 } 228 229 // {{end}} 230 231 // {{/* 232 // This code snippet decides whether to include the tuple with index i into 233 // the selection vector to be used by the right side projection. The tuple is 234 // excluded if we already know the result of logical operation (i.e. we do the 235 // short-circuiting for it). 236 func _ADD_TUPLE_FOR_RIGHT(_L_HAS_NULLS bool) { // */}} 237 // {{define "addTupleForRight" -}} 238 // {{if _L_HAS_NULLS}} 239 isLeftNull = leftNulls.NullAt(i) 240 // {{else}} 241 isLeftNull = false 242 // {{end}} 243 if isLeftNull || leftColVals[i] != knownResult { 244 // We add the tuple into the selection vector if the left value is NULL or 245 // it is different from knownResult. 246 sel[curIdx] = i 247 curIdx++ 248 } 249 // {{end}} 250 // {{/* 251 } 252 253 // */}} 254 255 // {{/* 256 // This code snippet sets the result of applying a logical operation AND or OR 257 // to two boolean vectors while paying attention to null values. 258 func _SET_VALUES(_IS_OR_OP bool, _L_HAS_NULLS bool, _R_HAS_NULLS bool) { // */}} 259 // {{define "setValues" -}} 260 if sel := batch.Selection(); sel != nil { 261 for _, idx := range sel[:origLen] { 262 _SET_SINGLE_VALUE(_IS_OR_OP, _L_HAS_NULLS, _R_HAS_NULLS) 263 } 264 } else { 265 if ranRightSide { 266 _ = rightColVals[origLen-1] 267 } 268 _ = outputColVals[origLen-1] 269 for idx := range leftColVals[:origLen] { 270 _SET_SINGLE_VALUE(_IS_OR_OP, _L_HAS_NULLS, _R_HAS_NULLS) 271 } 272 } 273 // {{end}} 274 // {{/* 275 } 276 277 // */}} 278 279 // {{/* 280 // This code snippet sets the result of applying a logical operation AND or OR 281 // to two boolean values which can be null. 282 func _SET_SINGLE_VALUE(_IS_OR_OP bool, _L_HAS_NULLS bool, _R_HAS_NULLS bool) { // */}} 283 // {{define "setSingleValue" -}} 284 // {{if _L_HAS_NULLS}} 285 isLeftNull = leftNulls.NullAt(idx) 286 // {{else}} 287 isLeftNull = false 288 // {{end}} 289 leftVal := leftColVals[idx] 290 if !isLeftNull && leftVal == knownResult { 291 outputColVals[idx] = leftVal 292 } else { 293 // {{if _R_HAS_NULLS}} 294 isRightNull = rightNulls.NullAt(idx) 295 // {{else}} 296 isRightNull = false 297 // {{end}} 298 rightVal := rightColVals[idx] 299 // {{if _IS_OR_OP}} 300 // The rules for OR'ing two booleans are: 301 // 1. if at least one of the values is TRUE, then the result is also TRUE 302 // 2. if both values are FALSE, then the result is also FALSE 303 // 3. in all other cases (one is FALSE and the other is NULL or both are NULL), 304 // the result is NULL. 305 if (leftVal && !isLeftNull) || (rightVal && !isRightNull) { 306 // Rule 1: at least one boolean is TRUE. 307 outputColVals[idx] = true 308 } else if (!leftVal && !isLeftNull) && (!rightVal && !isRightNull) { 309 // Rule 2: both booleans are FALSE. 310 outputColVals[idx] = false 311 } else { 312 // Rule 3. 313 outputNulls.SetNull(idx) 314 } 315 // {{else}} 316 // The rules for AND'ing two booleans are: 317 // 1. if at least one of the values is FALSE, then the result is also FALSE 318 // 2. if both values are TRUE, then the result is also TRUE 319 // 3. in all other cases (one is TRUE and the other is NULL or both are NULL), 320 // the result is NULL. 321 if (!leftVal && !isLeftNull) || (!rightVal && !isRightNull) { 322 // Rule 1: at least one boolean is FALSE. 323 outputColVals[idx] = false 324 } else if (leftVal && !isLeftNull) && (rightVal && !isRightNull) { 325 // Rule 2: both booleans are TRUE. 326 outputColVals[idx] = true 327 } else { 328 // Rule 3. 329 outputNulls.SetNull(idx) 330 } 331 // {{end}} 332 } 333 // {{end}} 334 // {{/* 335 } 336 337 // */}}