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  // */}}