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  }