Skip to content

Commit

Permalink
This is an automated cherry-pick of #40686
Browse files Browse the repository at this point in the history
Signed-off-by: ti-chi-bot <ti-community-prow-bot@tidb.io>
  • Loading branch information
qw4990 authored and ti-chi-bot committed Jan 28, 2023
1 parent d1dcac6 commit db07f19
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 2 deletions.
25 changes: 23 additions & 2 deletions expression/builtin_compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -1565,12 +1565,11 @@ func (c *compareFunctionClass) refineArgs(ctx sessionctx.Context, args []Express
arg0Type, arg1Type := args[0].GetType(), args[1].GetType()
arg0IsInt := arg0Type.EvalType() == types.ETInt
arg1IsInt := arg1Type.EvalType() == types.ETInt
arg0IsString := arg0Type.EvalType() == types.ETString
arg1IsString := arg1Type.EvalType() == types.ETString
arg0, arg0IsCon := args[0].(*Constant)
arg1, arg1IsCon := args[1].(*Constant)
isExceptional, finalArg0, finalArg1 := false, args[0], args[1]
isPositiveInfinite, isNegativeInfinite := false, false
<<<<<<< HEAD
if MaybeOverOptimized4PlanCache(ctx, args) {
// To keep the result be compatible with MySQL, refine `int non-constant <cmp> str constant`
// here and skip this refine operation in all other cases for safety.
Expand All @@ -1589,8 +1588,15 @@ func (c *compareFunctionClass) refineArgs(ctx sessionctx.Context, args []Express
// We should remove the mutable constant for correctness, because its value may be changed.
RemoveMutableConst(ctx, args)
}
=======
>>>>>>> 465ab74532 (planner: skip the plan cache if non-int values are converted into int when optimization (#40686))
// int non-constant [cmp] non-int constant
if arg0IsInt && !arg0IsCon && !arg1IsInt && arg1IsCon {
if MaybeOverOptimized4PlanCache(ctx, []Expression{arg1}) {
ctx.GetSessionVars().StmtCtx.SetSkipPlanCache(errors.Errorf("skip plan-cache: '%v' may be converted to INT", arg1.String()))
RemoveMutableConst(ctx, args)
}

arg1, isExceptional = RefineComparedConstant(ctx, *arg0Type, arg1, c.op)
// Why check not null flag
// eg: int_col > const_val(which is less than min_int32)
Expand Down Expand Up @@ -1618,6 +1624,11 @@ func (c *compareFunctionClass) refineArgs(ctx sessionctx.Context, args []Express
}
// non-int constant [cmp] int non-constant
if arg1IsInt && !arg1IsCon && !arg0IsInt && arg0IsCon {
if MaybeOverOptimized4PlanCache(ctx, []Expression{arg0}) {
ctx.GetSessionVars().StmtCtx.SetSkipPlanCache(errors.Errorf("skip plan-cache: '%v' may be converted to INT", arg0.String()))
RemoveMutableConst(ctx, args)
}

arg0, isExceptional = RefineComparedConstant(ctx, *arg1Type, arg0, symmetricOp[c.op])
if !isExceptional || (isExceptional && mysql.HasNotNullFlag(arg1Type.GetFlag())) {
finalArg0 = arg0
Expand All @@ -1635,6 +1646,11 @@ func (c *compareFunctionClass) refineArgs(ctx sessionctx.Context, args []Express
}
// int constant [cmp] year type
if arg0IsCon && arg0IsInt && arg1Type.GetType() == mysql.TypeYear && !arg0.Value.IsNull() {
if MaybeOverOptimized4PlanCache(ctx, []Expression{arg0}) {
ctx.GetSessionVars().StmtCtx.SetSkipPlanCache(errors.Errorf("skip plan-cache: '%v' may be converted to YEAR", arg0.String()))
RemoveMutableConst(ctx, args)
}

adjusted, failed := types.AdjustYear(arg0.Value.GetInt64(), false)
if failed == nil {
arg0.Value.SetInt64(adjusted)
Expand All @@ -1643,6 +1659,11 @@ func (c *compareFunctionClass) refineArgs(ctx sessionctx.Context, args []Express
}
// year type [cmp] int constant
if arg1IsCon && arg1IsInt && arg0Type.GetType() == mysql.TypeYear && !arg1.Value.IsNull() {
if MaybeOverOptimized4PlanCache(ctx, []Expression{arg1}) {
ctx.GetSessionVars().StmtCtx.SetSkipPlanCache(errors.Errorf("skip plan-cache: '%v' may be converted to YEAR", arg1.String()))
RemoveMutableConst(ctx, args)
}

adjusted, failed := types.AdjustYear(arg1.Value.GetInt64(), false)
if failed == nil {
arg1.Value.SetInt64(adjusted)
Expand Down
5 changes: 5 additions & 0 deletions planner/core/expression_rewriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -1559,9 +1559,14 @@ func (er *expressionRewriter) inToExpression(lLen int, not bool, tp *types.Field
} else {
continue
}
<<<<<<< HEAD
} else if er.sctx.GetSessionVars().StmtCtx.SkipPlanCache {
// We should remove the mutable constant for correctness, because its value may be changed.
expression.RemoveMutableConst(er.sctx, []expression.Expression{c})
=======
er.sctx.GetSessionVars().StmtCtx.SetSkipPlanCache(errors.Errorf("skip plan-cache: '%v' may be converted to INT", c.String()))
expression.RemoveMutableConst(er.sctx, args)
>>>>>>> 465ab74532 (planner: skip the plan cache if non-int values are converted into int when optimization (#40686))
}
args[i], isExceptional = expression.RefineComparedConstant(er.sctx, *leftFt, c, opcode.EQ)
if isExceptional {
Expand Down
127 changes: 127 additions & 0 deletions planner/core/plan_cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,3 +231,130 @@ func TestPlanCacheDiagInfo(t *testing.T) {
tk.MustExec("execute stmt using @a, @b") // a=1 and a=1 -> a=1
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 skip plan-cache: some parameters may be overwritten"))
}
<<<<<<< HEAD
=======

func TestIssue40224(t *testing.T) {
store := testkit.CreateMockStore(t)
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.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 skip plan-cache: '1.0' may be converted to INT"))
tk.MustExec("execute st using @a, @b")
tkProcess := tk.Session().ShowProcess()
ps := []*util.ProcessInfo{tkProcess}
tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps})
tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).CheckAt([]int{0},
[][]interface{}{
{"IndexReader_6"},
{"└─IndexRangeScan_5"}, // range scan not full scan
})

tk.MustExec("set @a=1, @b=2")
tk.MustExec("execute st using @a, @b")
tk.MustQuery("show warnings").Check(testkit.Rows()) // no warning for INT values
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")
tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).CheckAt([]int{0},
[][]interface{}{
{"IndexReader_6"},
{"└─IndexRangeScan_5"}, // range scan not full scan
})
}

func TestIssue40225(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("create table t (a int, key(a))")
tk.MustExec("prepare st from 'select * from t where a<?'")
tk.MustExec("set @a='1'")
tk.MustExec("execute st using @a")
tk.MustQuery("show warnings").Sort().Check(testkit.Rows("Warning 1105 skip plan-cache: '1' may be converted to INT")) // plan-cache limitation
tk.MustExec("create binding for select * from t where a<1 using select /*+ ignore_plan_cache() */ * from t where a<1")
tk.MustExec("execute st using @a")
tk.MustQuery("show warnings").Sort().Check(testkit.Rows("Warning 1105 skip plan-cache: ignore plan cache by binding"))
// no warning about plan-cache limitations('1' -> INT) since plan-cache is totally disabled.

tk.MustExec("prepare st from 'select * from t where a>?'")
tk.MustExec("set @a=1")
tk.MustExec("execute st using @a")
tk.MustExec("execute st using @a")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustExec("create binding for select * from t where a>1 using select /*+ ignore_plan_cache() */ * from t where a>1")
tk.MustExec("execute st using @a")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustExec("execute st using @a")
tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1"))
}

func TestPlanCacheWithLimit(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(a int primary key, b int)")

testCases := []struct {
sql string
params []int
}{
{"prepare stmt from 'select * from t limit ?'", []int{1}},
{"prepare stmt from 'select * from t limit ?, ?'", []int{1, 2}},
{"prepare stmt from 'delete from t order by a limit ?'", []int{1}},
{"prepare stmt from 'insert into t select * from t order by a desc limit ?'", []int{1}},
{"prepare stmt from 'insert into t select * from t order by a desc limit ?, ?'", []int{1, 2}},
{"prepare stmt from 'update t set a = 1 limit ?'", []int{1}},
{"prepare stmt from '(select * from t order by a limit ?) union (select * from t order by a desc limit ?)'", []int{1, 2}},
{"prepare stmt from 'select * from t where a = ? limit ?, ?'", []int{1, 1, 1}},
{"prepare stmt from 'select * from t where a in (?, ?) limit ?, ?'", []int{1, 2, 1, 1}},
}

for idx, testCase := range testCases {
tk.MustExec(testCase.sql)
var using []string
for i, p := range testCase.params {
tk.MustExec(fmt.Sprintf("set @a%d = %d", i, p))
using = append(using, fmt.Sprintf("@a%d", i))
}

tk.MustExec("execute stmt using " + strings.Join(using, ", "))
tk.MustExec("execute stmt using " + strings.Join(using, ", "))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))

if idx < 6 {
tk.MustExec("set @a0 = 6")
tk.MustExec("execute stmt using " + strings.Join(using, ", "))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
}
}

tk.MustExec("prepare stmt from 'select * from t limit ?'")
tk.MustExec("set @a = 10001")
tk.MustExec("execute stmt using @a")
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 skip plan-cache: limit count more than 10000"))
}

func TestIssue40679(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("create table t (a int, key(a));")
tk.MustExec("prepare st from 'select * from t use index(a) where a < ?'")
tk.MustExec("set @a1=1.1")
tk.MustExec("execute st using @a1")

tkProcess := tk.Session().ShowProcess()
ps := []*util.ProcessInfo{tkProcess}
tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps})
rows := tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Rows()
require.True(t, strings.Contains(rows[1][0].(string), "RangeScan")) // RangeScan not FullScan

tk.MustExec("execute st using @a1")
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 skip plan-cache: '1.1' may be converted to INT"))
}
>>>>>>> 465ab74532 (planner: skip the plan cache if non-int values are converted into int when optimization (#40686))

0 comments on commit db07f19

Please sign in to comment.