Skip to content

Commit

Permalink
planner: fix the issue that NonPrep Cache cannot work with Explain we…
Browse files Browse the repository at this point in the history
…ll (#40044)

close #40039
  • Loading branch information
qw4990 authored Dec 20, 2022
1 parent ae58fa1 commit 9f4dd80
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 13 deletions.
2 changes: 1 addition & 1 deletion executor/prepared.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func (e *PrepareExec) Next(ctx context.Context, req *chunk.Chunk) error {
return err
}
}
stmt, p, paramCnt, err := plannercore.GeneratePlanCacheStmtWithAST(ctx, e.ctx, stmt0)
stmt, p, paramCnt, err := plannercore.GeneratePlanCacheStmtWithAST(ctx, e.ctx, stmt0.Text(), stmt0)
if err != nil {
return err
}
Expand Down
31 changes: 31 additions & 0 deletions planner/core/plan_cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,37 @@ func TestInitLRUWithSystemVar(t *testing.T) {
require.NotNil(t, lru)
}

func TestNonPreparedPlanCacheWithExplain(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec(`use test`)
tk.MustExec("create table t(a int)")
tk.MustExec("set tidb_enable_non_prepared_plan_cache=1")
tk.MustExec("select * from t where a=1") // cache this plan

tk.MustQuery("explain select * from t where a=2").Check(testkit.Rows(
`Selection_8 10.00 root eq(test.t.a, 2)`,
`└─TableReader_7 10.00 root data:Selection_6`,
` └─Selection_6 10.00 cop[tikv] eq(test.t.a, 2)`,
` └─TableFullScan_5 10000.00 cop[tikv] table:t keep order:false, stats:pseudo`))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))

tk.MustQuery("explain format=verbose select * from t where a=2").Check(testkit.Rows(
`Selection_8 10.00 169474.57 root eq(test.t.a, 2)`,
`└─TableReader_7 10.00 168975.57 root data:Selection_6`,
` └─Selection_6 10.00 2534000.00 cop[tikv] eq(test.t.a, 2)`,
` └─TableFullScan_5 10000.00 2035000.00 cop[tikv] table:t keep order:false, stats:pseudo`))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))

tk.MustQuery("explain analyze select * from t where a=2").CheckAt([]int{0, 1, 2, 3}, [][]interface{}{
{"Selection_8", "10.00", "0", "root"},
{"└─TableReader_7", "10.00", "0", "root"},
{" └─Selection_6", "10.00", "0", "cop[tikv]"},
{" └─TableFullScan_5", "10000.00", "0", "cop[tikv]"},
})
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
}

func TestNonPreparedPlanCacheBasically(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
Expand Down
24 changes: 13 additions & 11 deletions planner/core/plan_cache_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,19 @@ func (e *paramMarkerExtractor) Leave(in ast.Node) (ast.Node, bool) {
}

// GeneratePlanCacheStmtWithAST generates the PlanCacheStmt structure for this AST.
func GeneratePlanCacheStmtWithAST(ctx context.Context, sctx sessionctx.Context, stmt ast.StmtNode) (*PlanCacheStmt, Plan, int, error) {
// paramSQL is the corresponding parameterized sql like 'select * from t where a<? and b>?'.
// paramStmt is the Node of paramSQL.
func GeneratePlanCacheStmtWithAST(ctx context.Context, sctx sessionctx.Context, paramSQL string, paramStmt ast.StmtNode) (*PlanCacheStmt, Plan, int, error) {
vars := sctx.GetSessionVars()
var extractor paramMarkerExtractor
stmt.Accept(&extractor)
paramStmt.Accept(&extractor)

// DDL Statements can not accept parameters
if _, ok := stmt.(ast.DDLNode); ok && len(extractor.markers) > 0 {
if _, ok := paramStmt.(ast.DDLNode); ok && len(extractor.markers) > 0 {
return nil, nil, 0, ErrPrepareDDL
}

switch stmt.(type) {
switch paramStmt.(type) {
case *ast.LoadDataStmt, *ast.PrepareStmt, *ast.ExecuteStmt, *ast.DeallocateStmt, *ast.NonTransactionalDMLStmt:
return nil, nil, 0, ErrUnsupportedPs
}
Expand All @@ -86,7 +88,7 @@ func GeneratePlanCacheStmtWithAST(ctx context.Context, sctx sessionctx.Context,
}

ret := &PreprocessorReturn{}
err := Preprocess(ctx, sctx, stmt, InPrepare, WithPreprocessorReturn(ret))
err := Preprocess(ctx, sctx, paramStmt, InPrepare, WithPreprocessorReturn(ret))
if err != nil {
return nil, nil, 0, err
}
Expand All @@ -103,8 +105,8 @@ func GeneratePlanCacheStmtWithAST(ctx context.Context, sctx sessionctx.Context,
}

prepared := &ast.Prepared{
Stmt: stmt,
StmtType: ast.GetStmtLabel(stmt),
Stmt: paramStmt,
StmtType: ast.GetStmtLabel(paramStmt),
Params: extractor.markers,
SchemaVersion: ret.InfoSchema.SchemaMetaVersion(),
}
Expand All @@ -117,12 +119,12 @@ func GeneratePlanCacheStmtWithAST(ctx context.Context, sctx sessionctx.Context,
if !vars.EnablePreparedPlanCache {
prepared.UseCache = false
} else {
cacheable, reason := CacheableWithCtx(sctx, stmt, ret.InfoSchema)
cacheable, reason := CacheableWithCtx(sctx, paramStmt, ret.InfoSchema)
prepared.UseCache = cacheable
if !cacheable {
sctx.GetSessionVars().StmtCtx.AppendWarning(errors.Errorf("skip plan-cache: " + reason))
}
selectStmtNode, normalizedSQL4PC, digest4PC, err = ExtractSelectAndNormalizeDigest(stmt, vars.CurrentDB)
selectStmtNode, normalizedSQL4PC, digest4PC, err = ExtractSelectAndNormalizeDigest(paramStmt, vars.CurrentDB)
if err != nil || selectStmtNode == nil {
normalizedSQL4PC = ""
digest4PC = ""
Expand All @@ -138,15 +140,15 @@ func GeneratePlanCacheStmtWithAST(ctx context.Context, sctx sessionctx.Context,

var p Plan
destBuilder, _ := NewPlanBuilder().Init(sctx, ret.InfoSchema, &hint.BlockHintProcessor{})
p, err = destBuilder.Build(ctx, stmt)
p, err = destBuilder.Build(ctx, paramStmt)
if err != nil {
return nil, nil, 0, err
}

preparedObj := &PlanCacheStmt{
PreparedAst: prepared,
StmtDB: vars.CurrentDB,
StmtText: stmt.Text(),
StmtText: paramSQL,
VisitInfos: destBuilder.GetVisitInfo(),
NormalizedSQL: normalizedSQL,
SQLDigest: digest,
Expand Down
2 changes: 1 addition & 1 deletion planner/optimize.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func getPlanFromNonPreparedPlanCache(ctx context.Context, sctx sessionctx.Contex
}
val := sctx.GetSessionVars().GetNonPreparedPlanCacheStmt(paramSQL)
if val == nil {
cachedStmt, _, _, err := core.GeneratePlanCacheStmtWithAST(ctx, sctx, stmt)
cachedStmt, _, _, err := core.GeneratePlanCacheStmtWithAST(ctx, sctx, paramSQL, stmt)
if err != nil {
return nil, nil, false, err
}
Expand Down

0 comments on commit 9f4dd80

Please sign in to comment.