github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/optbuilder/srfs.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 optbuilder 12 13 import ( 14 "context" 15 16 "github.com/cockroachdb/cockroach/pkg/sql/opt" 17 "github.com/cockroachdb/cockroach/pkg/sql/opt/memo" 18 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 19 "github.com/cockroachdb/cockroach/pkg/sql/types" 20 "github.com/cockroachdb/cockroach/pkg/util/errorutil/unimplemented" 21 "github.com/cockroachdb/errors" 22 ) 23 24 // srf represents an srf expression in an expression tree 25 // after it has been type-checked and added to the memo. 26 type srf struct { 27 // The resolved function expression. 28 *tree.FuncExpr 29 30 // cols contains the output columns of the srf. 31 cols []scopeColumn 32 33 // fn is the top level function expression of the srf. 34 fn opt.ScalarExpr 35 } 36 37 // Walk is part of the tree.Expr interface. 38 func (s *srf) Walk(v tree.Visitor) tree.Expr { 39 return s 40 } 41 42 // TypeCheck is part of the tree.Expr interface. 43 func (s *srf) TypeCheck( 44 _ context.Context, ctx *tree.SemaContext, desired *types.T, 45 ) (tree.TypedExpr, error) { 46 if ctx.Properties.Derived.SeenGenerator { 47 // This error happens if this srf struct is nested inside a raw srf that 48 // has not yet been replaced. This is possible since scope.replaceSRF first 49 // calls f.Walk(s) on the external raw srf, which replaces any internal 50 // raw srfs with srf structs. The next call to TypeCheck on the external 51 // raw srf triggers this error. 52 return nil, unimplemented.NewWithIssuef(26234, "nested set-returning functions") 53 } 54 55 return s, nil 56 } 57 58 // Eval is part of the tree.TypedExpr interface. 59 func (s *srf) Eval(_ *tree.EvalContext) (tree.Datum, error) { 60 panic(errors.AssertionFailedf("srf must be replaced before evaluation")) 61 } 62 63 var _ tree.Expr = &srf{} 64 var _ tree.TypedExpr = &srf{} 65 66 // buildZip builds a set of memo groups which represent a functional zip over 67 // the given expressions. 68 // 69 // Reminder, for context: the functional zip over iterators a,b,c 70 // returns tuples of values from a,b,c picked "simultaneously". NULLs 71 // are used when an iterator is "shorter" than another. For example: 72 // 73 // zip([1,2,3], ['a','b']) = [(1,'a'), (2,'b'), (3, null)] 74 // 75 func (b *Builder) buildZip(exprs tree.Exprs, inScope *scope) (outScope *scope) { 76 outScope = inScope.push() 77 78 // We need to save and restore the previous value of the field in 79 // semaCtx in case we are recursively called within a subquery 80 // context. 81 defer b.semaCtx.Properties.Restore(b.semaCtx.Properties) 82 b.semaCtx.Properties.Require(exprKindFrom.String(), 83 tree.RejectAggregates|tree.RejectWindowApplications|tree.RejectNestedGenerators) 84 inScope.context = exprKindFrom 85 86 // Build each of the provided expressions. 87 zip := make(memo.ZipExpr, len(exprs)) 88 for i, expr := range exprs { 89 // Output column names should exactly match the original expression, so we 90 // have to determine the output column name before we perform type 91 // checking. However, the alias may be overridden later below if the expression 92 // is a function and specifically defines a return label. 93 _, alias, err := tree.ComputeColNameInternal(b.semaCtx.SearchPath, expr) 94 if err != nil { 95 panic(err) 96 } 97 texpr := inScope.resolveType(expr, types.Any) 98 99 var def *tree.FunctionDefinition 100 if funcExpr, ok := texpr.(*tree.FuncExpr); ok { 101 if def, err = funcExpr.Func.Resolve(b.semaCtx.SearchPath); err != nil { 102 panic(err) 103 } 104 } 105 106 var outCol *scopeColumn 107 startCols := len(outScope.cols) 108 if def == nil || def.Class != tree.GeneratorClass || b.shouldCreateDefaultColumn(texpr) { 109 110 if def != nil && len(def.ReturnLabels) > 0 { 111 // Override the computed alias with the one defined in the ReturnLabels. This 112 // satisfies a Postgres quirk where some json functions use different labels 113 // when used in a from clause. 114 alias = def.ReturnLabels[0] 115 } 116 outCol = b.addColumn(outScope, alias, texpr) 117 } 118 119 scalar := b.buildScalar(texpr, inScope, outScope, outCol, nil) 120 cols := make(opt.ColList, len(outScope.cols)-startCols) 121 for j := startCols; j < len(outScope.cols); j++ { 122 cols[j-startCols] = outScope.cols[j].id 123 } 124 zip[i] = b.factory.ConstructZipItem(scalar, cols) 125 } 126 127 // Construct the zip as a ProjectSet with empty input. 128 input := b.factory.ConstructValues(memo.ScalarListWithEmptyTuple, &memo.ValuesPrivate{ 129 Cols: opt.ColList{}, 130 ID: b.factory.Metadata().NextUniqueID(), 131 }) 132 outScope.expr = b.factory.ConstructProjectSet(input, zip) 133 if len(outScope.cols) == 1 { 134 outScope.singleSRFColumn = true 135 } 136 return outScope 137 } 138 139 // finishBuildGeneratorFunction finishes building a set-generating function 140 // (SRF) such as generate_series() or unnest(). It synthesizes new columns in 141 // outScope for each of the SRF's output columns. 142 func (b *Builder) finishBuildGeneratorFunction( 143 f *tree.FuncExpr, fn opt.ScalarExpr, inScope, outScope *scope, outCol *scopeColumn, 144 ) (out opt.ScalarExpr) { 145 // Add scope columns. 146 if outCol != nil { 147 // Single-column return type. 148 b.populateSynthesizedColumn(outCol, fn) 149 } else { 150 // Multi-column return type. Use the tuple labels in the SRF's return type 151 // as column aliases. 152 typ := f.ResolvedType() 153 for i := range typ.TupleContents() { 154 b.synthesizeColumn(outScope, typ.TupleLabels()[i], typ.TupleContents()[i], nil, fn) 155 } 156 } 157 158 return fn 159 } 160 161 // buildProjectSet builds a ProjectSet, which is a lateral cross join 162 // between the given input expression and a functional zip constructed from the 163 // given srfs. 164 // 165 // This function is called at most once per SELECT clause, and updates 166 // inScope.expr if at least one SRF was discovered in the SELECT list. The 167 // ProjectSet is necessary in case some of the SRFs depend on the input. 168 // For example, consider this query: 169 // 170 // SELECT generate_series(t.a, t.a + 1) FROM t 171 // 172 // In this case, the inputs to generate_series depend on table t, so during 173 // execution, generate_series will be called once for each row of t. 174 func (b *Builder) buildProjectSet(inScope *scope) { 175 if len(inScope.srfs) == 0 { 176 return 177 } 178 179 // Get the output columns and function expressions of the zip. 180 zip := make(memo.ZipExpr, len(inScope.srfs)) 181 for i, srf := range inScope.srfs { 182 cols := make(opt.ColList, len(srf.cols)) 183 for j := range srf.cols { 184 cols[j] = srf.cols[j].id 185 } 186 zip[i] = b.factory.ConstructZipItem(srf.fn, cols) 187 } 188 189 inScope.expr = b.factory.ConstructProjectSet(inScope.expr, zip) 190 }