From 70d7ce7270889f790507f70851d7628964313fd8 Mon Sep 17 00:00:00 2001 From: Jian Zhang Date: Fri, 12 Oct 2018 09:53:26 +0800 Subject: [PATCH 1/2] *: add session variable "tidb_enable_cascades_planner" --- executor/adapter.go | 3 +- executor/compiler.go | 3 +- executor/executor_test.go | 3 +- executor/metrics_test.go | 3 +- executor/prepared.go | 3 +- executor/trace.go | 3 +- planner/core/common_plans.go | 4 +- planner/core/expression_rewriter.go | 20 +++---- planner/core/logical_plan_builder.go | 82 +++++++++++++------------- planner/core/logical_plan_test.go | 12 ++-- planner/core/optimizer.go | 48 +++------------- planner/core/planbuilder.go | 86 +++++++++++++++++----------- planner/core/point_get_plan.go | 2 +- planner/optimize.go | 74 ++++++++++++++++++++++++ session/session.go | 1 + sessionctx/variable/session.go | 5 ++ sessionctx/variable/sysvar.go | 1 + sessionctx/variable/tidb_vars.go | 3 + sessionctx/variable/varsutil.go | 2 +- 19 files changed, 215 insertions(+), 143 deletions(-) create mode 100644 planner/optimize.go diff --git a/executor/adapter.go b/executor/adapter.go index 9fde06cfa67bd..7dbce4214ab7a 100644 --- a/executor/adapter.go +++ b/executor/adapter.go @@ -28,6 +28,7 @@ import ( "github.com/pingcap/tidb/metrics" "github.com/pingcap/tidb/model" "github.com/pingcap/tidb/mysql" + "github.com/pingcap/tidb/planner" plannercore "github.com/pingcap/tidb/planner/core" "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/sessionctx/variable" @@ -174,7 +175,7 @@ func (a *ExecStmt) RebuildPlan() (int64, error) { if err := plannercore.Preprocess(a.Ctx, a.StmtNode, is, false); err != nil { return 0, errors.Trace(err) } - p, err := plannercore.Optimize(a.Ctx, a.StmtNode, is) + p, err := planner.Optimize(a.Ctx, a.StmtNode, is) if err != nil { return 0, errors.Trace(err) } diff --git a/executor/compiler.go b/executor/compiler.go index a56ed97462282..3db47c7070904 100644 --- a/executor/compiler.go +++ b/executor/compiler.go @@ -21,6 +21,7 @@ import ( "github.com/pingcap/tidb/config" "github.com/pingcap/tidb/infoschema" "github.com/pingcap/tidb/metrics" + "github.com/pingcap/tidb/planner" plannercore "github.com/pingcap/tidb/planner/core" "github.com/pingcap/tidb/sessionctx" "github.com/pkg/errors" @@ -45,7 +46,7 @@ func (c *Compiler) Compile(ctx context.Context, stmtNode ast.StmtNode) (*ExecStm return nil, errors.Trace(err) } - finalPlan, err := plannercore.Optimize(c.Ctx, stmtNode, infoSchema) + finalPlan, err := planner.Optimize(c.Ctx, stmtNode, infoSchema) if err != nil { return nil, errors.Trace(err) } diff --git a/executor/executor_test.go b/executor/executor_test.go index a97161d41de60..74f6800e25cf8 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -37,6 +37,7 @@ import ( "github.com/pingcap/tidb/model" "github.com/pingcap/tidb/mysql" "github.com/pingcap/tidb/parser" + "github.com/pingcap/tidb/planner" plannercore "github.com/pingcap/tidb/planner/core" "github.com/pingcap/tidb/session" "github.com/pingcap/tidb/sessionctx" @@ -1731,7 +1732,7 @@ func (s *testSuite) TestIsPointGet(c *C) { c.Check(err, IsNil) err = plannercore.Preprocess(ctx, stmtNode, infoSchema, false) c.Check(err, IsNil) - p, err := plannercore.Optimize(ctx, stmtNode, infoSchema) + p, err := planner.Optimize(ctx, stmtNode, infoSchema) c.Check(err, IsNil) ret := executor.IsPointGetWithPKOrUniqueKeyByAutoCommit(ctx, p) c.Assert(ret, Equals, result) diff --git a/executor/metrics_test.go b/executor/metrics_test.go index daf4c6fd9a5fc..13a17a619701e 100644 --- a/executor/metrics_test.go +++ b/executor/metrics_test.go @@ -19,6 +19,7 @@ import ( . "github.com/pingcap/check" "github.com/pingcap/tidb/executor" "github.com/pingcap/tidb/parser" + "github.com/pingcap/tidb/planner" plannercore "github.com/pingcap/tidb/planner/core" "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/util/testkit" @@ -62,7 +63,7 @@ func (s *testSuite) TestStmtLabel(c *C) { is := executor.GetInfoSchema(tk.Se) err = plannercore.Preprocess(tk.Se.(sessionctx.Context), stmtNode, is, false) c.Assert(err, IsNil) - _, err = plannercore.Optimize(tk.Se, stmtNode, is) + _, err = planner.Optimize(tk.Se, stmtNode, is) c.Assert(err, IsNil) c.Assert(executor.GetStmtLabel(stmtNode), Equals, tt.label) } diff --git a/executor/prepared.go b/executor/prepared.go index 962d35db6ae3c..a006bf0d6edd3 100644 --- a/executor/prepared.go +++ b/executor/prepared.go @@ -21,6 +21,7 @@ import ( "github.com/pingcap/tidb/expression" "github.com/pingcap/tidb/infoschema" "github.com/pingcap/tidb/parser" + "github.com/pingcap/tidb/planner" plannercore "github.com/pingcap/tidb/planner/core" "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/sessionctx/variable" @@ -248,7 +249,7 @@ func CompileExecutePreparedStmt(ctx sessionctx.Context, ID uint32, args ...inter execStmt.UsingVars[i] = ast.NewValueExpr(val) } is := GetInfoSchema(ctx) - execPlan, err := plannercore.Optimize(ctx, execStmt, is) + execPlan, err := planner.Optimize(ctx, execStmt, is) if err != nil { return nil, errors.Trace(err) } diff --git a/executor/trace.go b/executor/trace.go index 588df29065c1b..fd6db06e6346d 100644 --- a/executor/trace.go +++ b/executor/trace.go @@ -19,6 +19,7 @@ import ( "github.com/opentracing/basictracer-go" opentracing "github.com/opentracing/opentracing-go" "github.com/pingcap/tidb/ast" + "github.com/pingcap/tidb/planner" plannercore "github.com/pingcap/tidb/planner/core" "github.com/pingcap/tidb/util/chunk" "github.com/pingcap/tidb/util/tracing" @@ -51,7 +52,7 @@ func (e *TraceExec) Next(ctx context.Context, chk *chunk.Chunk) error { // record how much time was spent for optimizeing plan optimizeSp := e.rootTrace.Tracer().StartSpan("plan_optimize", opentracing.FollowsFrom(e.rootTrace.Context())) - stmtPlan, err := plannercore.Optimize(e.builder.ctx, e.stmtNode, e.builder.is) + stmtPlan, err := planner.Optimize(e.builder.ctx, e.stmtNode, e.builder.is) if err != nil { return err } diff --git a/planner/core/common_plans.go b/planner/core/common_plans.go index 2c0e08817043a..c6ecce85b01ac 100644 --- a/planner/core/common_plans.go +++ b/planner/core/common_plans.go @@ -135,7 +135,7 @@ type Execute struct { Plan Plan } -func (e *Execute) optimizePreparedPlan(ctx sessionctx.Context, is infoschema.InfoSchema) error { +func (e *Execute) OptimizePreparedPlan(ctx sessionctx.Context, is infoschema.InfoSchema) error { vars := ctx.GetSessionVars() if e.Name != "" { e.ExecID = vars.PreparedStmtNameToID[e.Name] @@ -191,7 +191,7 @@ func (e *Execute) getPhysicalPlan(ctx sessionctx.Context, is infoschema.InfoSche return plan, nil } } - p, err := Optimize(ctx, prepared.Stmt, is) + p, err := OptimizeAstNode(ctx, prepared.Stmt, is) if err != nil { return nil, errors.Trace(err) } diff --git a/planner/core/expression_rewriter.go b/planner/core/expression_rewriter.go index c098fdb7624d4..60d1e938b19b7 100644 --- a/planner/core/expression_rewriter.go +++ b/planner/core/expression_rewriter.go @@ -39,7 +39,7 @@ func evalAstExpr(ctx sessionctx.Context, expr ast.ExprNode) (types.Datum, error) if val, ok := expr.(*ast.ValueExpr); ok { return val.Datum, nil } - b := &planBuilder{ + b := &PlanBuilder{ ctx: ctx, colMapper: make(map[*ast.ColumnNameExpr]int), } @@ -53,7 +53,7 @@ func evalAstExpr(ctx sessionctx.Context, expr ast.ExprNode) (types.Datum, error) return newExpr.Eval(chunk.Row{}) } -func (b *planBuilder) rewriteInsertOnDuplicateUpdate(exprNode ast.ExprNode, mockPlan LogicalPlan, insertPlan *Insert) (expression.Expression, error) { +func (b *PlanBuilder) rewriteInsertOnDuplicateUpdate(exprNode ast.ExprNode, mockPlan LogicalPlan, insertPlan *Insert) (expression.Expression, error) { b.rewriterCounter++ defer func() { b.rewriterCounter-- }() @@ -77,7 +77,7 @@ func (b *planBuilder) rewriteInsertOnDuplicateUpdate(exprNode ast.ExprNode, mock // aggMapper maps ast.AggregateFuncExpr to the columns offset in p's output schema. // asScalar means whether this expression must be treated as a scalar expression. // And this function returns a result expression, a new plan that may have apply or semi-join. -func (b *planBuilder) rewrite(exprNode ast.ExprNode, p LogicalPlan, aggMapper map[*ast.AggregateFuncExpr]int, asScalar bool) (expression.Expression, LogicalPlan, error) { +func (b *PlanBuilder) rewrite(exprNode ast.ExprNode, p LogicalPlan, aggMapper map[*ast.AggregateFuncExpr]int, asScalar bool) (expression.Expression, LogicalPlan, error) { expr, resultPlan, err := b.rewriteWithPreprocess(exprNode, p, aggMapper, asScalar, nil) return expr, resultPlan, errors.Trace(err) } @@ -85,7 +85,7 @@ func (b *planBuilder) rewrite(exprNode ast.ExprNode, p LogicalPlan, aggMapper ma // rewriteWithPreprocess is for handling the situation that we need to adjust the input ast tree // before really using its node in `expressionRewriter.Leave`. In that case, we first call // er.preprocess(expr), which returns a new expr. Then we use the new expr in `Leave`. -func (b *planBuilder) rewriteWithPreprocess(exprNode ast.ExprNode, p LogicalPlan, aggMapper map[*ast.AggregateFuncExpr]int, asScalar bool, preprocess func(ast.Node) ast.Node) (expression.Expression, LogicalPlan, error) { +func (b *PlanBuilder) rewriteWithPreprocess(exprNode ast.ExprNode, p LogicalPlan, aggMapper map[*ast.AggregateFuncExpr]int, asScalar bool, preprocess func(ast.Node) ast.Node) (expression.Expression, LogicalPlan, error) { b.rewriterCounter++ defer func() { b.rewriterCounter-- }() @@ -106,7 +106,7 @@ func (b *planBuilder) rewriteWithPreprocess(exprNode ast.ExprNode, p LogicalPlan return expr, resultPlan, errors.Trace(err) } -func (b *planBuilder) getExpressionRewriter(p LogicalPlan) (rewriter *expressionRewriter) { +func (b *PlanBuilder) getExpressionRewriter(p LogicalPlan) (rewriter *expressionRewriter) { defer func() { if p != nil { rewriter.schema = p.Schema() @@ -129,7 +129,7 @@ func (b *planBuilder) getExpressionRewriter(p LogicalPlan) (rewriter *expression return } -func (b *planBuilder) rewriteExprNode(rewriter *expressionRewriter, exprNode ast.ExprNode, asScalar bool) (expression.Expression, LogicalPlan, error) { +func (b *PlanBuilder) rewriteExprNode(rewriter *expressionRewriter, exprNode ast.ExprNode, asScalar bool) (expression.Expression, LogicalPlan, error) { exprNode.Accept(rewriter) if rewriter.err != nil { return nil, nil, errors.Trace(rewriter.err) @@ -153,7 +153,7 @@ type expressionRewriter struct { schema *expression.Schema err error aggrMap map[*ast.AggregateFuncExpr]int - b *planBuilder + b *PlanBuilder ctx sessionctx.Context // asScalar indicates the return value must be a scalar value. @@ -552,7 +552,7 @@ func (er *expressionRewriter) handleExistSubquery(v *ast.ExistsSubqueryExpr) (as } er.ctxStack = append(er.ctxStack, er.p.Schema().Columns[er.p.Schema().Len()-1]) } else { - physicalPlan, err := doOptimize(er.b.optFlag, np) + physicalPlan, err := DoOptimize(er.b.optFlag, np) if err != nil { er.err = errors.Trace(err) return v, true @@ -621,7 +621,7 @@ func (er *expressionRewriter) handleInSubquery(v *ast.PatternInExpr) (ast.Node, // TODO: Now we cannot add it to CBO framework. Instead, user can set a session variable to open this optimization. // We will improve our CBO framework in future. if lLen == 1 && er.ctx.GetSessionVars().AllowInSubqueryUnFolding && len(np.extractCorrelatedCols()) == 0 { - physicalPlan, err1 := doOptimize(er.b.optFlag, np) + physicalPlan, err1 := DoOptimize(er.b.optFlag, np) if err1 != nil { er.err = errors.Trace(err1) return v, true @@ -709,7 +709,7 @@ func (er *expressionRewriter) handleScalarSubquery(v *ast.SubqueryExpr) (ast.Nod } return v, true } - physicalPlan, err := doOptimize(er.b.optFlag, np) + physicalPlan, err := DoOptimize(er.b.optFlag, np) if err != nil { er.err = errors.Trace(err) return v, true diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index db9178c9ad0b8..91d847b588e4b 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -66,7 +66,7 @@ func (la *LogicalAggregation) collectGroupByColumns() { } } -func (b *planBuilder) buildAggregation(p LogicalPlan, aggFuncList []*ast.AggregateFuncExpr, gbyItems []expression.Expression) (LogicalPlan, map[int]int, error) { +func (b *PlanBuilder) buildAggregation(p LogicalPlan, aggFuncList []*ast.AggregateFuncExpr, gbyItems []expression.Expression) (LogicalPlan, map[int]int, error) { b.optFlag = b.optFlag | flagBuildKeyInfo b.optFlag = b.optFlag | flagPushDownAgg // We may apply aggregation eliminate optimization. @@ -127,7 +127,7 @@ func (b *planBuilder) buildAggregation(p LogicalPlan, aggFuncList []*ast.Aggrega return plan4Agg, aggIndexMap, nil } -func (b *planBuilder) buildResultSetNode(node ast.ResultSetNode) (p LogicalPlan, err error) { +func (b *PlanBuilder) buildResultSetNode(node ast.ResultSetNode) (p LogicalPlan, err error) { switch x := node.(type) { case *ast.Join: return b.buildJoin(x) @@ -270,7 +270,7 @@ func (p *LogicalJoin) setPreferredJoinType(hintInfo *tableHintInfo) error { return nil } -func (b *planBuilder) buildJoin(joinNode *ast.Join) (LogicalPlan, error) { +func (b *PlanBuilder) buildJoin(joinNode *ast.Join) (LogicalPlan, error) { // We will construct a "Join" node for some statements like "INSERT", // "DELETE", "UPDATE", "REPLACE". For this scenario "joinNode.Right" is nil // and we only build the left "ResultSetNode". @@ -367,7 +367,7 @@ func (b *planBuilder) buildJoin(joinNode *ast.Join) (LogicalPlan, error) { // appears in "leftPlan". // 2. the rest columns in "leftPlan", in the order they appears in "leftPlan". // 3. the rest columns in "rightPlan", in the order they appears in "rightPlan". -func (b *planBuilder) buildUsingClause(p *LogicalJoin, leftPlan, rightPlan LogicalPlan, join *ast.Join) error { +func (b *PlanBuilder) buildUsingClause(p *LogicalJoin, leftPlan, rightPlan LogicalPlan, join *ast.Join) error { filter := make(map[string]bool, len(join.Using)) for _, col := range join.Using { filter[col.Name.L] = true @@ -381,12 +381,12 @@ func (b *planBuilder) buildUsingClause(p *LogicalJoin, leftPlan, rightPlan Logic // All the common columns // Every column in the first (left) table that is not a common column // Every column in the second (right) table that is not a common column -func (b *planBuilder) buildNaturalJoin(p *LogicalJoin, leftPlan, rightPlan LogicalPlan, join *ast.Join) error { +func (b *PlanBuilder) buildNaturalJoin(p *LogicalJoin, leftPlan, rightPlan LogicalPlan, join *ast.Join) error { return b.coalesceCommonColumns(p, leftPlan, rightPlan, join.Tp == ast.RightJoin, nil) } // coalesceCommonColumns is used by buildUsingClause and buildNaturalJoin. The filter is used by buildUsingClause. -func (b *planBuilder) coalesceCommonColumns(p *LogicalJoin, leftPlan, rightPlan LogicalPlan, rightJoin bool, filter map[string]bool) error { +func (b *PlanBuilder) coalesceCommonColumns(p *LogicalJoin, leftPlan, rightPlan LogicalPlan, rightJoin bool, filter map[string]bool) error { lsc := leftPlan.Schema().Clone() rsc := rightPlan.Schema().Clone() lColumns, rColumns := lsc.Columns, rsc.Columns @@ -452,7 +452,7 @@ func (b *planBuilder) coalesceCommonColumns(p *LogicalJoin, leftPlan, rightPlan return nil } -func (b *planBuilder) buildSelection(p LogicalPlan, where ast.ExprNode, AggMapper map[*ast.AggregateFuncExpr]int) (LogicalPlan, error) { +func (b *PlanBuilder) buildSelection(p LogicalPlan, where ast.ExprNode, AggMapper map[*ast.AggregateFuncExpr]int) (LogicalPlan, error) { b.optFlag = b.optFlag | flagPredicatePushDown if b.curClause != havingClause { b.curClause = whereClause @@ -493,7 +493,7 @@ func (b *planBuilder) buildSelection(p LogicalPlan, where ast.ExprNode, AggMappe } // buildProjectionFieldNameFromColumns builds the field name, table name and database name when field expression is a column reference. -func (b *planBuilder) buildProjectionFieldNameFromColumns(field *ast.SelectField, c *expression.Column) (colName, origColName, tblName, origTblName, dbName model.CIStr) { +func (b *PlanBuilder) buildProjectionFieldNameFromColumns(field *ast.SelectField, c *expression.Column) (colName, origColName, tblName, origTblName, dbName model.CIStr) { if astCol, ok := getInnerFromParentheses(field.Expr).(*ast.ColumnNameExpr); ok { origColName, tblName, dbName = astCol.Name.Name, astCol.Name.Table, astCol.Name.Schema } @@ -512,7 +512,7 @@ func (b *planBuilder) buildProjectionFieldNameFromColumns(field *ast.SelectField } // buildProjectionFieldNameFromExpressions builds the field name when field expression is a normal expression. -func (b *planBuilder) buildProjectionFieldNameFromExpressions(field *ast.SelectField) model.CIStr { +func (b *PlanBuilder) buildProjectionFieldNameFromExpressions(field *ast.SelectField) model.CIStr { if agg, ok := field.Expr.(*ast.AggregateFuncExpr); ok && agg.F == ast.AggFuncFirstRow { // When the query is select t.a from t group by a; The Column Name should be a but not t.a; return agg.Args[0].(*ast.ColumnNameExpr).Name.Name @@ -553,7 +553,7 @@ func (b *planBuilder) buildProjectionFieldNameFromExpressions(field *ast.SelectF } // buildProjectionField builds the field object according to SelectField in projection. -func (b *planBuilder) buildProjectionField(id, position int, field *ast.SelectField, expr expression.Expression) *expression.Column { +func (b *PlanBuilder) buildProjectionField(id, position int, field *ast.SelectField, expr expression.Expression) *expression.Column { var origTblName, tblName, origColName, colName, dbName model.CIStr if c, ok := expr.(*expression.Column); ok && !c.IsAggOrSubq { // Field is a column reference. @@ -577,7 +577,7 @@ func (b *planBuilder) buildProjectionField(id, position int, field *ast.SelectFi } // buildProjection returns a Projection plan and non-aux columns length. -func (b *planBuilder) buildProjection(p LogicalPlan, fields []*ast.SelectField, mapper map[*ast.AggregateFuncExpr]int) (LogicalPlan, int, error) { +func (b *PlanBuilder) buildProjection(p LogicalPlan, fields []*ast.SelectField, mapper map[*ast.AggregateFuncExpr]int) (LogicalPlan, int, error) { b.optFlag |= flagEliminateProjection b.curClause = fieldList proj := LogicalProjection{Exprs: make([]expression.Expression, 0, len(fields))}.init(b.ctx) @@ -604,7 +604,7 @@ func (b *planBuilder) buildProjection(p LogicalPlan, fields []*ast.SelectField, return proj, oldLen, nil } -func (b *planBuilder) buildDistinct(child LogicalPlan, length int) *LogicalAggregation { +func (b *PlanBuilder) buildDistinct(child LogicalPlan, length int) *LogicalAggregation { b.optFlag = b.optFlag | flagBuildKeyInfo b.optFlag = b.optFlag | flagPushDownAgg plan4Agg := LogicalAggregation{ @@ -646,7 +646,7 @@ func unionJoinFieldType(a, b *types.FieldType) *types.FieldType { return resultTp } -func (b *planBuilder) buildProjection4Union(u *LogicalUnionAll) { +func (b *PlanBuilder) buildProjection4Union(u *LogicalUnionAll) { unionCols := make([]*expression.Column, 0, u.children[0].Schema().Len()) // Infer union result types by its children's schema. @@ -685,7 +685,7 @@ func (b *planBuilder) buildProjection4Union(u *LogicalUnionAll) { } } -func (b *planBuilder) buildUnion(union *ast.UnionStmt) (LogicalPlan, error) { +func (b *PlanBuilder) buildUnion(union *ast.UnionStmt) (LogicalPlan, error) { distinctSelectPlans, allSelectPlans, err := b.divideUnionSelectPlans(union.SelectList.Selects) if err != nil { return nil, errors.Trace(err) @@ -725,7 +725,7 @@ func (b *planBuilder) buildUnion(union *ast.UnionStmt) (LogicalPlan, error) { // and divide result plans into "union-distinct" and "union-all" parts. // divide rule ref: https://dev.mysql.com/doc/refman/5.7/en/union.html // "Mixed UNION types are treated such that a DISTINCT union overrides any ALL union to its left." -func (b *planBuilder) divideUnionSelectPlans(selects []*ast.SelectStmt) (distinctSelects []LogicalPlan, allSelects []LogicalPlan, err error) { +func (b *PlanBuilder) divideUnionSelectPlans(selects []*ast.SelectStmt) (distinctSelects []LogicalPlan, allSelects []LogicalPlan, err error) { firstUnionAllIdx, columnNums := 0, -1 // The last slot is reserved for appending distinct union outside this function. children := make([]LogicalPlan, len(selects), len(selects)+1) @@ -751,7 +751,7 @@ func (b *planBuilder) divideUnionSelectPlans(selects []*ast.SelectStmt) (distinc return children[:firstUnionAllIdx], children[firstUnionAllIdx:], nil } -func (b *planBuilder) buildUnionAll(subPlan []LogicalPlan) LogicalPlan { +func (b *PlanBuilder) buildUnionAll(subPlan []LogicalPlan) LogicalPlan { if len(subPlan) == 0 { return nil } @@ -780,7 +780,7 @@ func (by *ByItems) Clone() *ByItems { return &ByItems{Expr: by.Expr.Clone(), Desc: by.Desc} } -func (b *planBuilder) buildSort(p LogicalPlan, byItems []*ast.ByItem, aggMapper map[*ast.AggregateFuncExpr]int) (*LogicalSort, error) { +func (b *PlanBuilder) buildSort(p LogicalPlan, byItems []*ast.ByItem, aggMapper map[*ast.AggregateFuncExpr]int) (*LogicalSort, error) { b.curClause = orderByClause sort := LogicalSort{}.init(b.ctx) exprs := make([]*ByItems, 0, len(byItems)) @@ -833,7 +833,7 @@ func extractLimitCountOffset(sc *stmtctx.StatementContext, limit *ast.Limit) (co return count, offset, nil } -func (b *planBuilder) buildLimit(src LogicalPlan, limit *ast.Limit) (LogicalPlan, error) { +func (b *PlanBuilder) buildLimit(src LogicalPlan, limit *ast.Limit) (LogicalPlan, error) { b.optFlag = b.optFlag | flagPushDownTopN var ( offset, count uint64 @@ -1053,7 +1053,7 @@ func (a *havingAndOrderbyExprResolver) Leave(n ast.Node) (node ast.Node, ok bool // resolveHavingAndOrderBy will process aggregate functions and resolve the columns that don't exist in select fields. // If we found some columns that are not in select fields, we will append it to select fields and update the colMapper. // When we rewrite the order by / having expression, we will find column in map at first. -func (b *planBuilder) resolveHavingAndOrderBy(sel *ast.SelectStmt, p LogicalPlan) ( +func (b *PlanBuilder) resolveHavingAndOrderBy(sel *ast.SelectStmt, p LogicalPlan) ( map[*ast.AggregateFuncExpr]int, map[*ast.AggregateFuncExpr]int, error) { extractor := &havingAndOrderbyExprResolver{ p: p, @@ -1093,7 +1093,7 @@ func (b *planBuilder) resolveHavingAndOrderBy(sel *ast.SelectStmt, p LogicalPlan return havingAggMapper, extractor.aggMapper, nil } -func (b *planBuilder) extractAggFuncs(fields []*ast.SelectField) ([]*ast.AggregateFuncExpr, map[*ast.AggregateFuncExpr]int) { +func (b *PlanBuilder) extractAggFuncs(fields []*ast.SelectField) ([]*ast.AggregateFuncExpr, map[*ast.AggregateFuncExpr]int) { extractor := &AggregateFuncExtractor{} for _, f := range fields { n, _ := f.Expr.Accept(extractor) @@ -1367,7 +1367,7 @@ func checkExprInGroupBy(p LogicalPlan, expr ast.ExprNode, offset int, loc string } } -func (b *planBuilder) checkOnlyFullGroupBy(p LogicalPlan, sel *ast.SelectStmt) (err error) { +func (b *PlanBuilder) checkOnlyFullGroupBy(p LogicalPlan, sel *ast.SelectStmt) (err error) { if sel.GroupBy != nil { err = b.checkOnlyFullGroupByWithGroupClause(p, sel) } else { @@ -1376,7 +1376,7 @@ func (b *planBuilder) checkOnlyFullGroupBy(p LogicalPlan, sel *ast.SelectStmt) ( return errors.Trace(err) } -func (b *planBuilder) checkOnlyFullGroupByWithGroupClause(p LogicalPlan, sel *ast.SelectStmt) error { +func (b *PlanBuilder) checkOnlyFullGroupByWithGroupClause(p LogicalPlan, sel *ast.SelectStmt) error { gbyCols := make(map[*expression.Column]struct{}, len(sel.Fields.Fields)) gbyExprs := make([]ast.ExprNode, 0, len(sel.Fields.Fields)) schema := p.Schema() @@ -1435,7 +1435,7 @@ func (b *planBuilder) checkOnlyFullGroupByWithGroupClause(p LogicalPlan, sel *as return nil } -func (b *planBuilder) checkOnlyFullGroupByWithOutGroupClause(p LogicalPlan, fields []*ast.SelectField) error { +func (b *PlanBuilder) checkOnlyFullGroupByWithOutGroupClause(p LogicalPlan, fields []*ast.SelectField) error { resolver := colResolverForOnlyFullGroupBy{} for idx, field := range fields { resolver.exprIdx = idx @@ -1515,7 +1515,7 @@ func allColFromExprNode(p LogicalPlan, n ast.Node, cols map[*expression.Column]s return } -func (b *planBuilder) resolveGbyExprs(p LogicalPlan, gby *ast.GroupByClause, fields []*ast.SelectField) (LogicalPlan, []expression.Expression, error) { +func (b *PlanBuilder) resolveGbyExprs(p LogicalPlan, gby *ast.GroupByClause, fields []*ast.SelectField) (LogicalPlan, []expression.Expression, error) { b.curClause = groupByClause exprs := make([]expression.Expression, 0, len(gby.Items)) resolver := &gbyResolver{ @@ -1541,7 +1541,7 @@ func (b *planBuilder) resolveGbyExprs(p LogicalPlan, gby *ast.GroupByClause, fie return p, exprs, nil } -func (b *planBuilder) unfoldWildStar(p LogicalPlan, selectFields []*ast.SelectField) (resultList []*ast.SelectField, err error) { +func (b *PlanBuilder) unfoldWildStar(p LogicalPlan, selectFields []*ast.SelectField) (resultList []*ast.SelectField, err error) { for i, field := range selectFields { if field.WildCard == nil { resultList = append(resultList, field) @@ -1577,7 +1577,7 @@ func (b *planBuilder) unfoldWildStar(p LogicalPlan, selectFields []*ast.SelectFi return resultList, nil } -func (b *planBuilder) pushTableHints(hints []*ast.TableOptimizerHint) bool { +func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint) bool { var sortMergeTables, INLJTables, hashJoinTables []model.CIStr for _, hint := range hints { switch hint.HintName.L { @@ -1602,19 +1602,19 @@ func (b *planBuilder) pushTableHints(hints []*ast.TableOptimizerHint) bool { return false } -func (b *planBuilder) popTableHints() { +func (b *PlanBuilder) popTableHints() { b.tableHintInfo = b.tableHintInfo[:len(b.tableHintInfo)-1] } // TableHints returns the *tableHintInfo of PlanBuilder. -func (b *planBuilder) TableHints() *tableHintInfo { +func (b *PlanBuilder) TableHints() *tableHintInfo { if len(b.tableHintInfo) == 0 { return nil } return &(b.tableHintInfo[len(b.tableHintInfo)-1]) } -func (b *planBuilder) buildSelect(sel *ast.SelectStmt) (p LogicalPlan, err error) { +func (b *PlanBuilder) buildSelect(sel *ast.SelectStmt) (p LogicalPlan, err error) { if b.pushTableHints(sel.TableHints) { // table hints are only visible in the current SELECT statement. defer b.popTableHints() @@ -1739,7 +1739,7 @@ func (b *planBuilder) buildSelect(sel *ast.SelectStmt) (p LogicalPlan, err error return p, nil } -func (b *planBuilder) buildTableDual() *LogicalTableDual { +func (b *PlanBuilder) buildTableDual() *LogicalTableDual { return LogicalTableDual{RowCount: 1}.init(b.ctx) } @@ -1789,7 +1789,7 @@ func getStatsTable(ctx sessionctx.Context, tblInfo *model.TableInfo, pid int64) return statsTbl } -func (b *planBuilder) buildDataSource(tn *ast.TableName) (LogicalPlan, error) { +func (b *PlanBuilder) buildDataSource(tn *ast.TableName) (LogicalPlan, error) { dbName := tn.Schema if dbName.L == "" { dbName = model.NewCIStr(b.ctx.GetSessionVars().CurrentDB) @@ -1894,7 +1894,7 @@ func (b *planBuilder) buildDataSource(tn *ast.TableName) (LogicalPlan, error) { // projectVirtualColumns is only for DataSource. If some table has virtual generated columns, // we add a projection on the original DataSource, and calculate those columns in the projection // so that plans above it can reference generated columns by their name. -func (b *planBuilder) projectVirtualColumns(ds *DataSource, columns []*table.Column) (*LogicalProjection, error) { +func (b *PlanBuilder) projectVirtualColumns(ds *DataSource, columns []*table.Column) (*LogicalProjection, error) { var hasVirtualGeneratedColumn = false for _, column := range columns { if column.IsGenerated() && !column.GeneratedStored { @@ -1936,7 +1936,7 @@ func (b *planBuilder) projectVirtualColumns(ds *DataSource, columns []*table.Col // buildApplyWithJoinType builds apply plan with outerPlan and innerPlan, which apply join with particular join type for // every row from outerPlan and the whole innerPlan. -func (b *planBuilder) buildApplyWithJoinType(outerPlan, innerPlan LogicalPlan, tp JoinType) LogicalPlan { +func (b *PlanBuilder) buildApplyWithJoinType(outerPlan, innerPlan LogicalPlan, tp JoinType) LogicalPlan { b.optFlag = b.optFlag | flagPredicatePushDown b.optFlag = b.optFlag | flagBuildKeyInfo b.optFlag = b.optFlag | flagDecorrelate @@ -1950,7 +1950,7 @@ func (b *planBuilder) buildApplyWithJoinType(outerPlan, innerPlan LogicalPlan, t } // buildSemiApply builds apply plan with outerPlan and innerPlan, which apply semi-join for every row from outerPlan and the whole innerPlan. -func (b *planBuilder) buildSemiApply(outerPlan, innerPlan LogicalPlan, condition []expression.Expression, asScalar, not bool) (LogicalPlan, error) { +func (b *PlanBuilder) buildSemiApply(outerPlan, innerPlan LogicalPlan, condition []expression.Expression, asScalar, not bool) (LogicalPlan, error) { b.optFlag = b.optFlag | flagPredicatePushDown b.optFlag = b.optFlag | flagBuildKeyInfo b.optFlag = b.optFlag | flagDecorrelate @@ -1966,13 +1966,13 @@ func (b *planBuilder) buildSemiApply(outerPlan, innerPlan LogicalPlan, condition return ap, nil } -func (b *planBuilder) buildMaxOneRow(p LogicalPlan) LogicalPlan { +func (b *PlanBuilder) buildMaxOneRow(p LogicalPlan) LogicalPlan { maxOneRow := LogicalMaxOneRow{}.init(b.ctx) maxOneRow.SetChildren(p) return maxOneRow } -func (b *planBuilder) buildSemiJoin(outerPlan, innerPlan LogicalPlan, onCondition []expression.Expression, asScalar bool, not bool) (*LogicalJoin, error) { +func (b *PlanBuilder) buildSemiJoin(outerPlan, innerPlan LogicalPlan, onCondition []expression.Expression, asScalar bool, not bool) (*LogicalJoin, error) { joinPlan := LogicalJoin{}.init(b.ctx) for i, expr := range onCondition { onCondition[i] = expr.Decorrelate(outerPlan.Schema()) @@ -2023,7 +2023,7 @@ func (b *planBuilder) buildSemiJoin(outerPlan, innerPlan LogicalPlan, onConditio return joinPlan, nil } -func (b *planBuilder) buildUpdate(update *ast.UpdateStmt) (Plan, error) { +func (b *PlanBuilder) buildUpdate(update *ast.UpdateStmt) (Plan, error) { if b.pushTableHints(update.TableHints) { // table hints are only visible in the current UPDATE statement. defer b.popTableHints() @@ -2090,7 +2090,7 @@ func (b *planBuilder) buildUpdate(update *ast.UpdateStmt) (Plan, error) { updt := Update{OrderedList: orderedList}.init(b.ctx) updt.SetSchema(p.Schema()) - updt.SelectPlan, err = doOptimize(b.optFlag, p) + updt.SelectPlan, err = DoOptimize(b.optFlag, p) if err != nil { return nil, errors.Trace(err) } @@ -2098,7 +2098,7 @@ func (b *planBuilder) buildUpdate(update *ast.UpdateStmt) (Plan, error) { return updt, nil } -func (b *planBuilder) buildUpdateLists(tableList []*ast.TableName, list []*ast.Assignment, p LogicalPlan) ([]*expression.Assignment, LogicalPlan, error) { +func (b *PlanBuilder) buildUpdateLists(tableList []*ast.TableName, list []*ast.Assignment, p LogicalPlan) ([]*expression.Assignment, LogicalPlan, error) { b.curClause = fieldList modifyColumns := make(map[string]struct{}, p.Schema().Len()) // Which columns are in set list. for _, assign := range list { @@ -2205,7 +2205,7 @@ func extractTableAsNameForUpdate(p LogicalPlan, asNames map[*model.TableInfo][]* } } -func (b *planBuilder) buildDelete(delete *ast.DeleteStmt) (Plan, error) { +func (b *PlanBuilder) buildDelete(delete *ast.DeleteStmt) (Plan, error) { if b.pushTableHints(delete.TableHints) { // table hints are only visible in the current DELETE statement. defer b.popTableHints() @@ -2254,7 +2254,7 @@ func (b *planBuilder) buildDelete(delete *ast.DeleteStmt) (Plan, error) { IsMultiTable: delete.IsMultiTable, }.init(b.ctx) - del.SelectPlan, err = doOptimize(b.optFlag, p) + del.SelectPlan, err = DoOptimize(b.optFlag, p) if err != nil { return nil, errors.Trace(err) } diff --git a/planner/core/logical_plan_test.go b/planner/core/logical_plan_test.go index 10d94e7cabb55..2b4a4c9687273 100644 --- a/planner/core/logical_plan_test.go +++ b/planner/core/logical_plan_test.go @@ -1711,13 +1711,13 @@ func (s *testPlanSuite) TestVisitInfo(c *C) { stmt, err := s.ParseOneStmt(tt.sql, "", "") c.Assert(err, IsNil, comment) Preprocess(s.ctx, stmt, s.is, false) - builder := &planBuilder{ + builder := &PlanBuilder{ colMapper: make(map[*ast.ColumnNameExpr]int), ctx: mockContext(), is: s.is, } builder.ctx.GetSessionVars().HashJoinConcurrency = 1 - _, err = builder.build(stmt) + _, err = builder.Build(stmt) c.Assert(err, IsNil, comment) checkVisitInfo(c, builder.visitInfo, tt.ans, comment) @@ -1815,12 +1815,12 @@ func (s *testPlanSuite) TestUnion(c *C) { stmt, err := s.ParseOneStmt(tt.sql, "", "") c.Assert(err, IsNil, comment) Preprocess(s.ctx, stmt, s.is, false) - builder := &planBuilder{ + builder := &PlanBuilder{ ctx: mockContext(), is: s.is, colMapper: make(map[*ast.ColumnNameExpr]int), } - plan, err := builder.build(stmt) + plan, err := builder.Build(stmt) if tt.err { c.Assert(err, NotNil) return @@ -1942,12 +1942,12 @@ func (s *testPlanSuite) TestTopNPushDown(c *C) { stmt, err := s.ParseOneStmt(tt.sql, "", "") c.Assert(err, IsNil, comment) Preprocess(s.ctx, stmt, s.is, false) - builder := &planBuilder{ + builder := &PlanBuilder{ ctx: mockContext(), is: s.is, colMapper: make(map[*ast.ColumnNameExpr]int), } - p, err := builder.build(stmt) + p, err := builder.Build(stmt) c.Assert(err, IsNil) p, err = logicalOptimize(builder.optFlag, p.(LogicalPlan)) c.Assert(err, IsNil) diff --git a/planner/core/optimizer.go b/planner/core/optimizer.go index 67e93412597aa..56bf1137eb0be 100644 --- a/planner/core/optimizer.go +++ b/planner/core/optimizer.go @@ -25,6 +25,9 @@ import ( "github.com/pkg/errors" ) +// EvalAstExpr evaluates ast expression directly. +var OptimizeAstNode func(ctx sessionctx.Context, node ast.Node, is infoschema.InfoSchema) (Plan, error) + // AllowCartesianProduct means whether tidb allows cartesian join without equal conditions. var AllowCartesianProduct = true @@ -59,60 +62,23 @@ type logicalOptRule interface { optimize(LogicalPlan) (LogicalPlan, error) } -// Optimize does optimization and creates a Plan. -// The node must be prepared first. -func Optimize(ctx sessionctx.Context, node ast.Node, is infoschema.InfoSchema) (Plan, error) { - fp := tryFastPlan(ctx, node) - if fp != nil { - return fp, nil - } - ctx.GetSessionVars().PlanID = 0 - ctx.GetSessionVars().PlanColumnID = 0 - builder := &planBuilder{ - ctx: ctx, - is: is, - colMapper: make(map[*ast.ColumnNameExpr]int), - } - p, err := builder.build(node) - if err != nil { - return nil, errors.Trace(err) - } - - // Maybe it's better to move this to Preprocess, but check privilege need table - // information, which is collected into visitInfo during logical plan builder. - if pm := privilege.GetPrivilegeManager(ctx); pm != nil { - if !checkPrivilege(pm, builder.visitInfo) { - return nil, errors.New("privilege check fail") - } - } - - if logic, ok := p.(LogicalPlan); ok { - return doOptimize(builder.optFlag, logic) - } - if execPlan, ok := p.(*Execute); ok { - err := execPlan.optimizePreparedPlan(ctx, is) - return p, errors.Trace(err) - } - return p, nil -} - // BuildLogicalPlan used to build logical plan from ast.Node. func BuildLogicalPlan(ctx sessionctx.Context, node ast.Node, is infoschema.InfoSchema) (Plan, error) { ctx.GetSessionVars().PlanID = 0 ctx.GetSessionVars().PlanColumnID = 0 - builder := &planBuilder{ + builder := &PlanBuilder{ ctx: ctx, is: is, colMapper: make(map[*ast.ColumnNameExpr]int), } - p, err := builder.build(node) + p, err := builder.Build(node) if err != nil { return nil, errors.Trace(err) } return p, nil } -func checkPrivilege(pm privilege.Manager, vs []visitInfo) bool { +func CheckPrivilege(pm privilege.Manager, vs []visitInfo) bool { for _, v := range vs { if !pm.RequestVerification(v.db, v.table, v.column, v.privilege) { return false @@ -121,7 +87,7 @@ func checkPrivilege(pm privilege.Manager, vs []visitInfo) bool { return true } -func doOptimize(flag uint64, logic LogicalPlan) (PhysicalPlan, error) { +func DoOptimize(flag uint64, logic LogicalPlan) (PhysicalPlan, error) { logic, err := logicalOptimize(flag, logic) if err != nil { return nil, errors.Trace(err) diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index 33c48c11abe8f..a043ed73ebf4f 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -104,9 +104,9 @@ var clauseMsg = map[clauseCode]string{ showStatement: "show statement", } -// planBuilder builds Plan from an ast.Node. +// PlanBuilder builds Plan from an ast.Node. // It just builds the ast node straightforwardly. -type planBuilder struct { +type PlanBuilder struct { ctx sessionctx.Context is infoschema.InfoSchema outerSchemas []*expression.Schema @@ -130,7 +130,23 @@ type planBuilder struct { inStraightJoin bool } -func (b *planBuilder) build(node ast.Node) (Plan, error) { +func (b *PlanBuilder) GetVisitInfo() []visitInfo { + return b.visitInfo +} + +func (b *PlanBuilder) GetOptFlag() uint64 { + return b.optFlag +} + +func NewPlanBuilder(sctx sessionctx.Context, is infoschema.InfoSchema) *PlanBuilder { + return &PlanBuilder{ + ctx: sctx, + is: is, + colMapper: make(map[*ast.ColumnNameExpr]int), + } +} + +func (b *PlanBuilder) Build(node ast.Node) (Plan, error) { b.optFlag = flagPrunColumns switch x := node.(type) { case *ast.AdminStmt: @@ -177,7 +193,7 @@ func (b *planBuilder) build(node ast.Node) (Plan, error) { return nil, ErrUnsupportedType.GenWithStack("Unsupported type %T", node) } -func (b *planBuilder) buildExecute(v *ast.ExecuteStmt) (Plan, error) { +func (b *PlanBuilder) buildExecute(v *ast.ExecuteStmt) (Plan, error) { vars := make([]expression.Expression, 0, len(v.UsingVars)) for _, expr := range v.UsingVars { newExpr, _, err := b.rewrite(expr, nil, nil, true) @@ -190,7 +206,7 @@ func (b *planBuilder) buildExecute(v *ast.ExecuteStmt) (Plan, error) { return exe, nil } -func (b *planBuilder) buildDo(v *ast.DoStmt) (Plan, error) { +func (b *PlanBuilder) buildDo(v *ast.DoStmt) (Plan, error) { dual := LogicalTableDual{RowCount: 1}.init(b.ctx) p := LogicalProjection{Exprs: make([]expression.Expression, 0, len(v.Exprs))}.init(b.ctx) @@ -213,7 +229,7 @@ func (b *planBuilder) buildDo(v *ast.DoStmt) (Plan, error) { return p, nil } -func (b *planBuilder) buildSet(v *ast.SetStmt) (Plan, error) { +func (b *PlanBuilder) buildSet(v *ast.SetStmt) (Plan, error) { p := &Set{} for _, vars := range v.Variables { assign := &expression.VarAssignment{ @@ -247,7 +263,7 @@ func (b *planBuilder) buildSet(v *ast.SetStmt) (Plan, error) { } // Detect aggregate function or groupby clause. -func (b *planBuilder) detectSelectAgg(sel *ast.SelectStmt) bool { +func (b *PlanBuilder) detectSelectAgg(sel *ast.SelectStmt) bool { if sel.GroupBy != nil { return true } @@ -364,13 +380,13 @@ func findIndexByName(indices []*model.IndexInfo, name model.CIStr) *model.IndexI return nil } -func (b *planBuilder) buildSelectLock(src LogicalPlan, lock ast.SelectLockType) *LogicalLock { +func (b *PlanBuilder) buildSelectLock(src LogicalPlan, lock ast.SelectLockType) *LogicalLock { selectLock := LogicalLock{Lock: lock}.init(b.ctx) selectLock.SetChildren(src) return selectLock } -func (b *planBuilder) buildPrepare(x *ast.PrepareStmt) Plan { +func (b *PlanBuilder) buildPrepare(x *ast.PrepareStmt) Plan { p := &Prepare{ Name: x.Name, } @@ -382,7 +398,7 @@ func (b *planBuilder) buildPrepare(x *ast.PrepareStmt) Plan { return p } -func (b *planBuilder) buildCheckIndex(dbName model.CIStr, as *ast.AdminStmt) (Plan, error) { +func (b *PlanBuilder) buildCheckIndex(dbName model.CIStr, as *ast.AdminStmt) (Plan, error) { tblName := as.Tables[0] tbl, err := b.is.TableByName(dbName, tblName.Name) if err != nil { @@ -443,7 +459,7 @@ func (b *planBuilder) buildCheckIndex(dbName model.CIStr, as *ast.AdminStmt) (Pl return rootT.p, nil } -func (b *planBuilder) buildAdmin(as *ast.AdminStmt) (Plan, error) { +func (b *PlanBuilder) buildAdmin(as *ast.AdminStmt) (Plan, error) { var ret Plan var err error switch as.Tp { @@ -514,7 +530,7 @@ func (b *planBuilder) buildAdmin(as *ast.AdminStmt) (Plan, error) { return ret, nil } -func (b *planBuilder) buildAdminCheckTable(as *ast.AdminStmt) (*CheckTable, error) { +func (b *PlanBuilder) buildAdminCheckTable(as *ast.AdminStmt) (*CheckTable, error) { p := &CheckTable{Tables: as.Tables} p.GenExprs = make(map[string]expression.Expression) @@ -555,7 +571,7 @@ func (b *planBuilder) buildAdminCheckTable(as *ast.AdminStmt) (*CheckTable, erro return p, nil } -func (b *planBuilder) buildCheckIndexSchema(tn *ast.TableName, indexName string) (*expression.Schema, error) { +func (b *PlanBuilder) buildCheckIndexSchema(tn *ast.TableName, indexName string) (*expression.Schema, error) { schema := expression.NewSchema() indexName = strings.ToLower(indexName) indicesInfo := tn.TableInfo.Indices @@ -637,7 +653,7 @@ func getPhysicalIDs(tblInfo *model.TableInfo) []int64 { return []int64{tblInfo.ID} } -func (b *planBuilder) buildAnalyzeTable(as *ast.AnalyzeTableStmt) Plan { +func (b *PlanBuilder) buildAnalyzeTable(as *ast.AnalyzeTableStmt) Plan { p := &Analyze{MaxNumBuckets: as.MaxNumBuckets} for _, tbl := range as.TableNames { idxInfo, colInfo, pkInfo := getColsInfo(tbl) @@ -656,7 +672,7 @@ func (b *planBuilder) buildAnalyzeTable(as *ast.AnalyzeTableStmt) Plan { return p } -func (b *planBuilder) buildAnalyzeIndex(as *ast.AnalyzeTableStmt) (Plan, error) { +func (b *PlanBuilder) buildAnalyzeIndex(as *ast.AnalyzeTableStmt) (Plan, error) { p := &Analyze{MaxNumBuckets: as.MaxNumBuckets} tblInfo := as.TableNames[0].TableInfo physicalIDs := getPhysicalIDs(tblInfo) @@ -672,7 +688,7 @@ func (b *planBuilder) buildAnalyzeIndex(as *ast.AnalyzeTableStmt) (Plan, error) return p, nil } -func (b *planBuilder) buildAnalyzeAllIndex(as *ast.AnalyzeTableStmt) Plan { +func (b *PlanBuilder) buildAnalyzeAllIndex(as *ast.AnalyzeTableStmt) Plan { p := &Analyze{MaxNumBuckets: as.MaxNumBuckets} tblInfo := as.TableNames[0].TableInfo physicalIDs := getPhysicalIDs(tblInfo) @@ -691,7 +707,7 @@ const ( numBucketsLimit = 1024 ) -func (b *planBuilder) buildAnalyze(as *ast.AnalyzeTableStmt) (Plan, error) { +func (b *PlanBuilder) buildAnalyze(as *ast.AnalyzeTableStmt) (Plan, error) { if as.MaxNumBuckets == 0 { as.MaxNumBuckets = defaultMaxNumBuckets } else { @@ -824,7 +840,7 @@ func splitWhere(where ast.ExprNode) []ast.ExprNode { return conditions } -func (b *planBuilder) buildShow(show *ast.ShowStmt) (Plan, error) { +func (b *PlanBuilder) buildShow(show *ast.ShowStmt) (Plan, error) { p := Show{ Tp: show.Tp, DBName: show.DBName, @@ -884,7 +900,7 @@ func (b *planBuilder) buildShow(show *ast.ShowStmt) (Plan, error) { return p, nil } -func (b *planBuilder) buildSimple(node ast.StmtNode) Plan { +func (b *PlanBuilder) buildSimple(node ast.StmtNode) Plan { p := &Simple{Statement: node} switch raw := node.(type) { @@ -941,7 +957,7 @@ func collectVisitInfoFromGrantStmt(vi []visitInfo, stmt *ast.GrantStmt) []visitI return vi } -func (b *planBuilder) getDefaultValue(col *table.Column) (*expression.Constant, error) { +func (b *PlanBuilder) getDefaultValue(col *table.Column) (*expression.Constant, error) { value, err := table.GetColDefaultValue(b.ctx, col.ToInfo()) if err != nil { return nil, errors.Trace(err) @@ -949,7 +965,7 @@ func (b *planBuilder) getDefaultValue(col *table.Column) (*expression.Constant, return &expression.Constant{Value: value, RetType: &col.FieldType}, nil } -func (b *planBuilder) findDefaultValue(cols []*table.Column, name *ast.ColumnName) (*expression.Constant, error) { +func (b *PlanBuilder) findDefaultValue(cols []*table.Column, name *ast.ColumnName) (*expression.Constant, error) { for _, col := range cols { if col.Name.L == name.Name.L { return b.getDefaultValue(col) @@ -960,7 +976,7 @@ func (b *planBuilder) findDefaultValue(cols []*table.Column, name *ast.ColumnNam // resolveGeneratedColumns resolves generated columns with their generation // expressions respectively. onDups indicates which columns are in on-duplicate list. -func (b *planBuilder) resolveGeneratedColumns(columns []*table.Column, onDups map[string]struct{}, mockPlan LogicalPlan) (igc InsertGeneratedColumns, err error) { +func (b *PlanBuilder) resolveGeneratedColumns(columns []*table.Column, onDups map[string]struct{}, mockPlan LogicalPlan) (igc InsertGeneratedColumns, err error) { for _, column := range columns { if !column.IsGenerated() { continue @@ -995,7 +1011,7 @@ func (b *planBuilder) resolveGeneratedColumns(columns []*table.Column, onDups ma return igc, nil } -func (b *planBuilder) buildInsert(insert *ast.InsertStmt) (Plan, error) { +func (b *PlanBuilder) buildInsert(insert *ast.InsertStmt) (Plan, error) { ts, ok := insert.Table.TableRefs.Left.(*ast.TableSource) if !ok { return nil, infoschema.ErrTableNotExists.GenWithStackByArgs() @@ -1115,7 +1131,7 @@ func (p *Insert) validateOnDup(onDup []*ast.Assignment, colMap map[string]*table return onDupColSet, dupCols, nil } -func (b *planBuilder) getAffectCols(insertStmt *ast.InsertStmt, insertPlan *Insert) (affectedValuesCols []*table.Column, err error) { +func (b *PlanBuilder) getAffectCols(insertStmt *ast.InsertStmt, insertPlan *Insert) (affectedValuesCols []*table.Column, err error) { if len(insertStmt.Columns) > 0 { // This branch is for the following scenarios: // 1. `INSERT INTO tbl_name (col_name [, col_name] ...) {VALUES | VALUE} (value_list) [, (value_list)] ...`, @@ -1138,7 +1154,7 @@ func (b *planBuilder) getAffectCols(insertStmt *ast.InsertStmt, insertPlan *Inse return affectedValuesCols, nil } -func (b *planBuilder) buildSetValuesOfInsert(insert *ast.InsertStmt, insertPlan *Insert, mockTablePlan *LogicalTableDual, checkRefColumn func(n ast.Node) ast.Node) error { +func (b *PlanBuilder) buildSetValuesOfInsert(insert *ast.InsertStmt, insertPlan *Insert, mockTablePlan *LogicalTableDual, checkRefColumn func(n ast.Node) ast.Node) error { tableInfo := insertPlan.Table.Meta() colNames := make([]string, 0, len(insert.Setlist)) exprCols := make([]*expression.Column, 0, len(insert.Setlist)) @@ -1179,7 +1195,7 @@ func (b *planBuilder) buildSetValuesOfInsert(insert *ast.InsertStmt, insertPlan return nil } -func (b *planBuilder) buildValuesListOfInsert(insert *ast.InsertStmt, insertPlan *Insert, mockTablePlan *LogicalTableDual, checkRefColumn func(n ast.Node) ast.Node) error { +func (b *PlanBuilder) buildValuesListOfInsert(insert *ast.InsertStmt, insertPlan *Insert, mockTablePlan *LogicalTableDual, checkRefColumn func(n ast.Node) ast.Node) error { affectedValuesCols, err := b.getAffectCols(insert, insertPlan) if err != nil { return errors.Trace(err) @@ -1240,12 +1256,12 @@ func (b *planBuilder) buildValuesListOfInsert(insert *ast.InsertStmt, insertPlan return nil } -func (b *planBuilder) buildSelectPlanOfInsert(insert *ast.InsertStmt, insertPlan *Insert) error { +func (b *PlanBuilder) buildSelectPlanOfInsert(insert *ast.InsertStmt, insertPlan *Insert) error { affectedValuesCols, err := b.getAffectCols(insert, insertPlan) if err != nil { return errors.Trace(err) } - selectPlan, err := b.build(insert.Select) + selectPlan, err := b.Build(insert.Select) if err != nil { return errors.Trace(err) } @@ -1268,7 +1284,7 @@ func (b *planBuilder) buildSelectPlanOfInsert(insert *ast.InsertStmt, insertPlan } } - insertPlan.SelectPlan, err = doOptimize(b.optFlag, selectPlan.(LogicalPlan)) + insertPlan.SelectPlan, err = DoOptimize(b.optFlag, selectPlan.(LogicalPlan)) if err != nil { return errors.Trace(err) } @@ -1293,7 +1309,7 @@ func (b *planBuilder) buildSelectPlanOfInsert(insert *ast.InsertStmt, insertPlan return nil } -func (b *planBuilder) buildLoadData(ld *ast.LoadDataStmt) (Plan, error) { +func (b *PlanBuilder) buildLoadData(ld *ast.LoadDataStmt) (Plan, error) { p := &LoadData{ IsLocal: ld.IsLocal, Path: ld.Path, @@ -1321,12 +1337,12 @@ func (b *planBuilder) buildLoadData(ld *ast.LoadDataStmt) (Plan, error) { return p, nil } -func (b *planBuilder) buildLoadStats(ld *ast.LoadStatsStmt) Plan { +func (b *PlanBuilder) buildLoadStats(ld *ast.LoadStatsStmt) Plan { p := &LoadStats{Path: ld.Path} return p } -func (b *planBuilder) buildDDL(node ast.DDLNode) Plan { +func (b *PlanBuilder) buildDDL(node ast.DDLNode) Plan { switch v := node.(type) { case *ast.AlterTableStmt: b.visitInfo = append(b.visitInfo, visitInfo{ @@ -1403,7 +1419,7 @@ func (b *planBuilder) buildDDL(node ast.DDLNode) Plan { // buildTrace builds a trace plan. Inside this method, it first optimize the // underlying query and then constructs a schema, which will be used to constructs // rows result. -func (b *planBuilder) buildTrace(trace *ast.TraceStmt) (Plan, error) { +func (b *PlanBuilder) buildTrace(trace *ast.TraceStmt) (Plan, error) { if _, ok := trace.Stmt.(*ast.SelectStmt); !ok { return nil, errors.New("trace only supports select query") } @@ -1420,11 +1436,11 @@ func (b *planBuilder) buildTrace(trace *ast.TraceStmt) (Plan, error) { return p, nil } -func (b *planBuilder) buildExplain(explain *ast.ExplainStmt) (Plan, error) { +func (b *PlanBuilder) buildExplain(explain *ast.ExplainStmt) (Plan, error) { if show, ok := explain.Stmt.(*ast.ShowStmt); ok { return b.buildShow(show) } - targetPlan, err := Optimize(b.ctx, explain.Stmt, b.is) + targetPlan, err := OptimizeAstNode(b.ctx, explain.Stmt, b.is) if err != nil { return nil, errors.Trace(err) } diff --git a/planner/core/point_get_plan.go b/planner/core/point_get_plan.go index 86f5968099ac1..6afe6e6017401 100644 --- a/planner/core/point_get_plan.go +++ b/planner/core/point_get_plan.go @@ -114,7 +114,7 @@ func (p *PointGetPlan) SetChildren(...PhysicalPlan) {} // ResolveIndices resolves the indices for columns. After doing this, the columns can evaluate the rows by their indices. func (p *PointGetPlan) ResolveIndices() {} -func tryFastPlan(ctx sessionctx.Context, node ast.Node) Plan { +func TryFastPlan(ctx sessionctx.Context, node ast.Node) Plan { if PreparedPlanCacheEnabled() { // Do not support plan cache. return nil diff --git a/planner/optimize.go b/planner/optimize.go new file mode 100644 index 0000000000000..bc6a0f43b030e --- /dev/null +++ b/planner/optimize.go @@ -0,0 +1,74 @@ +// Copyright 2018 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +package planner + +import ( + "github.com/pingcap/tidb/ast" + "github.com/pingcap/tidb/infoschema" + plannercore "github.com/pingcap/tidb/planner/core" + "github.com/pingcap/tidb/privilege" + "github.com/pingcap/tidb/sessionctx" + "github.com/pkg/errors" +) + +// Optimize does optimization and creates a Plan. +// The node must be prepared first. +func Optimize(ctx sessionctx.Context, node ast.Node, is infoschema.InfoSchema) (plannercore.Plan, error) { + fp := plannercore.TryFastPlan(ctx, node) + if fp != nil { + return fp, nil + } + + // build logical plan + ctx.GetSessionVars().PlanID = 0 + ctx.GetSessionVars().PlanColumnID = 0 + builder := plannercore.NewPlanBuilder(ctx, is) + p, err := builder.Build(node) + if err != nil { + return nil, err + } + + // Check privilege. Maybe it's better to move this to the Preprocess, but + // we need the table information to check privilege, which is collected + // into the visitInfo in the logical plan builder. + if pm := privilege.GetPrivilegeManager(ctx); pm != nil { + if !plannercore.CheckPrivilege(pm, builder.GetVisitInfo()) { + return nil, errors.New("privilege check fail") + } + } + + // Handle the execute statement. + if execPlan, ok := p.(*plannercore.Execute); ok { + err := execPlan.OptimizePreparedPlan(ctx, is) + return p, errors.Trace(err) + } + + // Handle the non-logical plan statement. + logic, isLogicalPlan := p.(plannercore.LogicalPlan) + if !isLogicalPlan { + return p, nil + } + + // Handle the logical plan statement, use cascades planner if enabled. + if ctx.GetSessionVars().EnableCascadesPlanner { + return nil, errors.New("The cascades planner is not implemented yet.") + } else { + return plannercore.DoOptimize(builder.GetOptFlag(), logic) + } + +} + +func init() { + plannercore.OptimizeAstNode = Optimize +} diff --git a/session/session.go b/session/session.go index bf6de1a3d2b1c..54e536887b6d5 100644 --- a/session/session.go +++ b/session/session.go @@ -1279,6 +1279,7 @@ const loadCommonGlobalVarsSQL = "select HIGH_PRIORITY * from mysql.global_variab variable.TiDBOptInSubqUnFolding + quoteCommaQuote + variable.TiDBDistSQLScanConcurrency + quoteCommaQuote + variable.TiDBMaxChunkSize + quoteCommaQuote + + variable.TiDBEnableCascadesPlanner + quoteCommaQuote + variable.TiDBRetryLimit + quoteCommaQuote + variable.TiDBDisableTxnAutoRetry + "')" diff --git a/sessionctx/variable/session.go b/sessionctx/variable/session.go index e9fc0aa336d92..5398984cfd900 100644 --- a/sessionctx/variable/session.go +++ b/sessionctx/variable/session.go @@ -289,6 +289,9 @@ type SessionVars struct { // EnableTablePartition enables table partition feature. EnableTablePartition bool + // EnableCascadesPlanner enables the cascades planner. + EnableCascadesPlanner bool + // DDLReorgPriority is the operation priority of adding indices. DDLReorgPriority int @@ -600,6 +603,8 @@ func (s *SessionVars) SetSystemVar(name string, val string) error { s.DisableTxnAutoRetry = TiDBOptOn(val) case TiDBEnableStreaming: s.EnableStreaming = TiDBOptOn(val) + case TiDBEnableCascadesPlanner: + s.EnableCascadesPlanner = TiDBOptOn(val) case TiDBOptimizerSelectivityLevel: s.OptimizerSelectivityLevel = tidbOptPositiveInt32(val, DefTiDBOptimizerSelectivityLevel) case TiDBEnableTablePartition: diff --git a/sessionctx/variable/sysvar.go b/sessionctx/variable/sysvar.go index 1be040e0d8919..b35e91cb8a1e6 100644 --- a/sessionctx/variable/sysvar.go +++ b/sessionctx/variable/sysvar.go @@ -639,6 +639,7 @@ var defaultSysVars = []*SysVar{ {ScopeSession, TiDBDMLBatchSize, strconv.Itoa(DefDMLBatchSize)}, {ScopeSession, TiDBCurrentTS, strconv.Itoa(DefCurretTS)}, {ScopeGlobal | ScopeSession, TiDBMaxChunkSize, strconv.Itoa(DefMaxChunkSize)}, + {ScopeGlobal | ScopeSession, TiDBEnableCascadesPlanner, "0"}, {ScopeSession, TIDBMemQuotaQuery, strconv.FormatInt(config.GetGlobalConfig().MemQuotaQuery, 10)}, {ScopeSession, TIDBMemQuotaHashJoin, strconv.FormatInt(DefTiDBMemQuotaHashJoin, 10)}, {ScopeSession, TIDBMemQuotaMergeJoin, strconv.FormatInt(DefTiDBMemQuotaMergeJoin, 10)}, diff --git a/sessionctx/variable/tidb_vars.go b/sessionctx/variable/tidb_vars.go index 823d044d52beb..bc48d33363bf8 100644 --- a/sessionctx/variable/tidb_vars.go +++ b/sessionctx/variable/tidb_vars.go @@ -158,6 +158,9 @@ const ( // tidb_max_chunk_capacity is used to control the max chunk size during query execution. TiDBMaxChunkSize = "tidb_max_chunk_size" + // tidb_enable_cascades_planner is used to control whether to enable the cascades planner. + TiDBEnableCascadesPlanner = "tidb_enable_cascades_planner" + // tidb_skip_utf8_check skips the UTF8 validate process, validate UTF8 has performance cost, if we can make sure // the input string values are valid, we can skip the check. TiDBSkipUTF8Check = "tidb_skip_utf8_check" diff --git a/sessionctx/variable/varsutil.go b/sessionctx/variable/varsutil.go index ff27620b55cef..b45544288d2a7 100644 --- a/sessionctx/variable/varsutil.go +++ b/sessionctx/variable/varsutil.go @@ -291,7 +291,7 @@ func ValidateSetSystemVar(vars *SessionVars, name string, value string) (string, case AutocommitVar, TiDBSkipUTF8Check, TiDBOptAggPushDown, TiDBOptInSubqUnFolding, TiDBEnableTablePartition, TiDBBatchInsert, TiDBDisableTxnAutoRetry, TiDBEnableStreaming, - TiDBBatchDelete: + TiDBBatchDelete, TiDBEnableCascadesPlanner: if strings.EqualFold(value, "ON") || value == "1" || strings.EqualFold(value, "OFF") || value == "0" { return value, nil } From 07256a2df1ca2b907074595216914374059f2bb9 Mon Sep 17 00:00:00 2001 From: Jian Zhang Date: Fri, 12 Oct 2018 10:27:12 +0800 Subject: [PATCH 2/2] fix ut && make lint happy --- planner/core/cbo_test.go | 11 ++++++----- planner/core/common_plans.go | 1 + planner/core/optimizer.go | 4 +++- planner/core/physical_plan_test.go | 23 ++++++++++++----------- planner/core/planbuilder.go | 4 ++++ planner/core/point_get_plan.go | 1 + planner/optimize.go | 6 ++---- 7 files changed, 29 insertions(+), 21 deletions(-) diff --git a/planner/core/cbo_test.go b/planner/core/cbo_test.go index 47582450a59b7..a87d21dc59ab6 100644 --- a/planner/core/cbo_test.go +++ b/planner/core/cbo_test.go @@ -21,6 +21,7 @@ import ( "github.com/pingcap/tidb/config" "github.com/pingcap/tidb/domain" "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/planner" "github.com/pingcap/tidb/planner/core" "github.com/pingcap/tidb/session" "github.com/pingcap/tidb/sessionctx" @@ -323,7 +324,7 @@ func (s *testAnalyzeSuite) TestIndexRead(c *C) { is := domain.GetDomain(ctx).InfoSchema() err = core.Preprocess(ctx, stmt, is, false) c.Assert(err, IsNil) - p, err := core.Optimize(ctx, stmt, is) + p, err := planner.Optimize(ctx, stmt, is) c.Assert(err, IsNil) c.Assert(core.ToString(p), Equals, tt.best, Commentf("for %s", tt.sql)) } @@ -373,7 +374,7 @@ func (s *testAnalyzeSuite) TestEmptyTable(c *C) { is := domain.GetDomain(ctx).InfoSchema() err = core.Preprocess(ctx, stmt, is, false) c.Assert(err, IsNil) - p, err := core.Optimize(ctx, stmt, is) + p, err := planner.Optimize(ctx, stmt, is) c.Assert(err, IsNil) c.Assert(core.ToString(p), Equals, tt.best, Commentf("for %s", tt.sql)) } @@ -485,7 +486,7 @@ func (s *testAnalyzeSuite) TestAnalyze(c *C) { is := domain.GetDomain(ctx).InfoSchema() err = core.Preprocess(ctx, stmt, is, false) c.Assert(err, IsNil) - p, err := core.Optimize(ctx, stmt, is) + p, err := planner.Optimize(ctx, stmt, is) c.Assert(err, IsNil) c.Assert(core.ToString(p), Equals, tt.best, Commentf("for %s", tt.sql)) } @@ -561,7 +562,7 @@ func (s *testAnalyzeSuite) TestPreparedNullParam(c *C) { is := domain.GetDomain(ctx).InfoSchema() err = core.Preprocess(ctx, stmt, is, true) c.Assert(err, IsNil) - p, err := core.Optimize(ctx, stmt, is) + p, err := planner.Optimize(ctx, stmt, is) c.Assert(err, IsNil) c.Assert(core.ToString(p), Equals, best, Commentf("for %s", sql)) @@ -811,7 +812,7 @@ func BenchmarkOptimize(b *testing.B) { b.Run(tt.sql, func(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := core.Optimize(ctx, stmt, is) + _, err := planner.Optimize(ctx, stmt, is) c.Assert(err, IsNil) } b.ReportAllocs() diff --git a/planner/core/common_plans.go b/planner/core/common_plans.go index c6ecce85b01ac..82a60716c13be 100644 --- a/planner/core/common_plans.go +++ b/planner/core/common_plans.go @@ -135,6 +135,7 @@ type Execute struct { Plan Plan } +// OptimizePreparedPlan optimizes the prepared statement. func (e *Execute) OptimizePreparedPlan(ctx sessionctx.Context, is infoschema.InfoSchema) error { vars := ctx.GetSessionVars() if e.Name != "" { diff --git a/planner/core/optimizer.go b/planner/core/optimizer.go index 56bf1137eb0be..40b51540bc59a 100644 --- a/planner/core/optimizer.go +++ b/planner/core/optimizer.go @@ -25,7 +25,7 @@ import ( "github.com/pkg/errors" ) -// EvalAstExpr evaluates ast expression directly. +// OptimizeAstNode optimizes the query to a physical plan directly. var OptimizeAstNode func(ctx sessionctx.Context, node ast.Node, is infoschema.InfoSchema) (Plan, error) // AllowCartesianProduct means whether tidb allows cartesian join without equal conditions. @@ -78,6 +78,7 @@ func BuildLogicalPlan(ctx sessionctx.Context, node ast.Node, is infoschema.InfoS return p, nil } +// CheckPrivilege checks the privilege for a user. func CheckPrivilege(pm privilege.Manager, vs []visitInfo) bool { for _, v := range vs { if !pm.RequestVerification(v.db, v.table, v.column, v.privilege) { @@ -87,6 +88,7 @@ func CheckPrivilege(pm privilege.Manager, vs []visitInfo) bool { return true } +// DoOptimize optimizes a logical plan to a physical plan. func DoOptimize(flag uint64, logic LogicalPlan) (PhysicalPlan, error) { logic, err := logicalOptimize(flag, logic) if err != nil { diff --git a/planner/core/physical_plan_test.go b/planner/core/physical_plan_test.go index 332171a49f6f8..13285cce38efb 100644 --- a/planner/core/physical_plan_test.go +++ b/planner/core/physical_plan_test.go @@ -19,6 +19,7 @@ import ( "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/model" "github.com/pingcap/tidb/parser" + "github.com/pingcap/tidb/planner" "github.com/pingcap/tidb/planner/core" "github.com/pingcap/tidb/session" "github.com/pingcap/tidb/sessionctx" @@ -196,7 +197,7 @@ func (s *testPlanSuite) TestDAGPlanBuilderSimpleCase(c *C) { err = se.NewTxn() c.Assert(err, IsNil) - p, err := core.Optimize(se, stmt, s.is) + p, err := planner.Optimize(se, stmt, s.is) c.Assert(err, IsNil) c.Assert(core.ToString(p), Equals, tt.best, comment) } @@ -417,7 +418,7 @@ func (s *testPlanSuite) TestDAGPlanBuilderJoin(c *C) { stmt, err := s.ParseOneStmt(tt.sql, "", "") c.Assert(err, IsNil, comment) - p, err := core.Optimize(se, stmt, s.is) + p, err := planner.Optimize(se, stmt, s.is) c.Assert(err, IsNil) c.Assert(core.ToString(p), Equals, tt.best, comment) } @@ -487,7 +488,7 @@ func (s *testPlanSuite) TestDAGPlanBuilderSubquery(c *C) { stmt, err := s.ParseOneStmt(tt.sql, "", "") c.Assert(err, IsNil, comment) - p, err := core.Optimize(se, stmt, s.is) + p, err := planner.Optimize(se, stmt, s.is) c.Assert(err, IsNil) c.Assert(core.ToString(p), Equals, tt.best, Commentf("for %s", tt.sql)) } @@ -544,7 +545,7 @@ func (s *testPlanSuite) TestDAGPlanTopN(c *C) { stmt, err := s.ParseOneStmt(tt.sql, "", "") c.Assert(err, IsNil, comment) - p, err := core.Optimize(se, stmt, s.is) + p, err := planner.Optimize(se, stmt, s.is) c.Assert(err, IsNil) c.Assert(core.ToString(p), Equals, tt.best, comment) } @@ -648,7 +649,7 @@ func (s *testPlanSuite) TestDAGPlanBuilderBasePhysicalPlan(c *C) { c.Assert(err, IsNil, comment) core.Preprocess(se, stmt, s.is, false) - p, err := core.Optimize(se, stmt, s.is) + p, err := planner.Optimize(se, stmt, s.is) c.Assert(err, IsNil) c.Assert(core.ToString(p), Equals, tt.best, Commentf("for %s", tt.sql)) } @@ -697,7 +698,7 @@ func (s *testPlanSuite) TestDAGPlanBuilderUnion(c *C) { stmt, err := s.ParseOneStmt(tt.sql, "", "") c.Assert(err, IsNil, comment) - p, err := core.Optimize(se, stmt, s.is) + p, err := planner.Optimize(se, stmt, s.is) c.Assert(err, IsNil) c.Assert(core.ToString(p), Equals, tt.best, comment) } @@ -764,7 +765,7 @@ func (s *testPlanSuite) TestDAGPlanBuilderUnionScan(c *C) { // Make txn not read only. se.Txn().Set(kv.Key("AAA"), []byte("BBB")) se.StmtCommit() - p, err := core.Optimize(se, stmt, s.is) + p, err := planner.Optimize(se, stmt, s.is) c.Assert(err, IsNil) c.Assert(core.ToString(p), Equals, tt.best, Commentf("for %s", tt.sql)) } @@ -924,7 +925,7 @@ func (s *testPlanSuite) TestDAGPlanBuilderAgg(c *C) { stmt, err := s.ParseOneStmt(tt.sql, "", "") c.Assert(err, IsNil, comment) - p, err := core.Optimize(se, stmt, s.is) + p, err := planner.Optimize(se, stmt, s.is) c.Assert(err, IsNil) c.Assert(core.ToString(p), Equals, tt.best, Commentf("for %s", tt.sql)) } @@ -1157,7 +1158,7 @@ func (s *testPlanSuite) TestRefine(c *C) { c.Assert(err, IsNil, comment) sc := se.(sessionctx.Context).GetSessionVars().StmtCtx sc.IgnoreTruncate = false - p, err := core.Optimize(se, stmt, s.is) + p, err := planner.Optimize(se, stmt, s.is) c.Assert(err, IsNil) c.Assert(core.ToString(p), Equals, tt.best, Commentf("for %s", tt.sql)) } @@ -1228,7 +1229,7 @@ func (s *testPlanSuite) TestAggEliminater(c *C) { c.Assert(err, IsNil, comment) sc := se.(sessionctx.Context).GetSessionVars().StmtCtx sc.IgnoreTruncate = false - p, err := core.Optimize(se, stmt, s.is) + p, err := planner.Optimize(se, stmt, s.is) c.Assert(err, IsNil) c.Assert(core.ToString(p), Equals, tt.best, Commentf("for %s", tt.sql)) } @@ -1265,7 +1266,7 @@ func (s *testPlanSuite) TestRequestTypeSupportedOff(c *C) { stmt, err := s.ParseOneStmt(sql, "", "") c.Assert(err, IsNil) - p, err := core.Optimize(se, stmt, s.is) + p, err := planner.Optimize(se, stmt, s.is) c.Assert(err, IsNil) c.Assert(core.ToString(p), Equals, expect, Commentf("for %s", sql)) } diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index a043ed73ebf4f..4bc1d6bd25208 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -130,14 +130,17 @@ type PlanBuilder struct { inStraightJoin bool } +// GetVisitInfo gets the visitInfo of the PlanBuilder. func (b *PlanBuilder) GetVisitInfo() []visitInfo { return b.visitInfo } +// GetOptFlag gets the optFlag of the PlanBuilder. func (b *PlanBuilder) GetOptFlag() uint64 { return b.optFlag } +// NewPlanBuilder creates a new PlanBuilder. func NewPlanBuilder(sctx sessionctx.Context, is infoschema.InfoSchema) *PlanBuilder { return &PlanBuilder{ ctx: sctx, @@ -146,6 +149,7 @@ func NewPlanBuilder(sctx sessionctx.Context, is infoschema.InfoSchema) *PlanBuil } } +// Build builds the ast node to a Plan. func (b *PlanBuilder) Build(node ast.Node) (Plan, error) { b.optFlag = flagPrunColumns switch x := node.(type) { diff --git a/planner/core/point_get_plan.go b/planner/core/point_get_plan.go index 6afe6e6017401..c3dde6f418aa4 100644 --- a/planner/core/point_get_plan.go +++ b/planner/core/point_get_plan.go @@ -114,6 +114,7 @@ func (p *PointGetPlan) SetChildren(...PhysicalPlan) {} // ResolveIndices resolves the indices for columns. After doing this, the columns can evaluate the rows by their indices. func (p *PointGetPlan) ResolveIndices() {} +// TryFastPlan tries to use the PointGetPlan for the query. func TryFastPlan(ctx sessionctx.Context, node ast.Node) Plan { if PreparedPlanCacheEnabled() { // Do not support plan cache. diff --git a/planner/optimize.go b/planner/optimize.go index bc6a0f43b030e..50c2706436107 100644 --- a/planner/optimize.go +++ b/planner/optimize.go @@ -62,11 +62,9 @@ func Optimize(ctx sessionctx.Context, node ast.Node, is infoschema.InfoSchema) ( // Handle the logical plan statement, use cascades planner if enabled. if ctx.GetSessionVars().EnableCascadesPlanner { - return nil, errors.New("The cascades planner is not implemented yet.") - } else { - return plannercore.DoOptimize(builder.GetOptFlag(), logic) + return nil, errors.New("the cascades planner is not implemented yet") } - + return plannercore.DoOptimize(builder.GetOptFlag(), logic) } func init() {