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 }