github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/execinfra/expr.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 execinfra
    12  
    13  import (
    14  	"fmt"
    15  
    16  	"github.com/cockroachdb/cockroach/pkg/sql/execinfrapb"
    17  	"github.com/cockroachdb/cockroach/pkg/sql/parser"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/sem/transform"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    20  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    22  	"github.com/cockroachdb/cockroach/pkg/util"
    23  	"github.com/cockroachdb/errors"
    24  )
    25  
    26  // ivarBinder is a tree.Visitor that binds ordinal references
    27  // (IndexedVars represented by @1, @2, ...) to an IndexedVarContainer.
    28  type ivarBinder struct {
    29  	h   *tree.IndexedVarHelper
    30  	err error
    31  }
    32  
    33  func (v *ivarBinder) VisitPre(expr tree.Expr) (recurse bool, newExpr tree.Expr) {
    34  	if v.err != nil {
    35  		return false, expr
    36  	}
    37  	if ivar, ok := expr.(*tree.IndexedVar); ok {
    38  		newVar, err := v.h.BindIfUnbound(ivar)
    39  		if err != nil {
    40  			v.err = err
    41  			return false, expr
    42  		}
    43  		return false, newVar
    44  	}
    45  	return true, expr
    46  }
    47  
    48  func (*ivarBinder) VisitPost(expr tree.Expr) tree.Expr { return expr }
    49  
    50  // processExpression parses the string expression inside an Expression,
    51  // and associates ordinal references (@1, @2, etc) with the given helper.
    52  func processExpression(
    53  	exprSpec execinfrapb.Expression,
    54  	evalCtx *tree.EvalContext,
    55  	semaCtx *tree.SemaContext,
    56  	h *tree.IndexedVarHelper,
    57  ) (tree.TypedExpr, error) {
    58  	if exprSpec.Expr == "" {
    59  		return nil, nil
    60  	}
    61  	expr, err := parser.ParseExpr(exprSpec.Expr)
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  
    66  	// Bind IndexedVars to our eh.Vars.
    67  	v := ivarBinder{h: h, err: nil}
    68  	expr, _ = tree.WalkExpr(&v, expr)
    69  	if v.err != nil {
    70  		return nil, v.err
    71  	}
    72  
    73  	semaCtx.IVarContainer = h.Container()
    74  	// Convert to a fully typed expression.
    75  	typedExpr, err := tree.TypeCheck(evalCtx.Context, expr, semaCtx, types.Any)
    76  	if err != nil {
    77  		// Type checking must succeed by now.
    78  		return nil, errors.NewAssertionErrorWithWrappedErrf(err, "%s", expr)
    79  	}
    80  
    81  	// Pre-evaluate constant expressions. This is necessary to avoid repeatedly
    82  	// re-evaluating constant values every time the expression is applied.
    83  	//
    84  	// TODO(solon): It would be preferable to enhance our expression serialization
    85  	// format so this wouldn't be necessary.
    86  	c := tree.MakeConstantEvalVisitor(evalCtx)
    87  	expr, _ = tree.WalkExpr(&c, typedExpr)
    88  	if err := c.Err(); err != nil {
    89  		return nil, err
    90  	}
    91  
    92  	return expr.(tree.TypedExpr), nil
    93  }
    94  
    95  // ExprHelper implements the common logic around evaluating an expression that
    96  // depends on a set of values.
    97  type ExprHelper struct {
    98  	_ util.NoCopy
    99  
   100  	Expr tree.TypedExpr
   101  	// Vars is used to generate IndexedVars that are "backed" by the values in
   102  	// `Row`.
   103  	Vars tree.IndexedVarHelper
   104  
   105  	evalCtx *tree.EvalContext
   106  
   107  	Types      []*types.T
   108  	Row        sqlbase.EncDatumRow
   109  	datumAlloc sqlbase.DatumAlloc
   110  }
   111  
   112  func (eh *ExprHelper) String() string {
   113  	if eh.Expr == nil {
   114  		return "none"
   115  	}
   116  	return eh.Expr.String()
   117  }
   118  
   119  // ExprHelper implements tree.IndexedVarContainer.
   120  var _ tree.IndexedVarContainer = &ExprHelper{}
   121  
   122  // IndexedVarResolvedType is part of the tree.IndexedVarContainer interface.
   123  func (eh *ExprHelper) IndexedVarResolvedType(idx int) *types.T {
   124  	return eh.Types[idx]
   125  }
   126  
   127  // IndexedVarEval is part of the tree.IndexedVarContainer interface.
   128  func (eh *ExprHelper) IndexedVarEval(idx int, ctx *tree.EvalContext) (tree.Datum, error) {
   129  	err := eh.Row[idx].EnsureDecoded(eh.Types[idx], &eh.datumAlloc)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	return eh.Row[idx].Datum.Eval(ctx)
   134  }
   135  
   136  // IndexedVarNodeFormatter is part of the parser.IndexedVarContainer interface.
   137  func (eh *ExprHelper) IndexedVarNodeFormatter(idx int) tree.NodeFormatter {
   138  	n := tree.Name(fmt.Sprintf("$%d", idx))
   139  	return &n
   140  }
   141  
   142  // Init initializes the ExprHelper.
   143  func (eh *ExprHelper) Init(
   144  	expr execinfrapb.Expression, types []*types.T, evalCtx *tree.EvalContext,
   145  ) error {
   146  	if expr.Empty() {
   147  		return nil
   148  	}
   149  	eh.evalCtx = evalCtx
   150  	eh.Types = types
   151  	eh.Vars = tree.MakeIndexedVarHelper(eh, len(types))
   152  
   153  	if expr.LocalExpr != nil {
   154  		eh.Expr = expr.LocalExpr
   155  		// Bind IndexedVars to our eh.Vars.
   156  		eh.Vars.Rebind(eh.Expr, true /* alsoReset */, false /* normalizeToNonNil */)
   157  		return nil
   158  	}
   159  	var err error
   160  	semaContext := tree.MakeSemaContext()
   161  	semaContext.TypeResolver = evalCtx.DistSQLTypeResolver
   162  	eh.Expr, err = processExpression(expr, evalCtx, &semaContext, &eh.Vars)
   163  	if err != nil {
   164  		return err
   165  	}
   166  	var t transform.ExprTransformContext
   167  	if t.AggregateInExpr(eh.Expr, evalCtx.SessionData.SearchPath) {
   168  		return errors.Errorf("expression '%s' has aggregate", eh.Expr)
   169  	}
   170  	return nil
   171  }
   172  
   173  // EvalFilter is used for filter expressions; it evaluates the expression and
   174  // returns whether the filter passes.
   175  func (eh *ExprHelper) EvalFilter(row sqlbase.EncDatumRow) (bool, error) {
   176  	eh.Row = row
   177  	eh.evalCtx.PushIVarContainer(eh)
   178  	pass, err := sqlbase.RunFilter(eh.Expr, eh.evalCtx)
   179  	eh.evalCtx.PopIVarContainer()
   180  	return pass, err
   181  }
   182  
   183  // Eval - given a row - evaluates the wrapped expression and returns the
   184  // resulting datum. For example, given a row (1, 2, 3, 4, 5):
   185  //  '@2' would return '2'
   186  //  '@2 + @5' would return '7'
   187  //  '@1' would return '1'
   188  //  '@2 + 10' would return '12'
   189  func (eh *ExprHelper) Eval(row sqlbase.EncDatumRow) (tree.Datum, error) {
   190  	eh.Row = row
   191  
   192  	eh.evalCtx.PushIVarContainer(eh)
   193  	d, err := eh.Expr.Eval(eh.evalCtx)
   194  	eh.evalCtx.PopIVarContainer()
   195  	return d, err
   196  }