github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/apply_join.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 package sql 12 13 import ( 14 "context" 15 16 "github.com/cockroachdb/cockroach/pkg/sql/opt/exec" 17 "github.com/cockroachdb/cockroach/pkg/sql/rowcontainer" 18 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 19 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 20 "github.com/cockroachdb/cockroach/pkg/util/hlc" 21 "github.com/cockroachdb/cockroach/pkg/util/log" 22 "github.com/cockroachdb/errors" 23 ) 24 25 // applyJoinNode implements apply join: the execution component of correlated 26 // subqueries. Note that only correlated subqueries that the optimizer's 27 // tranformations couldn't decorrelate get planned using apply joins. 28 // The node reads rows from the left planDataSource, and for each 29 // row, re-plans the right side of the join after replacing its outer columns 30 // with the corresponding values from the current row on the left. The new right 31 // plan is then executed and joined with the left row according to normal join 32 // semantics. This node doesn't support right or full outer joins, or set 33 // operations. 34 type applyJoinNode struct { 35 joinType sqlbase.JoinType 36 37 // The data source with no outer columns. 38 input planDataSource 39 40 // pred represents the join predicate. 41 pred *joinPredicate 42 43 // columns contains the metadata for the results of this node. 44 columns sqlbase.ResultColumns 45 46 // rightCols contains the metadata for the result of the right side of this 47 // apply join, as built in the optimization phase. Later on, every re-planning 48 // of the right side will emit these same columns. 49 rightCols sqlbase.ResultColumns 50 51 planRightSideFn exec.ApplyJoinPlanRightSideFn 52 53 run struct { 54 // emptyRight is a cached, all-NULL slice that's used for left outer joins 55 // in the case of finding no match on the left. 56 emptyRight tree.Datums 57 // leftRow is the current left row being processed. 58 leftRow tree.Datums 59 // leftRowFoundAMatch is set to true when a left row found any match at all, 60 // so that left outer joins and antijoins can know to output a row. 61 leftRowFoundAMatch bool 62 // rightRows will be populated with the result of the right side of the join 63 // each time it's run. 64 rightRows *rowcontainer.RowContainer 65 // curRightRow is the index into rightRows of the current right row being 66 // processed. 67 curRightRow int 68 // out is the full result row, populated on each call to Next. 69 out tree.Datums 70 // done is true if the left side has been exhausted. 71 done bool 72 } 73 } 74 75 // Set to true to enable ultra verbose debug logging. 76 func newApplyJoinNode( 77 joinType sqlbase.JoinType, 78 left planDataSource, 79 rightCols sqlbase.ResultColumns, 80 pred *joinPredicate, 81 planRightSideFn exec.ApplyJoinPlanRightSideFn, 82 ) (planNode, error) { 83 switch joinType { 84 case sqlbase.RightOuterJoin, sqlbase.FullOuterJoin: 85 return nil, errors.AssertionFailedf("unsupported right outer apply join: %d", log.Safe(joinType)) 86 case sqlbase.ExceptAllJoin, sqlbase.IntersectAllJoin: 87 return nil, errors.AssertionFailedf("unsupported apply set op: %d", log.Safe(joinType)) 88 } 89 90 return &applyJoinNode{ 91 joinType: joinType, 92 input: left, 93 pred: pred, 94 rightCols: rightCols, 95 planRightSideFn: planRightSideFn, 96 columns: pred.cols, 97 }, nil 98 } 99 100 func (a *applyJoinNode) startExec(params runParams) error { 101 // If needed, pre-allocate a right row of NULL tuples for when the 102 // join predicate fails to match. 103 if a.joinType == sqlbase.LeftOuterJoin { 104 a.run.emptyRight = make(tree.Datums, len(a.rightCols)) 105 for i := range a.run.emptyRight { 106 a.run.emptyRight[i] = tree.DNull 107 } 108 } 109 a.run.out = make(tree.Datums, len(a.columns)) 110 ci := sqlbase.ColTypeInfoFromResCols(a.rightCols) 111 acc := params.EvalContext().Mon.MakeBoundAccount() 112 a.run.rightRows = rowcontainer.NewRowContainer(acc, ci, 0 /* rowCapacity */) 113 return nil 114 } 115 116 func (a *applyJoinNode) Next(params runParams) (bool, error) { 117 if a.run.done { 118 return false, nil 119 } 120 121 for { 122 for a.run.curRightRow < a.run.rightRows.Len() { 123 // We have right rows set up - check the next one for a match. 124 var rrow tree.Datums 125 if len(a.rightCols) != 0 { 126 rrow = a.run.rightRows.At(a.run.curRightRow) 127 } 128 a.run.curRightRow++ 129 // Compute join. 130 predMatched, err := a.pred.eval(params.EvalContext(), a.run.leftRow, rrow) 131 if err != nil { 132 return false, err 133 } 134 if !predMatched { 135 // Didn't match? Try with the next right-side row. 136 continue 137 } 138 139 a.run.leftRowFoundAMatch = true 140 if a.joinType == sqlbase.LeftAntiJoin || 141 a.joinType == sqlbase.LeftSemiJoin { 142 // We found a match, but we're doing an anti or semi join, so we're 143 // done with this left row. 144 break 145 } 146 // We're doing an ordinary join, so prep the row and emit it. 147 a.pred.prepareRow(a.run.out, a.run.leftRow, rrow) 148 return true, nil 149 } 150 // We're out of right side rows. Clear them, and reset the match state for 151 // next time. 152 a.run.rightRows.Clear(params.ctx) 153 foundAMatch := a.run.leftRowFoundAMatch 154 a.run.leftRowFoundAMatch = false 155 156 if a.run.leftRow != nil { 157 // If we have a left row already, we have to check to see if we need to 158 // emit rows for semi, outer, or anti joins. 159 if foundAMatch { 160 if a.joinType == sqlbase.LeftSemiJoin { 161 // We found a match, and we're doing an semi-join, so we're done 162 // with this left row after we output it. 163 a.pred.prepareRow(a.run.out, a.run.leftRow, nil) 164 a.run.leftRow = nil 165 return true, nil 166 } 167 } else { 168 // We found no match. Output LEFT OUTER or ANTI match if necessary. 169 switch a.joinType { 170 case sqlbase.LeftOuterJoin: 171 a.pred.prepareRow(a.run.out, a.run.leftRow, a.run.emptyRight) 172 a.run.leftRow = nil 173 return true, nil 174 case sqlbase.LeftAntiJoin: 175 a.pred.prepareRow(a.run.out, a.run.leftRow, nil) 176 a.run.leftRow = nil 177 return true, nil 178 } 179 } 180 } 181 182 // We need a new row on the left. 183 ok, err := a.input.plan.Next(params) 184 if err != nil { 185 return false, err 186 } 187 if !ok { 188 // No more rows on the left. Goodbye! 189 a.run.done = true 190 return false, nil 191 } 192 193 // Extract the values of the outer columns of the other side of the apply 194 // from the latest input row. 195 leftRow := a.input.plan.Values() 196 a.run.leftRow = leftRow 197 198 // At this point, it's time to do the major lift of apply join: re-planning 199 // the right side of the join using the optimizer, with all outer columns 200 // in the right side replaced by the bindings that were defined by the most 201 // recently read left row. 202 p, err := a.planRightSideFn(leftRow) 203 if err != nil { 204 return false, err 205 } 206 plan := p.(*planTop) 207 208 if err := a.runRightSidePlan(params, plan); err != nil { 209 return false, err 210 } 211 212 // We've got fresh right rows. Continue along in the loop, which will deal 213 // with joining the right plan's output with our left row. 214 } 215 } 216 217 // runRightSidePlan runs a planTop that's been generated based on the 218 // re-optimized right hand side of the apply join, stashing the result in 219 // a.run.rightRows, ready for retrieval. An error indicates that something went 220 // wrong during execution of the right hand side of the join, and that we should 221 // completely give up on the outer join. 222 func (a *applyJoinNode) runRightSidePlan(params runParams, plan *planTop) error { 223 a.run.curRightRow = 0 224 a.run.rightRows.Clear(params.ctx) 225 return runPlanInsidePlan(params, plan, a.run.rightRows) 226 } 227 228 // runPlanInsidePlan is used to run a plan and gather the results in a row 229 // container, as part of the execution of an "outer" plan. 230 func runPlanInsidePlan( 231 params runParams, plan *planTop, rowContainer *rowcontainer.RowContainer, 232 ) error { 233 rowResultWriter := NewRowResultWriter(rowContainer) 234 recv := MakeDistSQLReceiver( 235 params.ctx, rowResultWriter, tree.Rows, 236 params.extendedEvalCtx.ExecCfg.RangeDescriptorCache, 237 params.extendedEvalCtx.ExecCfg.LeaseHolderCache, 238 params.p.Txn(), 239 func(ts hlc.Timestamp) { 240 params.extendedEvalCtx.ExecCfg.Clock.Update(ts) 241 }, 242 params.p.extendedEvalCtx.Tracing, 243 ) 244 defer recv.Release() 245 246 if !params.p.extendedEvalCtx.ExecCfg.DistSQLPlanner.PlanAndRunSubqueries( 247 params.ctx, 248 params.p, 249 params.extendedEvalCtx.copy, 250 plan.subqueryPlans, 251 recv, 252 true, 253 ) { 254 if err := rowResultWriter.Err(); err != nil { 255 return err 256 } 257 return recv.commErr 258 } 259 260 // Make a copy of the EvalContext so it can be safely modified. 261 evalCtx := params.p.ExtendedEvalContextCopy() 262 planCtx := params.p.extendedEvalCtx.ExecCfg.DistSQLPlanner.NewPlanningCtx(params.ctx, evalCtx, params.p.txn, false /* distribute */) 263 plannerCopy := *params.p 264 planCtx.planner = &plannerCopy 265 planCtx.planner.curPlan = *plan 266 planCtx.ExtendedEvalCtx.Planner = &plannerCopy 267 planCtx.stmtType = recv.stmtType 268 269 params.p.extendedEvalCtx.ExecCfg.DistSQLPlanner.PlanAndRun( 270 params.ctx, evalCtx, planCtx, params.p.Txn(), plan.main, recv, 271 )() 272 if recv.commErr != nil { 273 return recv.commErr 274 } 275 return rowResultWriter.err 276 } 277 278 func (a *applyJoinNode) Values() tree.Datums { 279 return a.run.out 280 } 281 282 func (a *applyJoinNode) Close(ctx context.Context) { 283 a.input.plan.Close(ctx) 284 if a.run.rightRows != nil { 285 a.run.rightRows.Close(ctx) 286 } 287 }