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  }