diff --git a/executor/prepared_test.go b/executor/prepared_test.go index 3e1f8e8ef40fd..4bee947f471f3 100644 --- a/executor/prepared_test.go +++ b/executor/prepared_test.go @@ -181,6 +181,38 @@ func TestIssue38533(t *testing.T) { tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) } +func TestIssue40224(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + + orgEnable := plannercore.PreparedPlanCacheEnabled() + defer func() { + plannercore.SetPreparedPlanCache(orgEnable) + }() + plannercore.SetPreparedPlanCache(true) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("create table t (a int, key(a))") + tk.MustExec("prepare st from 'select a from t where a in (?, ?)'") + tk.MustExec("set @a=1.0, @b=2.0") + tk.MustExec("execute st using @a, @b") + tk.MustExec("execute st using @a, @b") + tkProcess := tk.Session().ShowProcess() + ps := []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&mockSessionManager1{PS: ps}) + rs := tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)) + require.True(t, strings.Contains(rs.Rows()[1][0].(string), "RangeScan")) // range-scan instead of full-scan + + tk.MustExec("set @a=1, @b=2") + tk.MustExec("execute st using @a, @b") + tk.MustExec("execute st using @a, @b") + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) // cacheable for INT + tk.MustExec("execute st using @a, @b") + rs = tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)) + require.True(t, strings.Contains(rs.Rows()[1][0].(string), "RangeScan")) // range-scan instead of full-scan +} + func TestIssue29850(t *testing.T) { store, dom, err := newStoreWithBootstrap() require.NoError(t, err) diff --git a/planner/core/expression_rewriter.go b/planner/core/expression_rewriter.go index 1071d5fd6ebc4..abf8e4ae4c392 100644 --- a/planner/core/expression_rewriter.go +++ b/planner/core/expression_rewriter.go @@ -1473,16 +1473,10 @@ func (er *expressionRewriter) inToExpression(lLen int, not bool, tp *types.Field if c, ok := args[i].(*expression.Constant); ok { var isExceptional bool if expression.MaybeOverOptimized4PlanCache(er.sctx, []expression.Expression{c}) { - if c.GetType().EvalType() == types.ETString { - // To keep the result be compatible with MySQL, refine `int non-constant str constant` - // here and skip this refine operation in all other cases for safety. - er.sctx.GetSessionVars().StmtCtx.SkipPlanCache = true - expression.RemoveMutableConst(er.sctx, []expression.Expression{c}) - } else { - continue + if c.GetType().EvalType() == types.ETInt { + continue // no need to refine it } - } else if er.sctx.GetSessionVars().StmtCtx.SkipPlanCache { - // We should remove the mutable constant for correctness, because its value may be changed. + er.sctx.GetSessionVars().StmtCtx.SkipPlanCache = true expression.RemoveMutableConst(er.sctx, []expression.Expression{c}) } args[i], isExceptional = expression.RefineComparedConstant(er.sctx, *leftFt, c, opcode.EQ)