github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/colexec/bool_vec_to_sel.go (about) 1 // Copyright 2018 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 16 "github.com/cockroachdb/cockroach/pkg/col/coldata" 17 "github.com/cockroachdb/cockroach/pkg/sql/colexecbase" 18 "github.com/cockroachdb/cockroach/pkg/sql/types" 19 "github.com/cockroachdb/errors" 20 ) 21 22 // boolOrUnknownToSelOp plans an infrastructure necessary to convert a column 23 // of either Bool or Unknown type into a selection vector on the input batches. 24 func boolOrUnknownToSelOp( 25 input colexecbase.Operator, typs []*types.T, vecIdx int, 26 ) (colexecbase.Operator, error) { 27 switch typs[vecIdx].Family() { 28 case types.BoolFamily: 29 return newBoolVecToSelOp(input, vecIdx), nil 30 case types.UnknownFamily: 31 // If the column is of an Unknown type, then all values in that column 32 // must be NULLs, so the selection vector will always be empty, and we 33 // can simply plan a zero operator. 34 return NewZeroOp(input), nil 35 default: 36 return nil, errors.Errorf("unexpectedly %s is neither bool nor unknown", typs[vecIdx]) 37 } 38 } 39 40 // boolVecToSelOp transforms a boolean column into a selection vector by adding 41 // an index to the selection for each true value in the boolean column. 42 type boolVecToSelOp struct { 43 OneInputNode 44 NonExplainable 45 46 // outputCol is the boolean output column. It should be shared by other 47 // operators that write to it. 48 outputCol []bool 49 } 50 51 var _ colexecbase.Operator = &boolVecToSelOp{} 52 53 func (p *boolVecToSelOp) Next(ctx context.Context) coldata.Batch { 54 // Loop until we have non-zero amount of output to return, or our input's been 55 // exhausted. 56 for { 57 batch := p.input.Next(ctx) 58 n := batch.Length() 59 if n == 0 { 60 return batch 61 } 62 outputCol := p.outputCol 63 64 // Convert outputCol to a selection vector by outputting the index of each 65 // tuple whose outputCol value is true. 66 // Note that, if the input already had a selection vector, the output 67 // selection vector will be a subset of the input selection vector. 68 idx := 0 69 if sel := batch.Selection(); sel != nil { 70 sel = sel[:n] 71 for s := range sel { 72 i := sel[s] 73 var inc int 74 // This form is transformed into a data dependency by the compiler, 75 // avoiding an expensive conditional branch. 76 if outputCol[i] { 77 inc = 1 78 } 79 sel[idx] = i 80 idx += inc 81 } 82 } else { 83 batch.SetSelection(true) 84 sel := batch.Selection() 85 for i := range outputCol[:n] { 86 var inc int 87 // Ditto above: replace a conditional with a data dependency. 88 if outputCol[i] { 89 inc = 1 90 } 91 sel[idx] = i 92 idx += inc 93 } 94 } 95 96 if idx == 0 { 97 continue 98 } 99 100 batch.SetLength(idx) 101 return batch 102 } 103 } 104 105 func (p *boolVecToSelOp) Init() { 106 p.input.Init() 107 } 108 109 func boolVecToSel64(vec []bool, sel []int) []int { 110 l := len(vec) 111 for i := 0; i < l; i++ { 112 if vec[i] { 113 sel = append(sel, i) 114 } 115 } 116 return sel 117 } 118 119 // newBoolVecToSelOp is the operator form of boolVecToSelOp. It filters its 120 // input batch by the boolean column specified by colIdx. 121 // 122 // For internal use cases that just need a way to create a selection vector 123 // based on a boolean column that *isn't* in a batch, just create a 124 // boolVecToSelOp directly with the desired boolean slice. 125 // 126 // NOTE: if the column can be of a type other than boolean, 127 // boolOrUnknownToSelOp *must* be used instead. 128 func newBoolVecToSelOp(input colexecbase.Operator, colIdx int) colexecbase.Operator { 129 d := selBoolOp{OneInputNode: NewOneInputNode(input), colIdx: colIdx} 130 ret := &boolVecToSelOp{OneInputNode: NewOneInputNode(&d)} 131 d.boolVecToSelOp = ret 132 return ret 133 } 134 135 // selBoolOp is a small helper operator that transforms a boolVecToSelOp into 136 // an operator that can see the inside of its input batch for newBoolVecToSelOp. 137 type selBoolOp struct { 138 OneInputNode 139 NonExplainable 140 boolVecToSelOp *boolVecToSelOp 141 colIdx int 142 } 143 144 func (d selBoolOp) Init() { 145 d.input.Init() 146 } 147 148 func (d selBoolOp) Next(ctx context.Context) coldata.Batch { 149 batch := d.input.Next(ctx) 150 n := batch.Length() 151 if n == 0 { 152 return batch 153 } 154 inputCol := batch.ColVec(d.colIdx) 155 d.boolVecToSelOp.outputCol = inputCol.Bool() 156 if inputCol.MaybeHasNulls() { 157 // If the input column has null values, we need to explicitly set the 158 // values of the output column that correspond to those null values to 159 // false. For example, doing the comparison 'NULL < 0' will put true into 160 // the boolean Vec (because NULLs are smaller than any integer) but will 161 // also set the null. In the code above, we only copied the values' vector, 162 // so we need to adjust it. 163 // TODO(yuzefovich): think through this case more, possibly clean this up. 164 outputCol := d.boolVecToSelOp.outputCol 165 sel := batch.Selection() 166 nulls := inputCol.Nulls() 167 if sel != nil { 168 sel = sel[:n] 169 for _, i := range sel { 170 if nulls.NullAt(i) { 171 outputCol[i] = false 172 } 173 } 174 } else { 175 outputCol = outputCol[0:n] 176 for i := range outputCol { 177 if nulls.NullAt(i) { 178 outputCol[i] = false 179 } 180 } 181 } 182 } 183 return batch 184 }