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: fix wrong cached plan in cachedPlanExec (#34579) #34598

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions planner/core/prepare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ import (
"github.com/pingcap/tidb/planner/core"
"github.com/pingcap/tidb/session"
"github.com/pingcap/tidb/sessionctx/variable"
<<<<<<< HEAD
=======
"github.com/pingcap/tidb/testkit"
"github.com/pingcap/tidb/types"
>>>>>>> cd297b9f5... planner: fix wrong cached plan in cachedPlanExec (#34579)
"github.com/pingcap/tidb/util/hint"
"github.com/pingcap/tidb/util/israce"
"github.com/pingcap/tidb/util/kvcache"
Expand All @@ -42,8 +47,59 @@ import (
dto "github.com/prometheus/client_model/go"
)

<<<<<<< HEAD
var _ = Suite(&testPrepareSuite{})
var _ = SerialSuites(&testPrepareSerialSuite{})
=======
func TestPointGetPreparedPlan4PlanCache(t *testing.T) {
store, clean := testkit.CreateMockStore(t)
defer clean()
orgEnable := core.PreparedPlanCacheEnabled()
defer core.SetPreparedPlanCache(orgEnable)
core.SetPreparedPlanCache(true)
se, err := session.CreateSession4TestWithOpt(store, &session.Opt{
PreparedPlanCache: kvcache.NewSimpleLRUCache(100, 0.1, math.MaxUint64),
})
require.NoError(t, err)
tk1 := testkit.NewTestKitWithSession(t, store, se)
tk1.MustExec("drop database if exists ps_text")
defer tk1.MustExec("drop database if exists ps_text")
tk1.MustExec("create database ps_text")
tk1.MustExec("use ps_text")

tk1.MustExec(`create table t (a int, b int, c int,
primary key k_a(a),
unique key k_b(b))`)
tk1.MustExec("insert into t values (1, 1, 1)")
tk1.MustExec("insert into t values (2, 2, 2)")
tk1.MustExec("insert into t values (3, 3, 3)")

pspk1Id, _, _, err := tk1.Session().PrepareStmt("select * from t where a = ?")
require.NoError(t, err)
tk1.Session().GetSessionVars().PreparedStmts[pspk1Id].(*core.CachedPrepareStmt).PreparedAst.UseCache = false

ctx := context.Background()
// first time plan generated
_, err = tk1.Session().ExecutePreparedStmt(ctx, pspk1Id, []types.Datum{types.NewDatum(0)})
require.NoError(t, err)

// using the generated plan but with different params
_, err = tk1.Session().ExecutePreparedStmt(ctx, pspk1Id, []types.Datum{types.NewDatum(nil)})
require.NoError(t, err)
}

func TestPreparePointGetWithDML(t *testing.T) {
store, clean := testkit.CreateMockStore(t)
defer clean()
orgEnable := core.PreparedPlanCacheEnabled()
defer core.SetPreparedPlanCache(orgEnable)
core.SetPreparedPlanCache(true)
se, err := session.CreateSession4TestWithOpt(store, &session.Opt{
PreparedPlanCache: kvcache.NewSimpleLRUCache(100, 0.1, math.MaxUint64),
})
require.NoError(t, err)
tk := testkit.NewTestKitWithSession(t, store, se)
>>>>>>> cd297b9f5... planner: fix wrong cached plan in cachedPlanExec (#34579)

type testPrepareSuite struct {
}
Expand Down
28 changes: 22 additions & 6 deletions session/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -1823,20 +1823,27 @@ func (s *session) preparedStmtExec(ctx context.Context,
return runStmt(ctx, s, st)
}

<<<<<<< HEAD
// cachedPlanExec short path currently ONLY for cached "point select plan" execution
func (s *session) cachedPlanExec(ctx context.Context,
is infoschema.InfoSchema, snapshotTS uint64,
stmtID uint32, prepareStmt *plannercore.CachedPrepareStmt, args []types.Datum) (sqlexec.RecordSet, error) {
=======
// cachedPointPlanExec is a short path currently ONLY for cached "point select plan" execution
func (s *session) cachedPointPlanExec(ctx context.Context,
is infoschema.InfoSchema, stmtID uint32, prepareStmt *plannercore.CachedPrepareStmt, replicaReadScope string, args []types.Datum) (sqlexec.RecordSet, bool, error) {

>>>>>>> cd297b9f5... planner: fix wrong cached plan in cachedPlanExec (#34579)
prepared := prepareStmt.PreparedAst
// compile ExecStmt
execAst := &ast.ExecuteStmt{ExecID: stmtID}
if err := executor.ResetContextOfStmt(s, execAst); err != nil {
return nil, err
return nil, false, err
}
execAst.BinaryArgs = args
execPlan, err := planner.OptimizeExecStmt(ctx, s, execAst, is)
if err != nil {
return nil, err
return nil, false, err
}

stmtCtx := s.GetSessionVars().StmtCtx
Expand Down Expand Up @@ -1874,7 +1881,7 @@ func (s *session) cachedPlanExec(ctx context.Context,

// run ExecStmt
var resultSet sqlexec.RecordSet
switch prepared.CachedPlan.(type) {
switch execPlan.(type) {
case *plannercore.PointGetPlan:
resultSet, err = stmt.PointGet(ctx, is)
s.txn.changeToInvalid()
Expand All @@ -1889,11 +1896,10 @@ func (s *session) cachedPlanExec(ctx context.Context,
}
resultSet, err = runStmt(ctx, s, stmt)
default:
err = errors.Errorf("invalid cached plan type %T", prepared.CachedPlan)
prepared.CachedPlan = nil
return nil, err
return nil, false, nil
}
return resultSet, err
return resultSet, true, err
}

// IsCachedExecOk check if we can execute using plan cached in prepared structure
Expand Down Expand Up @@ -1979,7 +1985,17 @@ func (s *session) ExecutePreparedStmt(ctx context.Context, stmtID uint32, args [
is = s.GetInfoSchema().(infoschema.InfoSchema)
}
if ok {
<<<<<<< HEAD
return s.cachedPlanExec(ctx, is, snapshotTS, stmtID, preparedStmt, args)
=======
rs, ok, err := s.cachedPointPlanExec(ctx, txnManager.GetTxnInfoSchema(), stmtID, preparedStmt, replicaReadScope, args)
if err != nil {
return nil, err
}
if ok { // fallback to preparedStmtExec if we cannot get a valid point select plan in cachedPointPlanExec
return rs, nil
}
>>>>>>> cd297b9f5... planner: fix wrong cached plan in cachedPlanExec (#34579)
}
return s.preparedStmtExec(ctx, is, snapshotTS, stmtID, preparedStmt, args)
}
Expand Down