github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/group.go (about) 1 // Copyright 2015 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 "github.com/cockroachdb/cockroach/pkg/util/mon" 21 ) 22 23 // A groupNode implements the planNode interface and handles the grouping logic. 24 // It "wraps" a planNode which is used to retrieve the ungrouped results. 25 type groupNode struct { 26 // The schema for this groupNode. 27 columns sqlbase.ResultColumns 28 29 // The source node (which returns values that feed into the aggregation). 30 plan planNode 31 32 // Indices of the group by columns in the source plan. 33 groupCols []int 34 35 // Set when we have an input ordering on (a subset of) grouping columns. Only 36 // column indices in groupCols can appear in this ordering. 37 groupColOrdering sqlbase.ColumnOrdering 38 39 // isScalar is set for "scalar groupby", where we want a result 40 // even if there are no input rows, e.g. SELECT MIN(x) FROM t. 41 isScalar bool 42 43 // funcs are the aggregation functions that the renders use. 44 funcs []*aggregateFuncHolder 45 46 reqOrdering ReqOrdering 47 } 48 49 func (n *groupNode) startExec(params runParams) error { 50 panic("groupNode cannot be run in local mode") 51 } 52 53 func (n *groupNode) Next(params runParams) (bool, error) { 54 panic("groupNode cannot be run in local mode") 55 } 56 57 func (n *groupNode) Values() tree.Datums { 58 panic("groupNode cannot be run in local mode") 59 } 60 61 func (n *groupNode) Close(ctx context.Context) { 62 n.plan.Close(ctx) 63 for _, f := range n.funcs { 64 f.close(ctx) 65 } 66 } 67 68 // aggIsGroupingColumn returns true if the given output aggregation is an 69 // any_not_null aggregation for a grouping column. The grouping column 70 // index is also returned. 71 func (n *groupNode) aggIsGroupingColumn(aggIdx int) (colIdx int, ok bool) { 72 if holder := n.funcs[aggIdx]; holder.funcName == builtins.AnyNotNull { 73 for _, c := range n.groupCols { 74 for _, renderIdx := range holder.argRenderIdxs { 75 if c == renderIdx { 76 return c, true 77 } 78 } 79 } 80 } 81 return -1, false 82 } 83 84 type aggregateFuncHolder struct { 85 // Name of the aggregate function. Empty if this column reproduces a bucket 86 // key unchanged. 87 funcName string 88 89 resultType *types.T 90 91 // The argument of the function is a single value produced by the renderNode 92 // underneath. If the function has no argument (COUNT_ROWS), it is empty. 93 argRenderIdxs []int 94 // If there is a filter, the result is a single value produced by the 95 // renderNode underneath. If there is no filter, it is set to noRenderIdx. 96 filterRenderIdx int 97 98 // create instantiates the built-in execution context for the 99 // aggregation function. 100 create func(*tree.EvalContext, tree.Datums) tree.AggregateFunc 101 102 // arguments are constant expressions that can be optionally passed into an 103 // aggregator. 104 arguments tree.Datums 105 106 run aggregateFuncRun 107 } 108 109 // aggregateFuncRun contains the run-time state for one aggregation function 110 // during local execution. 111 type aggregateFuncRun struct { 112 buckets map[string]tree.AggregateFunc 113 bucketsMemAcc mon.BoundAccount 114 seen map[string]struct{} 115 } 116 117 const noRenderIdx = -1 118 119 // newAggregateFuncHolder creates an aggregateFuncHolder. 120 // 121 // If function is nil, this is an "ident" aggregation (meaning that the input is 122 // a group-by column and the "aggregation" returns its value) 123 // 124 // If the aggregation function takes no arguments (e.g. COUNT_ROWS), 125 // argRenderIdx is noRenderIdx. 126 func (n *groupNode) newAggregateFuncHolder( 127 funcName string, 128 resultType *types.T, 129 argRenderIdxs []int, 130 create func(*tree.EvalContext, tree.Datums) tree.AggregateFunc, 131 arguments tree.Datums, 132 acc mon.BoundAccount, 133 ) *aggregateFuncHolder { 134 res := &aggregateFuncHolder{ 135 funcName: funcName, 136 resultType: resultType, 137 argRenderIdxs: argRenderIdxs, 138 filterRenderIdx: noRenderIdx, 139 create: create, 140 arguments: arguments, 141 run: aggregateFuncRun{ 142 buckets: make(map[string]tree.AggregateFunc), 143 bucketsMemAcc: acc, 144 }, 145 } 146 return res 147 } 148 149 func (a *aggregateFuncHolder) hasFilter() bool { 150 return a.filterRenderIdx != noRenderIdx 151 } 152 153 // setDistinct causes a to ignore duplicate values of the argument. 154 func (a *aggregateFuncHolder) setDistinct() { 155 a.run.seen = make(map[string]struct{}) 156 } 157 158 // isDistinct returns true if only distinct values are aggregated, 159 // e.g. SUM(DISTINCT x). 160 func (a *aggregateFuncHolder) isDistinct() bool { 161 return a.run.seen != nil 162 } 163 164 func (a *aggregateFuncHolder) close(ctx context.Context) { 165 for _, aggFunc := range a.run.buckets { 166 aggFunc.Close(ctx) 167 } 168 169 a.run.buckets = nil 170 a.run.seen = nil 171 172 a.run.bucketsMemAcc.Close(ctx) 173 }