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 }