github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/window.go (about) 1 // Copyright 2016 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/tree" 17 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 18 "github.com/cockroachdb/cockroach/pkg/sql/types" 19 ) 20 21 // A windowNode implements the planNode interface and handles windowing logic. 22 // 23 // windowRender will contain renders that will output the desired result 24 // columns (so len(windowRender) == len(columns)). 25 // 1. If ith render from the source node does not have any window functions, 26 // then that column will be simply passed through and windowRender[i] is 27 // nil. Notably, windowNode will rearrange renders in the source node so 28 // that all such passed through columns are contiguous and in the beginning. 29 // (This happens during extractWindowFunctions call.) 30 // 2. If ith render from the source node has any window functions, then the 31 // render is stored in windowRender[i]. During 32 // constructWindowFunctionsDefinitions all variables used in OVER clauses 33 // of all window functions are being rendered, and during 34 // setupWindowFunctions all arguments to all window functions are being 35 // rendered (renders are reused if possible). 36 // Therefore, the schema of the source node will be changed to look as follows: 37 // pass through column | OVER clauses columns | arguments to window functions. 38 type windowNode struct { 39 // The source node. 40 plan planNode 41 // columns is the set of result columns. 42 columns sqlbase.ResultColumns 43 44 // A sparse array holding renders specific to this windowNode. This will 45 // contain nil entries for renders that do not contain window functions, 46 // and which therefore can be propagated directly from the "wrapped" node. 47 windowRender []tree.TypedExpr 48 49 // The window functions handled by this windowNode. 50 funcs []*windowFuncHolder 51 52 // colAndAggContainer is an IndexedVarContainer that provides indirection 53 // to migrate IndexedVars and aggregate functions below the windowing level. 54 colAndAggContainer windowNodeColAndAggContainer 55 } 56 57 func (n *windowNode) startExec(params runParams) error { 58 panic("windowNode can't be run in local mode") 59 } 60 61 func (n *windowNode) Next(params runParams) (bool, error) { 62 panic("windowNode can't be run in local mode") 63 } 64 65 func (n *windowNode) Values() tree.Datums { 66 panic("windowNode can't be run in local mode") 67 } 68 69 func (n *windowNode) Close(ctx context.Context) { 70 n.plan.Close(ctx) 71 } 72 73 var _ tree.TypedExpr = &windowFuncHolder{} 74 var _ tree.VariableExpr = &windowFuncHolder{} 75 76 type windowFuncHolder struct { 77 window *windowNode 78 79 expr *tree.FuncExpr 80 args []tree.Expr 81 82 argsIdxs []uint32 // indices of the columns that are arguments to the window function 83 filterColIdx int // optional index of filtering column, -1 if no filter 84 outputColIdx int // index of the column that the output should be put into 85 86 partitionIdxs []int 87 columnOrdering sqlbase.ColumnOrdering 88 frame *tree.WindowFrame 89 } 90 91 // samePartition returns whether f and other have the same PARTITION BY clause. 92 func (w *windowFuncHolder) samePartition(other *windowFuncHolder) bool { 93 if len(w.partitionIdxs) != len(other.partitionIdxs) { 94 return false 95 } 96 for i, p := range w.partitionIdxs { 97 if p != other.partitionIdxs[i] { 98 return false 99 } 100 } 101 return true 102 } 103 104 func (*windowFuncHolder) Variable() {} 105 106 func (w *windowFuncHolder) Format(ctx *tree.FmtCtx) { 107 // Avoid duplicating the type annotation by calling .Format directly. 108 w.expr.Format(ctx) 109 } 110 111 func (w *windowFuncHolder) String() string { return tree.AsString(w) } 112 113 func (w *windowFuncHolder) Walk(v tree.Visitor) tree.Expr { return w } 114 115 func (w *windowFuncHolder) TypeCheck( 116 _ context.Context, _ *tree.SemaContext, desired *types.T, 117 ) (tree.TypedExpr, error) { 118 return w, nil 119 } 120 121 func (w *windowFuncHolder) Eval(ctx *tree.EvalContext) (tree.Datum, error) { 122 panic("windowFuncHolder should not be evaluated directly") 123 } 124 125 func (w *windowFuncHolder) ResolvedType() *types.T { 126 return w.expr.ResolvedType() 127 } 128 129 // windowNodeColAndAggContainer is an IndexedVarContainer providing indirection 130 // for IndexedVars and aggregation functions found above the windowing level. 131 // See replaceIndexVarsAndAggFuncs. 132 type windowNodeColAndAggContainer struct { 133 // idxMap maps the index of IndexedVars created in replaceIndexVarsAndAggFuncs 134 // to the index their corresponding results in this container. It permits us to 135 // add a single render to the source plan per unique expression. 136 idxMap map[int]int 137 // sourceInfo contains information on the IndexedVars from the 138 // source plan where they were originally created. 139 sourceInfo *sqlbase.DataSourceInfo 140 // aggFuncs maps the index of IndexedVars to their corresponding aggregate function. 141 aggFuncs map[int]*tree.FuncExpr 142 // startAggIdx indicates the smallest index to be used by an IndexedVar replacing 143 // an aggregate function. We don't want to mix these IndexedVars with those 144 // that replace "original" IndexedVars. 145 startAggIdx int 146 } 147 148 func (c *windowNodeColAndAggContainer) IndexedVarEval( 149 idx int, ctx *tree.EvalContext, 150 ) (tree.Datum, error) { 151 panic("IndexedVarEval should not be called on windowNodeColAndAggContainer") 152 } 153 154 // IndexedVarResolvedType implements the tree.IndexedVarContainer interface. 155 func (c *windowNodeColAndAggContainer) IndexedVarResolvedType(idx int) *types.T { 156 if idx >= c.startAggIdx { 157 return c.aggFuncs[idx].ResolvedType() 158 } 159 return c.sourceInfo.SourceColumns[idx].Typ 160 } 161 162 // IndexedVarNodeFormatter implements the tree.IndexedVarContainer interface. 163 func (c *windowNodeColAndAggContainer) IndexedVarNodeFormatter(idx int) tree.NodeFormatter { 164 if idx >= c.startAggIdx { 165 // Avoid duplicating the type annotation by calling .Format directly. 166 return c.aggFuncs[idx] 167 } 168 // Avoid duplicating the type annotation by calling .Format directly. 169 return c.sourceInfo.NodeFormatter(idx) 170 }