Skip to content

Commit

Permalink
cherry pick pingcap#28944 to release-5.2
Browse files Browse the repository at this point in the history
Signed-off-by: ti-srebot <ti-srebot@pingcap.com>
  • Loading branch information
Reminiscent authored and ti-srebot committed Nov 4, 2021
1 parent da1c21f commit 207c896
Show file tree
Hide file tree
Showing 8 changed files with 1,151 additions and 1 deletion.
921 changes: 921 additions & 0 deletions executor/explainfor_test.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion executor/prepared_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ func (s *testSerialSuite) TestIssue28087And28162(c *C) {
tk.MustQuery(`execute stmt using @a,@b,@c`).Check(testkit.Rows("\x01"))
tk.MustExec(`set @a=0x00, @b=0x00, @c=0x01`)
tk.MustQuery(`execute stmt using @a,@b,@c`).Check(testkit.Rows("\x00", "\x01"))
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1"))
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0"))

// issue 28162
tk.MustExec(`drop table if exists IDT_MC21780`)
Expand Down
16 changes: 16 additions & 0 deletions executor/testdata/prepare_suite_out.json
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,16 @@
}
],
"Plan": [
<<<<<<< HEAD
"Projection_4 8000.00 root test.t1.a",
"└─IndexReader_10 8000.00 root index:Selection_9",
" └─Selection_9 8000.00 cop[tikv] eq(cast(test.t1.b, double BINARY), 0)",
" └─IndexFullScan_8 10000.00 cop[tikv] table:t1, index:b(b, a) keep order:false, stats:pseudo"
=======
"Projection_4 10.00 root test.t1.a",
"└─IndexReader_6 10.00 root index:IndexRangeScan_5",
" └─IndexRangeScan_5 10.00 cop[tikv] table:t1, index:b(b, a) range:[0,0], keep order:false, stats:pseudo"
>>>>>>> 83e559db0... planner: allow refineArgs for plan cache in some situations (#28944)
],
"LastPlanUseCache": "0",
"Result": null
Expand Down Expand Up @@ -202,13 +208,23 @@
}
],
"Plan": [
<<<<<<< HEAD
"HashJoin_38 63744383.74 root inner join, equal:[eq(test.t1.b, test.t2.b) eq(test.t1.a, test.t2.a)]",
"├─TableReader_59(Build) 7984.01 root data:Selection_58",
"│ └─Selection_58 7984.01 cop[tikv] eq(cast(test.t2.b, double BINARY), 0), not(isnull(test.t2.a)), not(isnull(test.t2.b))",
"│ └─TableFullScan_57 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo",
"└─TableReader_52(Probe) 7984.01 root data:Selection_51",
" └─Selection_51 7984.01 cop[tikv] eq(cast(test.t1.b, double BINARY), 0), not(isnull(test.t1.a)), not(isnull(test.t1.b))",
" └─TableFullScan_50 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo"
=======
"HashJoin_36 124.88 root inner join, equal:[eq(test.t1.a, test.t2.a)]",
"├─IndexLookUp_57(Build) 99.90 root ",
"│ ├─IndexRangeScan_55(Build) 99.90 cop[tikv] table:t2, index:b(b, a) range:[0 -inf,0 +inf], keep order:false, stats:pseudo",
"│ └─TableRowIDScan_56(Probe) 99.90 cop[tikv] table:t2 keep order:false, stats:pseudo",
"└─IndexLookUp_51(Probe) 99.90 root ",
" ├─IndexRangeScan_49(Build) 99.90 cop[tikv] table:t1, index:b(b, a) range:[0 -inf,0 +inf], keep order:false, stats:pseudo",
" └─TableRowIDScan_50(Probe) 99.90 cop[tikv] table:t1 keep order:false, stats:pseudo"
>>>>>>> 83e559db0... planner: allow refineArgs for plan cache in some situations (#28944)
],
"LastPlanUseCache": "0",
"Result": null
Expand Down
17 changes: 17 additions & 0 deletions expression/builtin_compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -1360,17 +1360,34 @@ func RefineComparedConstant(ctx sessionctx.Context, targetFieldType types.FieldT
// refineArgs will rewrite the arguments if the compare expression is `int column <cmp> non-int constant` or
// `non-int constant <cmp> int column`. E.g., `a < 1.1` will be rewritten to `a < 2`. It also handles comparing year type
// with int constant if the int constant falls into a sensible year representation.
// This refine operation depends on the values of these args, but these values can change when using plan-cache.
// So we have to skip this operation or mark the plan as over-optimized when using plan-cache.
func (c *compareFunctionClass) refineArgs(ctx sessionctx.Context, args []Expression) []Expression {
<<<<<<< HEAD
if ContainMutableConst(ctx, args) {
return args
}
=======
>>>>>>> 83e559db0... planner: allow refineArgs for plan cache in some situations (#28944)
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
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.
if (arg0IsInt && !arg0IsCon && arg1IsString && arg1IsCon) || (arg1IsInt && !arg1IsCon && arg0IsString && arg0IsCon) {
ctx.GetSessionVars().StmtCtx.MaybeOverOptimized4PlanCache = true
RemoveMutableConst(ctx, args)
} else {
return args
}
}
// int non-constant [cmp] non-int constant
if arg0IsInt && !arg0IsCon && !arg1IsInt && arg1IsCon {
arg1, isExceptional = RefineComparedConstant(ctx, *arg0Type, arg1, c.op)
Expand Down
4 changes: 4 additions & 0 deletions expression/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6267,6 +6267,10 @@ func (s *testIntegrationSerialSuite) TestCacheRefineArgs(c *C) {
tk.MustExec("set @p0='0'")
tk.MustQuery("execute stmt using @p0").Check(testkit.Rows("1"))

tk.MustExec("prepare stmt from 'SELECT UCASE(?) < col_int from t;';")
tk.MustExec("set @a1 = 'xayh7vrWVNqZtzlJmdJQUwAHnkI8Ec';")
tk.MustQuery("execute stmt using @a1;").Check(testkit.Rows("<nil>"))

tk.MustExec("delete from t")
tk.MustExec("insert into t values(1)")
tk.MustExec("prepare stmt from 'SELECT col_int < ? FROM t'")
Expand Down
43 changes: 43 additions & 0 deletions expression/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -879,12 +879,42 @@ func ContainCorrelatedColumn(exprs []Expression) bool {
return false
}

<<<<<<< HEAD
// ContainMutableConst checks if the expressions contain a lazy constant.
func ContainMutableConst(ctx sessionctx.Context, exprs []Expression) bool {
// Treat all constants immutable if plan cache is not enabled for this query.
if !ctx.GetSessionVars().StmtCtx.UseCache {
return false
}
=======
// MaybeOverOptimized4PlanCache used to check whether an optimization can work
// for the statement when we enable the plan cache.
// In some situations, some optimizations maybe over-optimize and cache an
// overOptimized plan. The cached plan may not get the correct result when we
// reuse the plan for other statements.
// For example, `pk>=$a and pk<=$b` can be optimized to a PointGet when
// `$a==$b`, but it will cause wrong results when `$a!=$b`.
// So we need to do the check here. The check includes the following aspects:
// 1. Whether the plan cache switch is enable.
// 2. Whether the statement can be cached.
// 3. Whether the expressions contain a lazy constant.
// TODO: Do more careful check here.
func MaybeOverOptimized4PlanCache(ctx sessionctx.Context, exprs []Expression) bool {
// If we do not enable plan cache, all the optimization can work correctly.
if !ctx.GetSessionVars().StmtCtx.UseCache {
return false
}
if ctx.GetSessionVars().StmtCtx.MaybeOverOptimized4PlanCache {
// If the current statement can not be cached. We should remove the mutable constant.
RemoveMutableConst(ctx, exprs)
return false
}
return containMutableConst(ctx, exprs)
}

// containMutableConst checks if the expressions contain a lazy constant.
func containMutableConst(ctx sessionctx.Context, exprs []Expression) bool {
>>>>>>> 83e559db0... planner: allow refineArgs for plan cache in some situations (#28944)
for _, expr := range exprs {
switch v := expr.(type) {
case *Constant:
Expand All @@ -900,6 +930,19 @@ func ContainMutableConst(ctx sessionctx.Context, exprs []Expression) bool {
return false
}

// RemoveMutableConst used to remove the `ParamMarker` and `DeferredExpr` in the `Constant` expr.
func RemoveMutableConst(ctx sessionctx.Context, exprs []Expression) {
for _, expr := range exprs {
switch v := expr.(type) {
case *Constant:
v.ParamMarker = nil
v.DeferredExpr = nil
case *ScalarFunction:
RemoveMutableConst(ctx, v.GetArgs())
}
}
}

const (
_ = iota
kib = 1 << (10 * iota)
Expand Down
14 changes: 14 additions & 0 deletions planner/core/expression_rewriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -1418,11 +1418,25 @@ func (er *expressionRewriter) inToExpression(lLen int, not bool, tp *types.Field
er.ctxStackAppend(expression.NewNull(), types.EmptyName)
return
}
<<<<<<< HEAD
containMut := expression.ContainMutableConst(er.sctx, args)
if !containMut && leftEt == types.ETInt {
=======
if leftEt == types.ETInt {
>>>>>>> 83e559db0... planner: allow refineArgs for plan cache in some situations (#28944)
for i := 1; i < len(args); i++ {
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 <cmp> str constant`
// here and skip this refine operation in all other cases for safety.
er.sctx.GetSessionVars().StmtCtx.MaybeOverOptimized4PlanCache = true
expression.RemoveMutableConst(er.sctx, []expression.Expression{c})
} else {
continue
}
}
args[i], isExceptional = expression.RefineComparedConstant(er.sctx, *leftFt, c, opcode.EQ)
if isExceptional {
args[i] = c
Expand Down
135 changes: 135 additions & 0 deletions planner/core/prepare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,141 @@ func (s *testPlanSerialSuite) TestIssue28254(c *C) {
tk.MustQuery("execute stmt using @a").Check(testkit.Rows("1"))
}

<<<<<<< HEAD
=======
func (s *testPlanSerialSuite) TestIssue28867(c *C) {
defer testleak.AfterTest(c)()
store, dom, err := newStoreWithBootstrap()
c.Assert(err, IsNil)
tk := testkit.NewTestKit(c, store)
orgEnable := core.PreparedPlanCacheEnabled()
defer func() {
dom.Close()
err = store.Close()
c.Assert(err, IsNil)
core.SetPreparedPlanCache(orgEnable)
}()
core.SetPreparedPlanCache(true)

tk.Se, err = session.CreateSession4TestWithOpt(store, &session.Opt{
PreparedPlanCache: kvcache.NewSimpleLRUCache(100, 0.1, math.MaxUint64),
})

tk.MustExec("use test")
tk.MustExec("drop table if exists t1, t2")
tk.MustExec(`CREATE TABLE t1 (c_int int, c_str varchar(40), PRIMARY KEY (c_int, c_str))`)
tk.MustExec(`CREATE TABLE t2 (c_str varchar(40), PRIMARY KEY (c_str))`)
tk.MustExec(`insert into t1 values (1, '1')`)
tk.MustExec(`insert into t2 values ('1')`)

tk.MustExec(`prepare stmt from 'select /*+ INL_JOIN(t1,t2) */ * from t1 join t2 on t1.c_str <= t2.c_str where t1.c_int in (?,?)'`)
tk.MustExec(`set @a=10, @b=20`)
tk.MustQuery(`execute stmt using @a, @b`).Check(testkit.Rows())
tk.MustExec(`set @a=1, @b=2`)
tk.MustQuery(`execute stmt using @a, @b`).Check(testkit.Rows("1 1 1"))

// test case for IndexJoin + PlanCache
tk.MustExec(`drop table t1, t2`)
tk.MustExec(`create table t1 (a int, b int, c int, index idxab(a, b, c))`)
tk.MustExec(`create table t2 (a int, b int)`)

tk.MustExec(`prepare stmt from 'select /*+ INL_JOIN(t1,t2) */ * from t1, t2 where t1.a=t2.a and t1.b=?'`)
tk.MustExec(`set @a=1`)
tk.MustExec(`execute stmt using @a`)
tk.MustExec(`execute stmt using @a`)
// the index range [a, b] depends on parameters, so it cannot use plan-cache
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))

tk.MustExec(`prepare stmt from 'select /*+ INL_JOIN(t1,t2) */ * from t1, t2 where t1.a=t2.a and t1.c=?'`)
tk.MustExec(`set @a=1`)
tk.MustExec(`execute stmt using @a`)
tk.MustExec(`execute stmt using @a`)
// the index range [a] doesn't depend on parameters, so it can use plan-cache
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
}

func (s *testPlanSerialSuite) TestIssue28828(c *C) {
store, dom, err := newStoreWithBootstrap()
c.Assert(err, IsNil)
tk := testkit.NewTestKit(c, store)
defer func() {
dom.Close()
store.Close()
}()
orgEnable := core.PreparedPlanCacheEnabled()
defer func() {
core.SetPreparedPlanCache(orgEnable)
}()
core.SetPreparedPlanCache(true)
tk.MustExec("use test")
tk.MustExec("set @@tidb_enable_collect_execution_info=0;")
tk.MustExec("CREATE TABLE t (" +
"id bigint(20) NOT NULL," +
"audit_id bigint(20) NOT NULL," +
"PRIMARY KEY (id) /*T![clustered_index] CLUSTERED */," +
"KEY index_audit_id (audit_id)" +
");")
tk.MustExec("insert into t values(1,9941971237863475), (2,9941971237863476), (3, 0);")
tk.MustExec("prepare stmt from 'select * from t where audit_id=?';")
tk.MustExec("set @a='9941971237863475', @b=9941971237863475, @c='xayh7vrWVNqZtzlJmdJQUwAHnkI8Ec', @d='0.0', @e='0.1', @f = '9941971237863476';")

tk.MustQuery("execute stmt using @a;").Check(testkit.Rows("1 9941971237863475"))
tk.MustQuery("execute stmt using @b;").Check(testkit.Rows("1 9941971237863475"))
// When the type of parameters have been changed, the plan cache can not be used.
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @c;").Check(testkit.Rows("3 0"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @d;").Check(testkit.Rows("3 0"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @e;").Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @d;").Check(testkit.Rows("3 0"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @f;").Check(testkit.Rows("2 9941971237863476"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0"))
tk.MustExec("prepare stmt from 'select count(*) from t where audit_id in (?, ?, ?, ?, ?)';")
tk.MustQuery("execute stmt using @a, @b, @c, @d, @e;").Check(testkit.Rows("2"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @f, @b, @c, @d, @e;").Check(testkit.Rows("3"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0"))
}

func (s *testPlanSerialSuite) TestIssue28920(c *C) {
store, dom, err := newStoreWithBootstrap()
c.Assert(err, IsNil)
tk := testkit.NewTestKit(c, store)
orgEnable := core.PreparedPlanCacheEnabled()
defer func() {
dom.Close()
c.Assert(store.Close(), IsNil)
core.SetPreparedPlanCache(orgEnable)
}()
core.SetPreparedPlanCache(true)

tk.Se, err = session.CreateSession4TestWithOpt(store, &session.Opt{
PreparedPlanCache: kvcache.NewSimpleLRUCache(100, 0.1, math.MaxUint64),
})
c.Assert(err, IsNil)

tk.MustExec(`use test`)
tk.MustExec(`drop table if exists UK_GCOL_VIRTUAL_18928`)
tk.MustExec(`
CREATE TABLE UK_GCOL_VIRTUAL_18928 (
COL102 bigint(20) DEFAULT NULL,
COL103 bigint(20) DEFAULT NULL,
COL1 bigint(20) GENERATED ALWAYS AS (COL102 & 10) VIRTUAL,
COL2 varchar(20) DEFAULT NULL,
COL4 datetime DEFAULT NULL,
COL3 bigint(20) DEFAULT NULL,
COL5 float DEFAULT NULL,
UNIQUE KEY UK_COL1 (COL1))`)
tk.MustExec(`insert into UK_GCOL_VIRTUAL_18928(col102,col2) values("-5175976006730879891", "屘厒镇览錻碛斵大擔觏譨頙硺箄魨搝珄鋧扭趖")`)
tk.MustExec(`prepare stmt from 'SELECT * FROM UK_GCOL_VIRTUAL_18928 WHERE col1 < ? AND col2 != ?'`)
tk.MustExec(`set @a=10, @b="aa"`)
tk.MustQuery(`execute stmt using @a, @b`).Check(testkit.Rows("-5175976006730879891 <nil> 8 屘厒镇览錻碛斵大擔觏譨頙硺箄魨搝珄鋧扭趖 <nil> <nil> <nil>"))
}

>>>>>>> 83e559db0... planner: allow refineArgs for plan cache in some situations (#28944)
func (s *testPlanSerialSuite) TestIssue18066(c *C) {
defer testleak.AfterTest(c)()
store, dom, err := newStoreWithBootstrap()
Expand Down

0 comments on commit 207c896

Please sign in to comment.