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 }