diff --git a/executor/adapter.go b/executor/adapter.go index 96080592f0a8b..be41e2ce0a173 100644 --- a/executor/adapter.go +++ b/executor/adapter.go @@ -138,6 +138,9 @@ func (a *statementAdapter) IsDDL() bool { func (a *statementAdapter) Exec(ctx context.Context) (rset.Recordset, error) { b := newExecutorBuilder(ctx, a.is) e := b.build(a.plan) + if b.err != nil { + return nil, errors.Trace(b.err) + } fields := make([]*field.ResultField, 0, len(e.Fields())) for _, v := range e.Fields() { f := &field.ResultField{ diff --git a/executor/builder.go b/executor/builder.go index 19896a3e8fb09..3df1bd71971f8 100644 --- a/executor/builder.go +++ b/executor/builder.go @@ -14,7 +14,6 @@ package executor import ( - "github.com/ngaut/log" "github.com/pingcap/tidb/ast" "github.com/pingcap/tidb/column" "github.com/pingcap/tidb/context" @@ -29,6 +28,7 @@ import ( type executorBuilder struct { ctx context.Context is infoschema.InfoSchema + err error } func newExecutorBuilder(ctx context.Context, is infoschema.InfoSchema) *executorBuilder { @@ -56,9 +56,10 @@ func (b *executorBuilder) build(p plan.Plan) Executor { return b.buildSort(v) case *plan.Limit: return b.buildLimit(v) + default: + b.err = ErrUnknownPlan.Gen("Unknown Plan %T", p) + return nil } - log.Fatalf("Unknown Plan %T", p) - return nil } func (b *executorBuilder) buildTableScan(v *plan.TableScan) Executor { diff --git a/executor/executor.go b/executor/executor.go index 626dc6df2caa2..fc7804591349e 100644 --- a/executor/executor.go +++ b/executor/executor.go @@ -18,7 +18,6 @@ import ( "strings" "github.com/juju/errors" - "github.com/ngaut/log" "github.com/pingcap/tidb/ast" "github.com/pingcap/tidb/context" "github.com/pingcap/tidb/kv" @@ -26,6 +25,7 @@ import ( "github.com/pingcap/tidb/optimizer/plan" "github.com/pingcap/tidb/sessionctx/forupdate" "github.com/pingcap/tidb/table" + "github.com/pingcap/tidb/terror" "github.com/pingcap/tidb/util" "github.com/pingcap/tidb/util/types" ) @@ -40,6 +40,16 @@ var ( _ Executor = &SortExec{} ) +// Error instances. +var ( + ErrUnknownPlan = terror.ClassExecutor.New(CodeUnknownPlan, "Unknown plan") +) + +// Error codes. +const ( + CodeUnknownPlan terror.ErrCode = iota + 1 +) + // Row represents a record row. type Row struct { // Data is the output record data for current Plan. @@ -195,13 +205,20 @@ func (e *IndexRangeExec) Next() (*Row, error) { } val := idxKey[0] if !e.skipLowCmp { - cmp := indexCompare(val, e.lowVal) + var cmp int + cmp, err = indexCompare(val, e.lowVal) + if err != nil { + return nil, errors.Trace(err) + } if cmp < 0 || (cmp == 0 && e.lowExclude) { continue } e.skipLowCmp = true } - cmp := indexCompare(val, e.highVal) + cmp, err := indexCompare(val, e.highVal) + if err != nil { + return nil, errors.Trace(err) + } if cmp > 0 || (cmp == 0 && e.highExclude) { // This span has finished iteration. e.finished = true @@ -217,40 +234,38 @@ func (e *IndexRangeExec) Next() (*Row, error) { } // comparison function that takes minNotNullVal and maxVal into account. -func indexCompare(a interface{}, b interface{}) int { +func indexCompare(a interface{}, b interface{}) (int, error) { if a == nil && b == nil { - return 0 + return 0, nil } else if b == nil { - return 1 + return 1, nil } else if a == nil { - return -1 + return -1, nil } // a and b both not nil if a == plan.MinNotNullVal && b == plan.MinNotNullVal { - return 0 + return 0, nil } else if b == plan.MinNotNullVal { - return 1 + return 1, nil } else if a == plan.MinNotNullVal { - return -1 + return -1, nil } // a and b both not min value if a == plan.MaxVal && b == plan.MaxVal { - return 0 + return 0, nil } else if a == plan.MaxVal { - return 1 + return 1, nil } else if b == plan.MaxVal { - return -1 + return -1, nil } n, err := types.Compare(a, b) if err != nil { - // Old compare panics if err, so here we do the same thing now. - // TODO: return err instead of panic. - log.Fatalf("should never happend %v", err) + return 0, errors.Trace(err) } - return n + return n, nil } func (e *IndexRangeExec) lookupRow(h int64) (*Row, error) { diff --git a/optimizer/evaluator/evaluator_binop.go b/optimizer/evaluator/evaluator_binop.go index cf658099c0ee6..d878b81a5f495 100644 --- a/optimizer/evaluator/evaluator_binop.go +++ b/optimizer/evaluator/evaluator_binop.go @@ -17,7 +17,6 @@ import ( "math" "github.com/juju/errors" - "github.com/ngaut/log" "github.com/pingcap/tidb/ast" "github.com/pingcap/tidb/mysql" "github.com/pingcap/tidb/parser/opcode" @@ -41,7 +40,7 @@ func (e *Evaluator) binaryOperation(o *ast.BinaryOperationExpr) bool { case opcode.Plus, opcode.Minus, opcode.Mod, opcode.Div, opcode.Mul, opcode.IntDiv: return e.handleArithmeticOp(o) default: - e.err = errors.New("invalid operation") + e.err = ErrInvalidOperation return false } } @@ -55,7 +54,7 @@ func (e *Evaluator) handleLogicOperation(o *ast.BinaryOperationExpr) bool { case opcode.LogicXor: return e.handleXor(o) default: - log.Fatalf("unkown operator %v", o.Op) + e.err = ErrInvalidOperation.Gen("unkown operator %s", o.Op) return false } } @@ -203,7 +202,7 @@ func getCompResult(op opcode.Op, value int) (bool, error) { case opcode.NullEQ: return value == 0, nil default: - return false, errors.Errorf("invalid op %v in comparision operation", op) + return false, ErrInvalidOperation.Gen("invalid op %v in comparision operation", op) } } @@ -240,7 +239,7 @@ func (e *Evaluator) handleBitOp(o *ast.BinaryOperationExpr) bool { case opcode.LeftShift: o.SetValue(uint64(x) << uint64(y)) default: - e.err = errors.Errorf("invalid op %v in bit operation", o.Op) + e.err = ErrInvalidOperation.Gen("invalid op %v in bit operation", o.Op) return false } return true @@ -282,7 +281,7 @@ func (e *Evaluator) handleArithmeticOp(o *ast.BinaryOperationExpr) bool { case opcode.IntDiv: result, e.err = computeIntDiv(a, b) default: - e.err = errors.Errorf("invalid op %v in arithmetic operation", o.Op) + e.err = ErrInvalidOperation.Gen("invalid op %v in arithmetic operation", o.Op) return false } o.SetValue(result) diff --git a/optimizer/optimizer.go b/optimizer/optimizer.go index d5061e95aac5a..e063e4f929ff7 100644 --- a/optimizer/optimizer.go +++ b/optimizer/optimizer.go @@ -39,11 +39,17 @@ func Optimize(is infoschema.InfoSchema, ctx context.Context, node ast.Node) (pla if err := rewriteStatic(ctx, node); err != nil { return nil, errors.Trace(err) } - p := plan.BuildPlan(node) + p, err := plan.BuildPlan(node) + if err != nil { + return nil, errors.Trace(err) + } bestCost := plan.EstimateCost(p) bestPlan := p - alts := plan.Alternatives(p) + alts, err := plan.Alternatives(p) + if err != nil { + return nil, errors.Trace(err) + } for _, alt := range alts { cost := plan.EstimateCost(alt) if cost < bestCost { diff --git a/optimizer/plan/alternatives.go b/optimizer/plan/alternatives.go index 5a2444f408b19..8750ee2a86e63 100644 --- a/optimizer/plan/alternatives.go +++ b/optimizer/plan/alternatives.go @@ -13,27 +13,32 @@ package plan -import ( - "github.com/ngaut/log" -) +import "github.com/juju/errors" // Alternatives returns multiple alternative plans that // can be picked base on their cost. -func Alternatives(p Plan) []Plan { +func Alternatives(p Plan) ([]Plan, error) { var plans []Plan switch x := p.(type) { case nil: case *TableScan: plans = tableScanAlternatives(x) case WithSrcPlan: - plans = planWithSrcAlternatives(x) + var err error + plans, err = planWithSrcAlternatives(x) + if err != nil { + return nil, errors.Trace(err) + } default: - log.Fatalf("unknown plan %T", p) + return nil, ErrUnsupportedType.Gen("Unknown plan %T", p) } for _, val := range plans { - refine(val) + err := refine(val) + if err != nil { + return nil, errors.Trace(err) + } } - return plans + return plans, nil } // tableScanAlternatives returns all index plans from the same table. @@ -57,14 +62,17 @@ func tableScanAlternatives(p *TableScan) []Plan { // planWithSrcAlternatives shallow copies the WithSrcPlan, // and set its src to src alternatives. -func planWithSrcAlternatives(p WithSrcPlan) []Plan { - srcs := Alternatives(p.Src()) +func planWithSrcAlternatives(p WithSrcPlan) ([]Plan, error) { + srcs, err := Alternatives(p.Src()) + if err != nil { + return nil, errors.Trace(err) + } for i, val := range srcs { alt := shallowCopy(p) alt.SetSrc(val) srcs[i] = alt } - return srcs + return srcs, nil } func shallowCopy(p WithSrcPlan) WithSrcPlan { diff --git a/optimizer/plan/explainer.go b/optimizer/plan/explainer.go index eccbf9efcfb84..6fac3a57cc2fc 100644 --- a/optimizer/plan/explainer.go +++ b/optimizer/plan/explainer.go @@ -16,19 +16,18 @@ package plan import ( "fmt" "strings" - - "github.com/ngaut/log" ) // Explain explains a Plan, returns description string. -func Explain(p Plan) string { +func Explain(p Plan) (string, error) { var e explainer p.Accept(&e) - return strings.Join(e.strs, "->") + return strings.Join(e.strs, "->"), e.err } type explainer struct { strs []string + err error } func (e *explainer) Enter(in Plan) (Plan, bool) { @@ -56,7 +55,8 @@ func (e *explainer) Leave(in Plan) (Plan, bool) { case *Limit: str = "Limit" default: - log.Fatalf("Unknown plan type %T", in) + e.err = ErrUnsupportedType.Gen("Unknown plan type %T", in) + return in, false } e.strs = append(e.strs, str) return in, true diff --git a/optimizer/plan/plan_test.go b/optimizer/plan/plan_test.go index 91ec3a1223d6a..6271ebe4bf504 100644 --- a/optimizer/plan/plan_test.go +++ b/optimizer/plan/plan_test.go @@ -194,6 +194,7 @@ func (s *testPlanSuite) TestRangeBuilder(c *C) { c.Assert(rc, Equals, 0, Commentf("error %v, for expr %", lexer.Errors(), ca.exprStr)) stmt := lexer.Stmts()[0].(*ast.SelectStmt) result := rb.build(stmt.Where) + c.Assert(rb.err, IsNil) got := fmt.Sprintf("%v", result) c.Assert(got, Equals, ca.resultStr, Commentf("differen for expr %s", ca.exprStr)) } @@ -240,8 +241,11 @@ func (s *testPlanSuite) TestBuilder(c *C) { c.Assert(rc, Equals, 0, Commentf("error %v for expr %s", lexer.Errors(), ca.sqlStr)) stmt := lexer.Stmts()[0].(*ast.SelectStmt) mockResolve(stmt) - p := BuildPlan(stmt) - c.Assert(ca.planStr, Equals, Explain(p)) + p, err := BuildPlan(stmt) + c.Assert(err, IsNil) + explainStr, err := Explain(p) + c.Assert(err, IsNil) + c.Assert(ca.planStr, Equals, explainStr) } } @@ -298,10 +302,12 @@ func (s *testPlanSuite) TestBestPlan(c *C) { c.Assert(rc, Equals, 0, Commentf("error %v for sql %s", lexer.Errors(), ca.sql)) stmt := lexer.Stmts()[0].(*ast.SelectStmt) mockResolve(stmt) - p := BuildPlan(stmt) + p, err := BuildPlan(stmt) + c.Assert(err, IsNil) bestCost := EstimateCost(p) bestPlan := p - alts := Alternatives(p) + alts, err := Alternatives(p) + c.Assert(err, IsNil) for _, alt := range alts { cost := EstimateCost(alt) if cost < bestCost { @@ -309,7 +315,9 @@ func (s *testPlanSuite) TestBestPlan(c *C) { bestPlan = alt } } - c.Assert(Explain(bestPlan), Equals, ca.best, Commentf("for %s cost %v", ca.sql, bestCost)) + explainStr, err := Explain(bestPlan) + c.Assert(err, IsNil) + c.Assert(explainStr, Equals, ca.best, Commentf("for %s cost %v", ca.sql, bestCost)) } } diff --git a/optimizer/plan/planbuilder.go b/optimizer/plan/planbuilder.go index 33ee28da9abef..55bd0803d0782 100644 --- a/optimizer/plan/planbuilder.go +++ b/optimizer/plan/planbuilder.go @@ -14,22 +14,37 @@ package plan import ( - "github.com/ngaut/log" "github.com/pingcap/tidb/ast" "github.com/pingcap/tidb/parser/opcode" + "github.com/pingcap/tidb/terror" +) + +// Error instances. +var ( + ErrUnsupportedType = terror.ClassOptimizerPlan.New(CodeUnsupportedType, "Unsupported type") +) + +// Error codes. +const ( + CodeUnsupportedType = iota + 1 ) // BuildPlan builds a plan from a node. -func BuildPlan(node ast.Node) Plan { +// returns ErrUnsupportedType if ast.Node type is not supported yet. +func BuildPlan(node ast.Node) (Plan, error) { var builder planBuilder p := builder.build(node) - refine(p) - return p + if builder.err != nil { + return nil, builder.err + } + err := refine(p) + return p, err } // planBuilder builds Plan from an ast.Node. // It just build the ast node straightforwardly. type planBuilder struct { + err error } func (b *planBuilder) build(node ast.Node) Plan { @@ -37,7 +52,7 @@ func (b *planBuilder) build(node ast.Node) Plan { case *ast.SelectStmt: return b.buildSelect(x) } - log.Fatalf("Unsupported type %T", node) + b.err = ErrUnsupportedType.Gen("Unsupported type %T", node) return nil } @@ -45,24 +60,48 @@ func (b *planBuilder) buildSelect(sel *ast.SelectStmt) Plan { var p Plan if sel.From != nil { p = b.buildJoin(sel.From.TableRefs) + if b.err != nil { + return nil + } if sel.Where != nil { p = b.buildFilter(p, sel.Where) + if b.err != nil { + return nil + } } if sel.LockTp != ast.SelectLockNone { p = b.buildSelectLock(p, sel.LockTp) + if b.err != nil { + return nil + } } p = b.buildSelectFields(p, sel.GetResultFields()) + if b.err != nil { + return nil + } } else { p = b.buildSelectFields(p, sel.GetResultFields()) + if b.err != nil { + return nil + } if sel.Where != nil { p = b.buildFilter(p, sel.Where) + if b.err != nil { + return nil + } } } if sel.OrderBy != nil { p = b.buildSort(p, sel.OrderBy.Items) + if b.err != nil { + return nil + } } if sel.Limit != nil { p = b.buildLimit(p, sel.Limit) + if b.err != nil { + return nil + } } return p } @@ -71,11 +110,13 @@ func (b *planBuilder) buildJoin(from *ast.Join) Plan { // Only support single table for now. ts, ok := from.Left.(*ast.TableSource) if !ok { - log.Fatalf("Unsupported type %T", from.Left) + b.err = ErrUnsupportedType.Gen("Unsupported type %T", from.Left) + return nil } tn, ok := ts.Source.(*ast.TableName) if !ok { - log.Fatalf("Unsupported type %T", ts.Source) + b.err = ErrUnsupportedType.Gen("Unsupported type %T", ts.Source) + return nil } p := &TableScan{ Table: tn.TableInfo, diff --git a/optimizer/plan/range.go b/optimizer/plan/range.go index 330ceecd98615..5e16fe19e9c28 100644 --- a/optimizer/plan/range.go +++ b/optimizer/plan/range.go @@ -17,7 +17,6 @@ import ( "fmt" "sort" - "github.com/ngaut/log" "github.com/pingcap/tidb/ast" "github.com/pingcap/tidb/parser/opcode" "github.com/pingcap/tidb/util/types" @@ -195,8 +194,8 @@ func (r *rangeBuilder) buildFromBinop(x *ast.BinaryOperationExpr) []rangePoint { func (r *rangeBuilder) buildFromIn(x *ast.PatternInExpr) []rangePoint { if x.Not { - // NOT in is not supported. - log.Fatal("NOT IN is not supported") + r.err = ErrUnsupportedType.Gen("NOT IN is not supported") + return fullRange } var rangePoints []rangePoint for _, v := range x.List { @@ -268,7 +267,8 @@ func (r *rangeBuilder) buildFromIsTruth(x *ast.IsTruthExpr) []rangePoint { func (r *rangeBuilder) buildFromPatternLike(x *ast.PatternLikeExpr) []rangePoint { if x.Not { // Pattern not like is not supported. - log.Fatal("NOT LIKE is not supported.") + r.err = ErrUnsupportedType.Gen("NOT LIKE is not supported.") + return fullRange } pattern := x.Pattern.GetValue().(string) lowValue := make([]byte, 0, len(pattern)) diff --git a/optimizer/plan/refiner.go b/optimizer/plan/refiner.go index 345f68347370b..68f3570d50aec 100644 --- a/optimizer/plan/refiner.go +++ b/optimizer/plan/refiner.go @@ -19,9 +19,10 @@ import ( "github.com/pingcap/tidb/parser/opcode" ) -func refine(p Plan) { +func refine(p Plan) error { r := refiner{} p.Accept(&r) + return r.err } // refiner tries to build index range, bypass sort, set limit for source plan. @@ -30,6 +31,7 @@ type refiner struct { conditions []ast.ExprNode // store index scan plan for sort to use. indexScan *IndexScan + err error } func (r *refiner) Enter(in Plan) (Plan, bool) { @@ -51,7 +53,7 @@ func (r *refiner) Leave(in Plan) (Plan, bool) { case *Limit: x.SetLimit(0) } - return in, true + return in, r.err == nil } func (r *refiner) sortBypass(p *Sort) { @@ -116,6 +118,8 @@ func (r *refiner) buildIndexRange(p *IndexScan) { p.Ranges = rb.appendIndexRanges(p.Ranges, rangePoints) } } + r.err = rb.err + return } // conditionChecker checks if this condition can be pushed to index plan. diff --git a/optimizer/validator.go b/optimizer/validator.go index a5564a5183581..7bd5196321b45 100644 --- a/optimizer/validator.go +++ b/optimizer/validator.go @@ -25,6 +25,7 @@ const ( CodeRowColumns CodeSameColumns CodeMultiWildCard + CodeUnsupported ) // Optimizer base errors. @@ -33,6 +34,7 @@ var ( ErrRowColumns = terror.ClassOptimizer.New(CodeRowColumns, "Operand should contain >= 2 columns for Row") ErrSameColumns = terror.ClassOptimizer.New(CodeRowColumns, "Operands should contain same columns") ErrMultiWildCard = terror.ClassOptimizer.New(CodeMultiWildCard, "wildcard field exist more than once") + ErrUnSupported = terror.ClassOptimizer.New(CodeUnsupported, "unsupported") ) // validate checkes whether the node is valid. diff --git a/terror/terror.go b/terror/terror.go index e7238080ed5d1..2d2dfb95c5778 100644 --- a/terror/terror.go +++ b/terror/terror.go @@ -71,6 +71,7 @@ const ( ClassParser ErrClass = iota + 1 ClassSchema ClassOptimizer + ClassOptimizerPlan ClassExecutor ClassEvaluator ClassKV