From 1bc4305bf6d52ead5da129b9592c6dcadad7c926 Mon Sep 17 00:00:00 2001 From: you06 Date: Fri, 13 May 2022 10:42:35 +0800 Subject: [PATCH] cherry pick #34523 to release-6.0 Signed-off-by: ti-srebot --- executor/adapter.go | 2 +- planner/core/logical_plan_builder.go | 4 ++- planner/core/planbuilder.go | 4 ++- planner/core/point_get_plan.go | 1 + planner/core/prepare_test.go | 47 ++++++++++++++++++++++++++++ planner/core/rule_build_key_info.go | 4 ++- planner/optimize.go | 7 ++++- session/session.go | 6 ++++ 8 files changed, 70 insertions(+), 5 deletions(-) diff --git a/executor/adapter.go b/executor/adapter.go index 62c5ec29a6fd8..3efee33863631 100644 --- a/executor/adapter.go +++ b/executor/adapter.go @@ -811,7 +811,7 @@ type pessimisticTxn interface { KeysNeedToLock() ([]kv.Key, error) } -// buildExecutor build a executor from plan, prepared statement may need additional procedure. +// buildExecutor build an executor from plan, prepared statement may need additional procedure. func (a *ExecStmt) buildExecutor() (Executor, error) { ctx := a.Ctx stmtCtx := ctx.GetSessionVars().StmtCtx diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index d7cad573edd46..08417374aec95 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -4295,7 +4295,9 @@ func (ds *DataSource) ExtractFD() *fd.FDSet { changed bool err error ) - if ds.isForUpdateRead { + check := ds.ctx.GetSessionVars().IsIsolation(ast.ReadCommitted) || ds.isForUpdateRead + check = check && ds.ctx.GetSessionVars().ConnectionID > 0 + if check { latestIndexes, changed, err = getLatestIndexInfo(ds.ctx, ds.table.Meta().ID, 0) if err != nil { ds.fdSet = fds diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index ee409fefec101..8261b954bfa08 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -1095,6 +1095,7 @@ func getPossibleAccessPaths(ctx sessionctx.Context, tableHints *tableHintInfo, i optimizerUseInvisibleIndexes := ctx.GetSessionVars().OptimizerUseInvisibleIndexes + check = check || ctx.GetSessionVars().IsIsolation(ast.ReadCommitted) check = check && ctx.GetSessionVars().ConnectionID > 0 var latestIndexes map[int64]*model.IndexInfo var err error @@ -1592,7 +1593,8 @@ func (b *PlanBuilder) buildPhysicalIndexLookUpReaders(ctx context.Context, dbNam indexInfos := make([]*model.IndexInfo, 0, len(tblInfo.Indices)) indexLookUpReaders := make([]Plan, 0, len(tblInfo.Indices)) - check := b.isForUpdateRead && b.ctx.GetSessionVars().ConnectionID > 0 + check := b.isForUpdateRead || b.ctx.GetSessionVars().IsIsolation(ast.ReadCommitted) + check = check && b.ctx.GetSessionVars().ConnectionID > 0 var latestIndexes map[int64]*model.IndexInfo var err error diff --git a/planner/core/point_get_plan.go b/planner/core/point_get_plan.go index bc0dec4ce0aa1..26950954cfbea 100644 --- a/planner/core/point_get_plan.go +++ b/planner/core/point_get_plan.go @@ -967,6 +967,7 @@ func tryPointGetPlan(ctx sessionctx.Context, selStmt *ast.SelectStmt, check bool return nil } + check = check || ctx.GetSessionVars().IsIsolation(ast.ReadCommitted) check = check && ctx.GetSessionVars().ConnectionID > 0 var latestIndexes map[int64]*model.IndexInfo var err error diff --git a/planner/core/prepare_test.go b/planner/core/prepare_test.go index 3675aee5b5bc0..8dcc9a9d6cbdf 100644 --- a/planner/core/prepare_test.go +++ b/planner/core/prepare_test.go @@ -2723,3 +2723,50 @@ func TestCachedTable(t *testing.T) { require.True(t, lastReadFromCache(tk)) tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) } + +func TestPlanCacheWithRCWhenInfoSchemaChange(t *testing.T) { + orgEnable := core.PreparedPlanCacheEnabled() + defer func() { + core.SetPreparedPlanCache(orgEnable) + }() + core.SetPreparedPlanCache(true) + + ctx := context.Background() + store, clean := testkit.CreateMockStore(t) + defer clean() + + tk1 := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + tk2.MustExec("use test") + tk1.MustExec("drop table if exists t1") + tk1.MustExec("create table t1(id int primary key, c int, index ic (c))") + // prepare text protocol + tk1.MustExec("prepare s from 'select /*+use_index(t1, ic)*/ * from t1 where 1'") + // prepare binary protocol + stmtID, _, _, err := tk2.Session().PrepareStmt("select /*+use_index(t1, ic)*/ * from t1 where 1") + require.Nil(t, err) + tk1.MustExec("set tx_isolation='READ-COMMITTED'") + tk1.MustExec("begin pessimistic") + tk2.MustExec("set tx_isolation='READ-COMMITTED'") + tk2.MustExec("begin pessimistic") + tk1.MustQuery("execute s").Check(testkit.Rows()) + rs, err := tk2.Session().ExecutePreparedStmt(ctx, stmtID, []types.Datum{}) + require.Nil(t, err) + tk2.ResultSetToResult(rs, fmt.Sprintf("%v", rs)).Check(testkit.Rows()) + + tk3 := testkit.NewTestKit(t, store) + tk3.MustExec("use test") + tk3.MustExec("alter table t1 drop index ic") + tk3.MustExec("insert into t1 values(1, 0)") + + // The execution after schema changed should not hit plan cache. + // execute text protocol + tk1.MustQuery("execute s").Check(testkit.Rows("1 0")) + tk1.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) + // execute binary protocol + rs, err = tk2.Session().ExecutePreparedStmt(ctx, stmtID, []types.Datum{}) + require.Nil(t, err) + tk2.ResultSetToResult(rs, fmt.Sprintf("%v", rs)).Check(testkit.Rows("1 0")) + tk2.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) +} diff --git a/planner/core/rule_build_key_info.go b/planner/core/rule_build_key_info.go index ae8a0a1d3a566..41e8b195af45a 100644 --- a/planner/core/rule_build_key_info.go +++ b/planner/core/rule_build_key_info.go @@ -267,8 +267,10 @@ func (ds *DataSource) BuildKeyInfo(selfSchema *expression.Schema, childSchema [] var latestIndexes map[int64]*model.IndexInfo var changed bool var err error + check := ds.ctx.GetSessionVars().IsIsolation(ast.ReadCommitted) || ds.isForUpdateRead + check = check && ds.ctx.GetSessionVars().ConnectionID > 0 // we should check index valid while forUpdateRead, see detail in https://github.com/pingcap/tidb/pull/22152 - if ds.isForUpdateRead { + if check { latestIndexes, changed, err = getLatestIndexInfo(ds.ctx, ds.table.Meta().ID, 0) if err != nil { return diff --git a/planner/optimize.go b/planner/optimize.go index fcf58000a2976..ec5429df4bd39 100644 --- a/planner/optimize.go +++ b/planner/optimize.go @@ -73,7 +73,12 @@ func GetExecuteForUpdateReadIS(node ast.Node, sctx sessionctx.Context) infoschem execID = vars.PreparedStmtNameToID[execStmt.Name] } if preparedPointer, ok := vars.PreparedStmts[execID]; ok { - if preparedObj, ok := preparedPointer.(*core.CachedPrepareStmt); ok && preparedObj.ForUpdateRead { + checkSchema := vars.IsIsolation(ast.ReadCommitted) + if !checkSchema { + preparedObj, ok := preparedPointer.(*core.CachedPrepareStmt) + checkSchema = ok && preparedObj.ForUpdateRead + } + if checkSchema { is := domain.GetDomain(sctx).InfoSchema() return temptable.AttachLocalTemporaryTableInfoSchema(sctx, is) } diff --git a/session/session.go b/session/session.go index 51efe43531ee4..6f6ccbf69067f 100644 --- a/session/session.go +++ b/session/session.go @@ -2311,6 +2311,12 @@ func (s *session) ExecutePreparedStmt(ctx context.Context, stmtID uint32, args [ if err != nil { return nil, errors.Trace(err) } +<<<<<<< HEAD +======= + } else if s.sessionVars.IsIsolation(ast.ReadCommitted) || preparedStmt.ForUpdateRead { + is = domain.GetDomain(s).InfoSchema() + is = temptable.AttachLocalTemporaryTableInfoSchema(s, is) +>>>>>>> 0703a64f7... planner: plan cache always check scheme valid in RC isolation level (#34523) } else { is = s.GetInfoSchema().(infoschema.InfoSchema) }