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  }