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  }