func (s *testEvaluatorSuite) TestEvaluatedFlag(c *C) { l := ast.NewValueExpr(int64(1)) r := ast.NewValueExpr(int64(2)) b := &ast.BinaryOperationExpr{L: l, R: r, Op: opcode.Plus} ast.SetFlag(b) c.Assert(ast.IsPreEvaluable(b), Equals, true) d, err := Eval(s.ctx, b) c.Assert(ast.IsEvaluated(b), Equals, true) c.Assert(err, IsNil) c.Assert(d, testutil.DatumEquals, types.NewIntDatum(3)) funcCall := &ast.FuncCallExpr{ FnName: model.NewCIStr("abs"), Args: []ast.ExprNode{ast.NewValueExpr(int(-1))}, } b = &ast.BinaryOperationExpr{L: funcCall, R: r, Op: opcode.Plus} ast.ResetEvaluatedFlag(b) ast.SetFlag(b) c.Assert(ast.IsPreEvaluable(b), Equals, true) d, err = Eval(s.ctx, b) c.Assert(ast.IsEvaluated(b), Equals, false) c.Assert(err, IsNil) c.Assert(d, testutil.DatumEquals, types.NewIntDatum(3)) rf := &ast.ResultField{Expr: ast.NewValueExpr(int64(1))} colExpr := &ast.ColumnNameExpr{Refer: rf} b = &ast.BinaryOperationExpr{L: colExpr, R: r, Op: opcode.Plus} ast.ResetEvaluatedFlag(b) ast.SetFlag(b) c.Assert(ast.IsPreEvaluable(b), Equals, false) d, err = Eval(s.ctx, b) c.Assert(ast.IsEvaluated(b), Equals, false) c.Assert(err, IsNil) c.Assert(d, testutil.DatumEquals, types.NewIntDatum(3)) }
func (c *conditionChecker) check(condition ast.ExprNode) bool { switch x := condition.(type) { case *ast.BinaryOperationExpr: return c.checkBinaryOperation(x) case *ast.BetweenExpr: if ast.IsPreEvaluable(x.Left) && ast.IsPreEvaluable(x.Right) && c.checkColumnExpr(x.Expr) { return true } case *ast.ColumnNameExpr: return c.checkColumnExpr(x) case *ast.IsNullExpr: if c.checkColumnExpr(x.Expr) { return true } case *ast.IsTruthExpr: if c.checkColumnExpr(x.Expr) { return true } case *ast.ParenthesesExpr: return c.check(x.Expr) case *ast.PatternInExpr: if x.Sel != nil || x.Not { return false } if !c.checkColumnExpr(x.Expr) { return false } for _, val := range x.List { if !ast.IsPreEvaluable(val) { return false } } return true case *ast.PatternLikeExpr: if x.Not { return false } if !c.checkColumnExpr(x.Expr) { return false } if !ast.IsPreEvaluable(x.Pattern) { return false } patternVal := x.Pattern.GetValue() if patternVal == nil { return false } patternStr, err := types.ToString(patternVal) if err != nil { return false } if len(patternStr) == 0 { return true } firstChar := patternStr[0] return firstChar != '%' && firstChar != '_' } return false }
func (b *planBuilder) buildIndexScanPlan(index *model.IndexInfo, path *joinPath) Plan { tn := path.table ip := &IndexScan{Table: tn.TableInfo, Index: index, TableName: tn} ip.RefAccess = len(path.eqConds) > 0 ip.SetFields(tn.GetResultFields()) ip.TableAsName = getTableAsName(ip.Fields()) condMap := map[ast.ExprNode]bool{} for _, con := range path.conditions { condMap[con] = true } out: // Build equal access conditions first. // Starts from the first index column, if equal condition is found, add it to access conditions, // proceed to the next index column. until we can't find any equal condition for the column. for ip.AccessEqualCount < len(index.Columns) { for con := range condMap { binop, ok := con.(*ast.BinaryOperationExpr) if !ok || binop.Op != opcode.EQ { continue } if ast.IsPreEvaluable(binop.L) { binop.L, binop.R = binop.R, binop.L } if !ast.IsPreEvaluable(binop.R) { continue } cn, ok2 := binop.L.(*ast.ColumnNameExpr) if !ok2 || cn.Refer.Column.Name.L != index.Columns[ip.AccessEqualCount].Name.L { continue } ip.AccessConditions = append(ip.AccessConditions, con) delete(condMap, con) ip.AccessEqualCount++ continue out } break } for con := range condMap { if ip.AccessEqualCount < len(ip.Index.Columns) { // Try to add non-equal access condition for index column at AccessEqualCount. checker := conditionChecker{tableName: tn.TableInfo.Name, idx: index, columnOffset: ip.AccessEqualCount} if checker.check(con) { ip.AccessConditions = append(ip.AccessConditions, con) } else { ip.FilterConditions = append(ip.FilterConditions, con) } } else { ip.FilterConditions = append(ip.FilterConditions, con) } } return ip }
func (c *conditionChecker) checkBinaryOperation(b *ast.BinaryOperationExpr) bool { switch b.Op { case opcode.OrOr: return c.check(b.L) && c.check(b.R) case opcode.AndAnd: return c.check(b.L) && c.check(b.R) case opcode.EQ, opcode.NE, opcode.GE, opcode.GT, opcode.LE, opcode.LT: if ast.IsPreEvaluable(b.L) { return c.checkColumnExpr(b.R) } else if ast.IsPreEvaluable(b.R) { return c.checkColumnExpr(b.L) } } return false }
// Eval evaluates an expression to a datum. func Eval(ctx context.Context, expr ast.ExprNode) (d types.Datum, err error) { if ast.IsEvaluated(expr) { return *expr.GetDatum(), nil } e := &Evaluator{ctx: ctx} expr.Accept(e) if e.err != nil { return d, errors.Trace(e.err) } if ast.IsPreEvaluable(expr) && (expr.GetFlag()&ast.FlagHasFunc == 0) { expr.SetFlag(expr.GetFlag() | ast.FlagPreEvaluated) } return *expr.GetDatum(), nil }
func (r *preEvaluator) Leave(in ast.Node) (ast.Node, bool) { if expr, ok := in.(ast.ExprNode); ok { if _, ok = expr.(*ast.ValueExpr); ok { return in, true } else if ast.IsPreEvaluable(expr) { val, err := evaluator.Eval(r.ctx, expr) if err != nil { r.err = err return in, false } if ast.IsConstant(expr) { // The expression is constant, rewrite the expression to value expression. valExpr := &ast.ValueExpr{} valExpr.SetText(expr.Text()) valExpr.SetType(expr.GetType()) valExpr.SetValue(val) return valExpr, true } expr.SetValue(val) } } return in, true }