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

     1  // Copyright 2018 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/sem/builtins"
    17  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    20  )
    21  
    22  // projectSetNode zips through a list of generators for every row of
    23  // the table source.
    24  //
    25  // Reminder, for context: the functional zip over iterators a,b,c
    26  // returns tuples of values from a,b,c picked "simultaneously". NULLs
    27  // are used when an iterator is "shorter" than another. For example:
    28  //
    29  //    zip([1,2,3], ['a','b']) = [(1,'a'), (2,'b'), (3, null)]
    30  //
    31  // In this context, projectSetNode corresponds to a relational
    32  // operator project(R, a, b, c, ...) which, for each row in R,
    33  // produces all the rows produced by zip(a, b, c, ...) with the values
    34  // of R prefixed. Formally, this performs a lateral cross join of R
    35  // with zip(a,b,c).
    36  type projectSetNode struct {
    37  	source     planNode
    38  	sourceCols sqlbase.ResultColumns
    39  
    40  	// columns contains all the columns from the source, and then
    41  	// the columns from the generators.
    42  	columns sqlbase.ResultColumns
    43  
    44  	// numColsInSource is the number of columns in the source plan, i.e.
    45  	// the number of columns at the beginning of rowBuffer that do not
    46  	// contain SRF results.
    47  	numColsInSource int
    48  
    49  	// exprs are the constant-folded, type checked expressions specified
    50  	// in the ROWS FROM syntax. This can contain many kinds of expressions
    51  	// (anything that is "function-like" including COALESCE, NULLIF) not just
    52  	// SRFs.
    53  	exprs tree.TypedExprs
    54  
    55  	// funcs contains a valid pointer to a SRF FuncExpr for every entry
    56  	// in `exprs` that is actually a SRF function application.
    57  	// The size of the slice is the same as `exprs` though.
    58  	funcs []*tree.FuncExpr
    59  
    60  	// numColsPerGen indicates how many columns are produced by
    61  	// each entry in `exprs`.
    62  	numColsPerGen []int
    63  
    64  	reqOrdering ReqOrdering
    65  
    66  	run projectSetRun
    67  }
    68  
    69  func (n *projectSetNode) IndexedVarEval(idx int, ctx *tree.EvalContext) (tree.Datum, error) {
    70  	return n.run.rowBuffer[idx].Eval(ctx)
    71  }
    72  
    73  func (n *projectSetNode) IndexedVarResolvedType(idx int) *types.T {
    74  	return n.columns[idx].Typ
    75  }
    76  
    77  func (n *projectSetNode) IndexedVarNodeFormatter(idx int) tree.NodeFormatter {
    78  	return n.columns.NodeFormatter(idx)
    79  }
    80  
    81  type projectSetRun struct {
    82  	// inputRowReady is set when there was a row of input data available
    83  	// from the source.
    84  	inputRowReady bool
    85  
    86  	// rowBuffer will contain the current row of results.
    87  	rowBuffer tree.Datums
    88  
    89  	// gens contains the current "active" ValueGenerators for each entry
    90  	// in `funcs`. They are initialized anew for every new row in the source.
    91  	gens []tree.ValueGenerator
    92  
    93  	// done indicates for each `expr` whether the values produced by
    94  	// either the SRF or the scalar expressions are fully consumed and
    95  	// thus also whether NULLs should be emitted instead.
    96  	done []bool
    97  }
    98  
    99  func (n *projectSetNode) startExec(runParams) error {
   100  	return nil
   101  }
   102  
   103  func (n *projectSetNode) Next(params runParams) (bool, error) {
   104  	for {
   105  		// If there's a cancellation request or a timeout, process it here.
   106  		if err := params.p.cancelChecker.Check(); err != nil {
   107  			return false, err
   108  		}
   109  
   110  		// Start of a new row of input?
   111  		if !n.run.inputRowReady {
   112  			// Read the row from the source.
   113  			hasRow, err := n.source.Next(params)
   114  			if err != nil || !hasRow {
   115  				return false, err
   116  			}
   117  
   118  			// Keep the values for later.
   119  			copy(n.run.rowBuffer, n.source.Values())
   120  
   121  			// Initialize a round of SRF generators or scalar values.
   122  			colIdx := n.numColsInSource
   123  			evalCtx := params.EvalContext()
   124  			evalCtx.IVarContainer = n
   125  			for i := range n.exprs {
   126  				if fn := n.funcs[i]; fn != nil {
   127  					// A set-generating function. Prepare its ValueGenerator.
   128  					gen, err := fn.EvalArgsAndGetGenerator(evalCtx)
   129  					if err != nil {
   130  						return false, err
   131  					}
   132  					if gen == nil {
   133  						gen = builtins.EmptyGenerator()
   134  					}
   135  					if err := gen.Start(params.ctx, params.extendedEvalCtx.Txn); err != nil {
   136  						return false, err
   137  					}
   138  					n.run.gens[i] = gen
   139  				}
   140  				n.run.done[i] = false
   141  				colIdx += n.numColsPerGen[i]
   142  			}
   143  
   144  			// Mark the row ready for further iterations.
   145  			n.run.inputRowReady = true
   146  		}
   147  
   148  		// Try to find some data on the generator side.
   149  		colIdx := n.numColsInSource
   150  		newValAvail := false
   151  		for i := range n.exprs {
   152  			numCols := n.numColsPerGen[i]
   153  
   154  			// Do we have a SRF?
   155  			if gen := n.run.gens[i]; gen != nil {
   156  				// Yes. Is there still work to do for the current row?
   157  				if !n.run.done[i] {
   158  					// Yes; heck whether this source still has some values available.
   159  					hasVals, err := gen.Next(params.ctx)
   160  					if err != nil {
   161  						return false, err
   162  					}
   163  					if hasVals {
   164  						// This source has values, use them.
   165  						values, err := gen.Values()
   166  						if err != nil {
   167  							return false, err
   168  						}
   169  						copy(n.run.rowBuffer[colIdx:colIdx+numCols], values)
   170  						newValAvail = true
   171  					} else {
   172  						n.run.done[i] = true
   173  						// No values left. Fill the buffer with NULLs for future
   174  						// results.
   175  						for j := 0; j < numCols; j++ {
   176  							n.run.rowBuffer[colIdx+j] = tree.DNull
   177  						}
   178  					}
   179  				}
   180  			} else {
   181  				// A simple scalar result.
   182  				// Do we still need to produce the scalar value? (first row)
   183  				if !n.run.done[i] {
   184  					// Yes. Produce it once, then indicate it's "done".
   185  					var err error
   186  					n.run.rowBuffer[colIdx], err = n.exprs[i].Eval(params.EvalContext())
   187  					if err != nil {
   188  						return false, err
   189  					}
   190  					newValAvail = true
   191  					n.run.done[i] = true
   192  				} else {
   193  					// Ensure that every row after the first returns a NULL value.
   194  					n.run.rowBuffer[colIdx] = tree.DNull
   195  				}
   196  			}
   197  
   198  			// Advance to the next column group.
   199  			colIdx += numCols
   200  		}
   201  
   202  		if newValAvail {
   203  			return true, nil
   204  		}
   205  
   206  		// The current batch of SRF values was exhausted. Advance
   207  		// to the next input row.
   208  		n.run.inputRowReady = false
   209  	}
   210  }
   211  
   212  func (n *projectSetNode) Values() tree.Datums { return n.run.rowBuffer }
   213  
   214  func (n *projectSetNode) Close(ctx context.Context) {
   215  	n.source.Close(ctx)
   216  	for _, gen := range n.run.gens {
   217  		if gen != nil {
   218  			gen.Close()
   219  		}
   220  	}
   221  }