Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

planner: handle PointPlan specially in execute statements #36987

Merged
merged 14 commits into from
Aug 9, 2022
29 changes: 25 additions & 4 deletions executor/prepared.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,16 @@ func CompileExecutePreparedStmt(ctx context.Context, sctx sessionctx.Context,
defer func() {
sctx.GetSessionVars().DurationCompile = time.Since(startTime)
}()

preparedObj, err := plannercore.GetPreparedStmt(execStmt, sctx.GetSessionVars())
if err != nil {
return nil, err
}
pointPlanShortPathOK, err := plannercore.IsPointPlanShortPathOK(sctx, is, preparedObj)
if err != nil {
return nil, err
}

execPlan, names, err := planner.Optimize(ctx, sctx, execStmt, is)
if err != nil {
return nil, err
Expand All @@ -342,13 +352,24 @@ func CompileExecutePreparedStmt(ctx context.Context, sctx sessionctx.Context,
OutputNames: names,
Ti: &TelemetryInfo{},
}
preparedObj, err := plannercore.GetPreparedStmt(execStmt, sctx.GetSessionVars())
if err != nil {
return nil, err
}
stmtCtx := sctx.GetSessionVars().StmtCtx
stmt.Text = preparedObj.PreparedAst.Stmt.Text()
stmtCtx.OriginalSQL = stmt.Text
stmtCtx.InitSQLDigest(preparedObj.NormalizedSQL, preparedObj.SQLDigest)

// handle point plan short path specially
if pointPlanShortPathOK {
if ep, ok := execPlan.(*plannercore.Execute); ok {
if pointPlan, ok := ep.Plan.(*plannercore.PointGetPlan); ok {
stmtCtx.SetPlan(execPlan)
stmtCtx.SetPlanDigest(preparedObj.NormalizedPlan, preparedObj.PlanDigest)
stmt.Plan = pointPlan
stmt.PsStmt = preparedObj
Reminiscent marked this conversation as resolved.
Show resolved Hide resolved
} else {
// invalid the previous cached point plan
preparedObj.PreparedAst.CachedPlan = nil
}
}
}
return stmt, nil
}
39 changes: 39 additions & 0 deletions planner/core/plan_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/pingcap/tidb/sessionctx"
"github.com/pingcap/tidb/sessionctx/stmtctx"
"github.com/pingcap/tidb/sessionctx/variable"
"github.com/pingcap/tidb/sessiontxn/staleread"
"github.com/pingcap/tidb/table/tables"
"github.com/pingcap/tidb/types"
"github.com/pingcap/tidb/util/chunk"
Expand Down Expand Up @@ -631,3 +632,41 @@ func GetBindSQL4PlanCache(sctx sessionctx.Context, preparedStmt *CachedPrepareSt
}
return "", ignore
}

// IsPointPlanShortPathOK check if we can execute using plan cached in prepared structure
// Be careful with the short path, current precondition is ths cached plan satisfying
// IsPointGetWithPKOrUniqueKeyByAutoCommit
func IsPointPlanShortPathOK(sctx sessionctx.Context, is infoschema.InfoSchema, preparedStmt *CachedPrepareStmt) (bool, error) {
prepared := preparedStmt.PreparedAst
if prepared.CachedPlan == nil || staleread.IsStmtStaleness(sctx) {
return false, nil
}
// check auto commit
if !IsAutoCommitTxn(sctx) {
return false, nil
}
if prepared.SchemaVersion != is.SchemaMetaVersion() {
prepared.CachedPlan = nil
preparedStmt.ColumnInfos = nil
return false, nil
}
// maybe we'd better check cached plan type here, current
// only point select/update will be cached, see "getPhysicalPlan" func
var ok bool
var err error
switch prepared.CachedPlan.(type) {
case *PointGetPlan:
ok = true
case *Update:
pointUpdate := prepared.CachedPlan.(*Update)
_, ok = pointUpdate.SelectPlan.(*PointGetPlan)
if !ok {
err = errors.Errorf("cached update plan not point update")
prepared.CachedPlan = nil
return false, err
}
default:
ok = false
}
return ok, err
}
3 changes: 2 additions & 1 deletion session/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,10 +324,11 @@ func BenchmarkPreparedPointGet(b *testing.B) {
b.Fatal(err)
}

params := expression.Args2Expressions4Test(64)
alloc := chunk.NewAllocator()
b.ResetTimer()
for i := 0; i < b.N; i++ {
rs, err := se.ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(64))
rs, err := se.ExecutePreparedStmt(ctx, stmtID, params)
if err != nil {
b.Fatal(err)
}
Expand Down
7 changes: 7 additions & 0 deletions session/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -2302,6 +2302,13 @@ func (s *session) preparedStmtExec(ctx context.Context, execStmt *ast.ExecuteStm
}
sessionExecuteCompileDurationGeneral.Observe(time.Since(s.sessionVars.StartTime).Seconds())
logGeneralQuery(st, s, true)

if st.PsStmt != nil { // point plan short path
resultSet, err := st.PointGet(ctx)
s.txn.changeToInvalid()
return resultSet, err
}

return runStmt(ctx, s, st)
}

Expand Down