github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/rowexec/joinerbase.go (about)

     1  // Copyright 2016 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 rowexec
    12  
    13  import (
    14  	"github.com/cockroachdb/cockroach/pkg/sql/execinfra"
    15  	"github.com/cockroachdb/cockroach/pkg/sql/execinfrapb"
    16  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    17  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    19  	"github.com/cockroachdb/errors"
    20  )
    21  
    22  // joinerBase is the common core of all joiners.
    23  type joinerBase struct {
    24  	execinfra.ProcessorBase
    25  
    26  	joinType    sqlbase.JoinType
    27  	onCond      execinfra.ExprHelper
    28  	emptyLeft   sqlbase.EncDatumRow
    29  	emptyRight  sqlbase.EncDatumRow
    30  	combinedRow sqlbase.EncDatumRow
    31  
    32  	// EqCols contains the indices of the columns that are constrained to be
    33  	// equal. Specifically column EqCols[0][i] on the left side must match the
    34  	// column EqCols[1][i] on the right side.
    35  	eqCols [2][]uint32
    36  
    37  	// numMergedEqualityColumns specifies how many of the equality
    38  	// columns must be merged at the beginning of each result row. This
    39  	// is the desired behavior for USING and NATURAL JOIN.
    40  	numMergedEqualityColumns int
    41  }
    42  
    43  // Init initializes the joinerBase.
    44  //
    45  // opts is passed along to the underlying ProcessorBase. The zero value is used
    46  // if the processor using the joinerBase is not implementing RowSource.
    47  func (jb *joinerBase) init(
    48  	self execinfra.RowSource,
    49  	flowCtx *execinfra.FlowCtx,
    50  	processorID int32,
    51  	leftTypes []*types.T,
    52  	rightTypes []*types.T,
    53  	jType sqlbase.JoinType,
    54  	onExpr execinfrapb.Expression,
    55  	leftEqColumns []uint32,
    56  	rightEqColumns []uint32,
    57  	numMergedColumns uint32,
    58  	post *execinfrapb.PostProcessSpec,
    59  	output execinfra.RowReceiver,
    60  	opts execinfra.ProcStateOpts,
    61  ) error {
    62  	jb.joinType = jType
    63  
    64  	if jb.joinType.IsSetOpJoin() {
    65  		if !onExpr.Empty() {
    66  			return errors.Errorf("expected empty onExpr, got %v", onExpr.Expr)
    67  		}
    68  	}
    69  
    70  	jb.emptyLeft = make(sqlbase.EncDatumRow, len(leftTypes))
    71  	for i := range jb.emptyLeft {
    72  		jb.emptyLeft[i] = sqlbase.DatumToEncDatum(leftTypes[i], tree.DNull)
    73  	}
    74  	jb.emptyRight = make(sqlbase.EncDatumRow, len(rightTypes))
    75  	for i := range jb.emptyRight {
    76  		jb.emptyRight[i] = sqlbase.DatumToEncDatum(rightTypes[i], tree.DNull)
    77  	}
    78  
    79  	jb.eqCols[leftSide] = leftEqColumns
    80  	jb.eqCols[rightSide] = rightEqColumns
    81  	jb.numMergedEqualityColumns = int(numMergedColumns)
    82  
    83  	size := len(leftTypes) + jb.numMergedEqualityColumns + len(rightTypes)
    84  	jb.combinedRow = make(sqlbase.EncDatumRow, size)
    85  
    86  	condTypes := make([]*types.T, 0, size)
    87  	for idx := 0; idx < jb.numMergedEqualityColumns; idx++ {
    88  		ltype := leftTypes[jb.eqCols[leftSide][idx]]
    89  		rtype := rightTypes[jb.eqCols[rightSide][idx]]
    90  		var ctype *types.T
    91  		if ltype.Family() != types.UnknownFamily {
    92  			ctype = ltype
    93  		} else {
    94  			ctype = rtype
    95  		}
    96  		condTypes = append(condTypes, ctype)
    97  	}
    98  	condTypes = append(condTypes, leftTypes...)
    99  	condTypes = append(condTypes, rightTypes...)
   100  
   101  	outputSize := len(leftTypes) + jb.numMergedEqualityColumns
   102  	if jb.joinType.ShouldIncludeRightColsInOutput() {
   103  		outputSize += len(rightTypes)
   104  	}
   105  	outputTypes := condTypes[:outputSize]
   106  
   107  	if err := jb.ProcessorBase.Init(
   108  		self, post, outputTypes, flowCtx, processorID, output, nil /* memMonitor */, opts,
   109  	); err != nil {
   110  		return err
   111  	}
   112  	return jb.onCond.Init(onExpr, condTypes, jb.EvalCtx)
   113  }
   114  
   115  // joinSide is the utility type to distinguish between two sides of the join.
   116  type joinSide uint8
   117  
   118  const (
   119  	// leftSide indicates the left side of the join.
   120  	leftSide joinSide = 0
   121  	// rightSide indicates the right side of the join.
   122  	rightSide joinSide = 1
   123  )
   124  
   125  // otherSide returns the opposite to s side.
   126  func otherSide(s joinSide) joinSide {
   127  	return joinSide(1 - uint8(s))
   128  }
   129  
   130  func (j joinSide) String() string {
   131  	if j == leftSide {
   132  		return "left"
   133  	}
   134  	return "right"
   135  }
   136  
   137  // renderUnmatchedRow creates a result row given an unmatched row on either
   138  // side. Only used for outer joins.
   139  func (jb *joinerBase) renderUnmatchedRow(
   140  	row sqlbase.EncDatumRow, side joinSide,
   141  ) sqlbase.EncDatumRow {
   142  	lrow, rrow := jb.emptyLeft, jb.emptyRight
   143  	if side == leftSide {
   144  		lrow = row
   145  	} else {
   146  		rrow = row
   147  	}
   148  
   149  	// If there are merged columns, they take first positions in a row
   150  	// Values are taken from non-empty row
   151  	jb.combinedRow = jb.combinedRow[:0]
   152  	for idx := 0; idx < jb.numMergedEqualityColumns; idx++ {
   153  		jb.combinedRow = append(jb.combinedRow, row[jb.eqCols[side][idx]])
   154  	}
   155  	jb.combinedRow = append(jb.combinedRow, lrow...)
   156  	jb.combinedRow = append(jb.combinedRow, rrow...)
   157  	return jb.combinedRow
   158  }
   159  
   160  // shouldEmitUnmatchedRow determines if we should emit am ummatched row (with
   161  // NULLs for the columns of the other stream). This happens in FULL OUTER joins
   162  // and LEFT or RIGHT OUTER joins and ANTI joins (depending on which stream is
   163  // stored).
   164  func shouldEmitUnmatchedRow(side joinSide, joinType sqlbase.JoinType) bool {
   165  	switch joinType {
   166  	case sqlbase.LeftSemiJoin, sqlbase.InnerJoin, sqlbase.IntersectAllJoin:
   167  		return false
   168  	case sqlbase.RightOuterJoin:
   169  		return side == rightSide
   170  	case sqlbase.LeftOuterJoin:
   171  		return side == leftSide
   172  	case sqlbase.LeftAntiJoin:
   173  		return side == leftSide
   174  	case sqlbase.ExceptAllJoin:
   175  		return side == leftSide
   176  	case sqlbase.FullOuterJoin:
   177  		return true
   178  	default:
   179  		return true
   180  	}
   181  }
   182  
   183  // render constructs a row with columns from both sides. The ON condition is
   184  // evaluated; if it fails, returns nil.
   185  // Note the left and right merged equality columns (i.e. from a USING clause
   186  // or after simplifying ON left.x = right.x) are NOT checked for equality.
   187  // See CompareEncDatumRowForMerge.
   188  func (jb *joinerBase) render(lrow, rrow sqlbase.EncDatumRow) (sqlbase.EncDatumRow, error) {
   189  	n := jb.numMergedEqualityColumns
   190  	jb.combinedRow = jb.combinedRow[:n+len(lrow)+len(rrow)]
   191  	for i := 0; i < n; i++ {
   192  		// This function is called only when lrow and rrow match on the equality
   193  		// columns which can never happen if there are any NULLs in these
   194  		// columns. So we know for sure the lrow value is not null
   195  		jb.combinedRow[i] = lrow[jb.eqCols[leftSide][i]]
   196  	}
   197  	copy(jb.combinedRow[n:], lrow)
   198  	copy(jb.combinedRow[n+len(lrow):], rrow)
   199  
   200  	if jb.onCond.Expr != nil {
   201  		res, err := jb.onCond.EvalFilter(jb.combinedRow)
   202  		if !res || err != nil {
   203  			return nil, err
   204  		}
   205  	}
   206  	return jb.combinedRow, nil
   207  }