Skip to content

Commit

Permalink
evaluator: just one time eval (pingcap#1062)
Browse files Browse the repository at this point in the history
* evaluator: just one time eval
  • Loading branch information
zxylvlp committed Apr 12, 2016
1 parent 457b8c7 commit bebacf4
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 12 deletions.
1 change: 1 addition & 0 deletions ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const (
FlagHasSubquery
FlagHasVariable
FlagHasDefault
FlagPreEvaluated
)

// ExprNode is a node that can be evaluated.
Expand Down
40 changes: 39 additions & 1 deletion ast/flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,28 @@

package ast

const preEvaluable = FlagHasParamMarker | FlagHasFunc | FlagHasVariable | FlagHasDefault
const preEvaluable = FlagHasParamMarker | FlagHasFunc | FlagHasVariable | FlagHasDefault | FlagPreEvaluated

// IsPreEvaluable checks if the expression can be evaluated before execution.
func IsPreEvaluable(expr ExprNode) bool {
return expr.GetFlag()|preEvaluable == preEvaluable
}

// ResetEvaluatedFlag resets the evaluated flag.
func ResetEvaluatedFlag(n Node) {
var reseter preEvaluatedReseter
n.Accept(&reseter)
}

func resetEvaluatedExpr(expr ExprNode) {
expr.SetFlag(expr.GetFlag() &^ FlagPreEvaluated)
}

// IsEvaluated checks if the expression are evaluated and needn't be evaluated any more.
func IsEvaluated(expr ExprNode) bool {
return expr.GetFlag()&FlagPreEvaluated == FlagPreEvaluated
}

// IsConstant checks if the expression is constant.
// A constant expression is safe to be rewritten to value expression.
func IsConstant(expr ExprNode) bool {
Expand All @@ -31,6 +46,20 @@ func HasAggFlag(expr ExprNode) bool {
return expr.GetFlag()&FlagHasAggregateFunc > 0
}

type preEvaluatedReseter struct {
}

func (r *preEvaluatedReseter) Enter(in Node) (Node, bool) {
return in, false
}

func (r *preEvaluatedReseter) Leave(in Node) (Node, bool) {
if expr, ok := in.(ExprNode); ok {
resetEvaluatedExpr(expr)
}
return in, true
}

// SetFlag sets flag for expression.
func SetFlag(n Node) {
var setter flagSetter
Expand Down Expand Up @@ -163,3 +192,12 @@ func (f *flagSetter) aggregateFunc(x *AggregateFuncExpr) {
}
x.SetFlag(flag)
}

// MergeChildrenFlags sets flag to parent by children.
func MergeChildrenFlags(parent ExprNode, children ...ExprNode) {
var flag uint64
for _, child := range children {
flag |= child.GetFlag()
}
parent.SetFlag(flag &^ FlagPreEvaluated)
}
1 change: 1 addition & 0 deletions ast/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ func (a *AggregateFuncExtractor) Leave(n Node) (node Node, ok bool) {
F: AggFuncFirstRow,
Args: []ExprNode{v},
}
agg.SetFlag((v.GetFlag() | FlagHasAggregateFunc))
a.AggFuncs = append(a.AggFuncs, agg)
return agg, true
}
Expand Down
1 change: 1 addition & 0 deletions evaluator/builtin_time_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ func (s *testEvaluatorSuite) TestDateArith(c *C) {
dateArithInterval,
},
}
ast.SetFlag(expr)
v, err := Eval(ctx, expr)
if t.error == true {
c.Assert(err, NotNil)
Expand Down
18 changes: 11 additions & 7 deletions evaluator/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,23 @@ const (

// Eval evaluates an expression to a value.
func Eval(ctx context.Context, expr ast.ExprNode) (interface{}, error) {
e := &Evaluator{ctx: ctx}
expr.Accept(e)
if e.err != nil {
return nil, errors.Trace(e.err)
}
return expr.GetValue(), nil
d, err := EvalDatum(ctx, expr)
return d.GetValue(), err
}

// EvalDatum evaluates an expression to a datum.
func EvalDatum(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
}

Expand Down Expand Up @@ -182,8 +184,10 @@ func (e *Evaluator) between(v *ast.BetweenExpr) bool {
l = &ast.BinaryOperationExpr{Op: opcode.GE, L: v.Expr, R: v.Left}
r = &ast.BinaryOperationExpr{Op: opcode.LE, L: v.Expr, R: v.Right}
}

ast.MergeChildrenFlags(l, v.Expr, v.Left)
ast.MergeChildrenFlags(l, v.Expr, v.Right)
ret := &ast.BinaryOperationExpr{Op: op, L: l, R: r}
ast.MergeChildrenFlags(ret, l, r)
ret.Accept(e)
if e.err != nil {
return false
Expand Down
43 changes: 42 additions & 1 deletion evaluator/evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ func (s *testEvaluatorSuite) TestCaseWhen(c *C) {
c.Assert(err, IsNil)
c.Assert(v, Equals, int64(1))
valExpr.SetValue(4)
ast.ResetEvaluatedFlag(caseExpr)
v, err = Eval(ctx, caseExpr)
c.Assert(err, IsNil)
c.Assert(v, IsNil)
Expand Down Expand Up @@ -418,6 +419,7 @@ func (s *testEvaluatorSuite) TestCast(c *C) {
Expr: ast.NewValueExpr(1),
Tp: f,
}
ast.SetFlag(expr)
ctx := mock.NewContext()
v, err := Eval(ctx, expr)
c.Assert(err, IsNil)
Expand Down Expand Up @@ -788,8 +790,8 @@ func (s *testEvaluatorSuite) TestUnaryOp(c *C) {
{mysql.Set{Name: "a", Value: 1}, opcode.Minus, -1.0},
}
ctx := mock.NewContext()
expr := &ast.UnaryOperationExpr{}
for i, t := range tbl {
expr := &ast.UnaryOperationExpr{}
expr.Op = t.op
expr.V = ast.NewValueExpr(t.arg)
result, err := Eval(ctx, expr)
Expand Down Expand Up @@ -831,6 +833,7 @@ func (s *testEvaluatorSuite) TestColumnNameExpr(c *C) {
rf := &ast.ResultField{Expr: value1}
expr := &ast.ColumnNameExpr{Refer: rf}

ast.SetFlag(expr)
result, err := Eval(ctx, expr)
c.Assert(err, IsNil)
c.Assert(result, Equals, int64(1))
Expand All @@ -849,6 +852,7 @@ func (s *testEvaluatorSuite) TestAggFuncAvg(c *C) {
F: ast.AggFuncAvg,
}
avg.CurrentGroup = "emptyGroup"
ast.SetFlag(avg)
result, err := Eval(ctx, avg)
c.Assert(err, IsNil)
// Empty group should return nil.
Expand Down Expand Up @@ -957,3 +961,40 @@ func (s *testEvaluatorSuite) TestIsCurrentTimeExpr(c *C) {
v = IsCurrentTimeExpr(&ast.FuncCallExpr{FnName: model.NewCIStr("CURRENT_TIMESTAMP")})
c.Assert(v, IsTrue)
}

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)
ctx := mock.NewContext()
d, err := EvalDatum(ctx, b)
c.Assert(ast.IsEvaluated(b), Equals, true)
c.Assert(err, IsNil)
c.Assert(d, 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 = EvalDatum(ctx, b)
c.Assert(ast.IsEvaluated(b), Equals, false)
c.Assert(err, IsNil)
c.Assert(d, 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 = EvalDatum(ctx, b)
c.Assert(ast.IsEvaluated(b), Equals, false)
c.Assert(err, IsNil)
c.Assert(d, DatumEquals, types.NewIntDatum(3))
}
2 changes: 2 additions & 0 deletions executor/aggregate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ func (s *testAggFuncSuite) TestCount(c *C) {
AggFuncs: []*ast.AggregateFuncExpr{fc1, fc2},
Src: src,
}
ast.SetFlag(fc1)
ast.SetFlag(fc2)
var (
row *Row
cnt int
Expand Down
1 change: 1 addition & 0 deletions executor/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ func (b *executorBuilder) joinConditions(conditions []ast.ExprNode) ast.ExprNode
L: conditions[0],
R: b.joinConditions(conditions[1:]),
}
ast.MergeChildrenFlags(condition, condition.L, condition.R)
return condition
}

Expand Down
1 change: 1 addition & 0 deletions executor/prepared.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ func (e *ExecuteExec) Build() error {
prepared.Params[i].SetValue(val)
}

ast.ResetEvaluatedFlag(prepared.Stmt)
if prepared.SchemaVersion != e.IS.SchemaMetaVersion() {
// If the schema version has changed we need to prepare it again,
// if this time it failed, the real reason for the error is schema changed.
Expand Down
15 changes: 12 additions & 3 deletions optimizer/plan/planbuilder_join.go
Original file line number Diff line number Diff line change
Expand Up @@ -743,7 +743,14 @@ func (b *planBuilder) buildPlanFromJoinPath(path *joinPath) Plan {
}
join.Conditions = path.conditions
for _, equiv := range path.eqConds {
cond := &ast.BinaryOperationExpr{L: equiv.left.Expr, R: equiv.right.Expr, Op: opcode.EQ}
columnNameExpr := &ast.ColumnNameExpr{}
columnNameExpr.Name = &ast.ColumnName{}
columnNameExpr.Name.Name = equiv.left.Column.Name
columnNameExpr.Name.Table = equiv.left.Table.Name
columnNameExpr.Refer = equiv.left
ast.SetFlag(columnNameExpr)
cond := &ast.BinaryOperationExpr{L: columnNameExpr, R: equiv.right.Expr, Op: opcode.EQ}
ast.MergeChildrenFlags(cond, columnNameExpr, equiv.right.Expr)
join.Conditions = append(join.Conditions, cond)
}
return join
Expand All @@ -757,8 +764,9 @@ func (b *planBuilder) buildTablePlanFromJoinPath(path *joinPath) Plan {
columnNameExpr.Name.Table = equiv.left.Table.Name
columnNameExpr.Refer = equiv.left
columnNameExpr.Type = equiv.left.Expr.GetType()
ast.SetFlag(columnNameExpr)
condition := &ast.BinaryOperationExpr{L: columnNameExpr, R: equiv.right.Expr, Op: opcode.EQ}
ast.SetFlag(condition)
ast.MergeChildrenFlags(condition, columnNameExpr, equiv.right.Expr)
path.conditions = append(path.conditions, condition)
}
candidates := b.buildAllAccessMethodsPlan(path)
Expand Down Expand Up @@ -787,8 +795,9 @@ func (b *planBuilder) buildSubqueryJoinPath(path *joinPath) Plan {
columnNameExpr.Name.Table = equiv.left.Table.Name
columnNameExpr.Refer = equiv.left
columnNameExpr.Type = equiv.left.Expr.GetType()
ast.SetFlag(columnNameExpr)
condition := &ast.BinaryOperationExpr{L: columnNameExpr, R: equiv.right.Expr, Op: opcode.EQ}
ast.SetFlag(condition)
ast.MergeChildrenFlags(condition, columnNameExpr, equiv.right.Expr)
path.conditions = append(path.conditions, condition)
}
p := b.build(path.subquery)
Expand Down
20 changes: 20 additions & 0 deletions session_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,26 @@ func (s *testSessionSuite) TestPrepare(c *C) {
c.Assert(err, IsNil)
mustExecSQL(c, se, s.dropDBSQL)

mustExecSQL(c, se, "prepare stmt from 'select 1+?'")
mustExecSQL(c, se, "set @v1=100")
rs := mustExecSQL(c, se, "execute stmt using @v1")
r, err := rs.Next()
c.Assert(err, IsNil)
c.Assert(r.Data[0].GetFloat64(), Equals, float64(101))

mustExecSQL(c, se, "set @v2=200")
rs = mustExecSQL(c, se, "execute stmt using @v2")
r, err = rs.Next()
c.Assert(err, IsNil)
c.Assert(r.Data[0].GetFloat64(), Equals, float64(201))

mustExecSQL(c, se, "set @v3=300")
rs = mustExecSQL(c, se, "execute stmt using @v3")
r, err = rs.Next()
c.Assert(err, IsNil)
c.Assert(r.Data[0].GetFloat64(), Equals, float64(301))
mustExecSQL(c, se, "deallocate prepare stmt")

err = store.Close()
c.Assert(err, IsNil)
}
Expand Down

0 comments on commit bebacf4

Please sign in to comment.