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: check the ignore-plan-cache hint in insert-stmt (#40080) #40693

Closed
wants to merge 14 commits into from
2 changes: 1 addition & 1 deletion executor/explainfor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -949,7 +949,7 @@ func TestIndexMerge4PlanCache(t *testing.T) {
tk.MustExec("set @a=9, @b=10, @c=11;")
tk.MustQuery("execute stmt using @a, @a;").Check(testkit.Rows("10 10 10"))
tk.MustQuery("execute stmt using @a, @c;").Check(testkit.Rows("10 10 10", "11 11 11"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @c, @a;").Check(testkit.Rows("10 10 10"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1"))

Expand Down
10 changes: 5 additions & 5 deletions executor/seqtest/prepared_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -495,14 +495,14 @@ func TestPreparedInsert(t *testing.T) {
err = counter.Write(pb)
require.NoError(t, err)
hit := pb.GetCounter().GetValue()
require.Equal(t, float64(1), hit)
require.Equal(t, float64(0), hit)
}
tk.MustExec(`set @a=3,@b=3; execute stmt_insert using @a, @b;`)
if flag {
err = counter.Write(pb)
require.NoError(t, err)
hit := pb.GetCounter().GetValue()
require.Equal(t, float64(2), hit)
require.Equal(t, float64(0), hit)
}

result := tk.MustQuery("select id, c1 from prepare_test where id = ?", 1)
Expand All @@ -518,21 +518,21 @@ func TestPreparedInsert(t *testing.T) {
err = counter.Write(pb)
require.NoError(t, err)
hit := pb.GetCounter().GetValue()
require.Equal(t, float64(2), hit)
require.Equal(t, float64(0), hit)
}
tk.MustExec(`set @a=2; execute stmt_insert_select using @a;`)
if flag {
err = counter.Write(pb)
require.NoError(t, err)
hit := pb.GetCounter().GetValue()
require.Equal(t, float64(3), hit)
require.Equal(t, float64(1), hit)
}
tk.MustExec(`set @a=3; execute stmt_insert_select using @a;`)
if flag {
err = counter.Write(pb)
require.NoError(t, err)
hit := pb.GetCounter().GetValue()
require.Equal(t, float64(4), hit)
require.Equal(t, float64(2), hit)
}

result = tk.MustQuery("select id, c1 from prepare_test where id = ?", 101)
Expand Down
13 changes: 13 additions & 0 deletions planner/core/cacheable_checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,19 @@ func (checker *cacheableChecker) Enter(in ast.Node) (out ast.Node, skipChildren
return in, true
}
}
case *ast.InsertStmt:
if node.Select == nil {
// do not cache insert-values-stmt like 'insert into t values (...)' since
// no performance benefit and to save memory.
checker.cacheable = false
return in, true
}
for _, hints := range node.TableHints {
if hints.HintName.L == HintIgnorePlanCache {
checker.cacheable = false
return in, true
}
}
case *ast.VariableExpr, *ast.ExistsSubqueryExpr, *ast.SubqueryExpr:
checker.cacheable = false
return in, true
Expand Down
147 changes: 146 additions & 1 deletion planner/core/cacheable_checker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package core_test

import (
"fmt"
"testing"

"github.com/pingcap/tidb/expression"
Expand All @@ -24,9 +25,151 @@ import (
"github.com/pingcap/tidb/planner/core"
"github.com/pingcap/tidb/testkit"
driver "github.com/pingcap/tidb/types/parser_driver"
"github.com/pingcap/tidb/util"
"github.com/stretchr/testify/require"
)

func TestIssue40224(t *testing.T) {
store, clean := testkit.CreateMockStore(t)
defer clean()
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(&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 TestInvalidRange(t *testing.T) {
store, clean := testkit.CreateMockStore(t)
defer clean()
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>? and a<?'")
tk.MustExec("set @l=100, @r=10")
tk.MustExec("execute st using @l, @r")

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{}{{"TableDual_5"}}) // use TableDual directly instead of TableFullScan

tk.MustExec("execute st using @l, @r")
tk.MustExec("execute st using @l, @r")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
}

func TestIssue40093(t *testing.T) {
store, clean := testkit.CreateMockStore(t)
defer clean()
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("create table t1 (a int, b int)")
tk.MustExec("create table t2 (a int, b int, key(b, a))")
tk.MustExec("prepare st from 'select * from t1 left join t2 on t1.a=t2.a where t2.b in (?)'")
tk.MustExec("set @b=1")
tk.MustExec("execute st using @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{}{
{"Projection_9"},
{"└─HashJoin_21"},
{" ├─IndexReader_26(Build)"},
{" │ └─IndexRangeScan_25"}, // RangeScan instead of FullScan
{" └─TableReader_24(Probe)"},
{" └─Selection_23"},
{" └─TableFullScan_22"},
})

tk.MustExec("execute st using @b")
tk.MustExec("execute st using @b")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
}

func TestIssue38205(t *testing.T) {
store, clean := testkit.CreateMockStore(t)
defer clean()
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("CREATE TABLE `item` (`id` int, `vid` varbinary(16), `sid` int)")
tk.MustExec("CREATE TABLE `lv` (`item_id` int, `sid` int, KEY (`sid`,`item_id`))")

tk.MustExec("prepare stmt from 'SELECT /*+ TIDB_INLJ(lv, item) */ * FROM lv LEFT JOIN item ON lv.sid = item.sid AND lv.item_id = item.id WHERE item.sid = ? AND item.vid IN (?, ?)'")
tk.MustExec("set @a=1, @b='1', @c='3'")
tk.MustExec("execute stmt using @a, @b, @c")

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{}{
{"IndexJoin_10"},
{"├─TableReader_19(Build)"},
{"│ └─Selection_18"},
{"│ └─TableFullScan_17"}, // RangeScan instead of FullScan
{"└─IndexReader_9(Probe)"},
{" └─Selection_8"},
{" └─IndexRangeScan_7"},
})

tk.MustExec("execute stmt using @a, @b, @c")
tk.MustExec("execute stmt using @a, @b, @c")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
}

func TestIgnoreInsertStmt(t *testing.T) {
store, clean := testkit.CreateMockStore(t)
defer clean()
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("create table t (a int)")

// do not cache native insert-stmt
tk.MustExec("prepare st from 'insert into t values (1)'")
tk.MustExec("execute st")
tk.MustExec("execute st")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))

// ignore-hint in insert-stmt can work
tk.MustExec("prepare st from 'insert into t select * from t'")
tk.MustExec("execute st")
tk.MustExec("execute st")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustExec("prepare st from 'insert /*+ ignore_plan_cache() */ into t select * from t'")
tk.MustExec("execute st")
tk.MustExec("execute st")
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
}

func TestCacheable(t *testing.T) {
store, clean := testkit.CreateMockStore(t)
defer clean()
Expand All @@ -52,7 +195,9 @@ func TestCacheable(t *testing.T) {

tableRefsClause := &ast.TableRefsClause{TableRefs: &ast.Join{Left: &ast.TableSource{Source: tbl}}}
// test InsertStmt
stmt = &ast.InsertStmt{Table: tableRefsClause}
stmt = &ast.InsertStmt{Table: tableRefsClause} // insert-values-stmt
require.False(t, core.Cacheable(stmt, is))
stmt = &ast.InsertStmt{Table: tableRefsClause, Select: &ast.SelectStmt{}} // insert-select-stmt
require.True(t, core.Cacheable(stmt, is))

// test DeleteStmt
Expand Down
11 changes: 4 additions & 7 deletions planner/core/expression_rewriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -1496,14 +1496,11 @@ 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 <cmp> 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
}
er.sctx.GetSessionVars().StmtCtx.SkipPlanCache = true
expression.RemoveMutableConst(er.sctx, args)
} 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})
Expand Down
14 changes: 7 additions & 7 deletions planner/core/prepare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1583,7 +1583,7 @@ func TestIssue29303(t *testing.T) {
tk.MustQuery(`execute stmt using @a,@b,@c,@d`).Check(testkit.Rows())
tk.MustExec(`set @a="龂", @b="龂", @c="龂", @d="龂"`)
tk.MustQuery(`execute stmt using @a,@b,@c,@d`).Check(testkit.Rows("� 龂 � 龂"))
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0"))
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1"))
}

func TestIssue34725(t *testing.T) {
Expand Down Expand Up @@ -2222,7 +2222,7 @@ func TestPlanCachePointGetAndTableDual(t *testing.T) {
tk.MustQuery("execute s1 using @a1, @b1, @b1").Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustQuery("execute s1 using @a1, @a1, @b1").Check(testkit.Rows("0000 7777 1"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))

tk.MustExec("create table t2(c1 bigint(20) primary key, c2 varchar(20))")
tk.MustExec("insert into t2 values(1,'7777')")
Expand All @@ -2242,13 +2242,13 @@ func TestPlanCachePointGetAndTableDual(t *testing.T) {
tk.MustQuery("execute s3 using @a3,@a3").Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustQuery("execute s3 using @a3,@b3").Check(testkit.Rows("2 1 1"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))

tk.MustExec("prepare s3 from 'select /*+ use_index_merge(t3) */ * from t3 where (c1 >= ? and c1 <= ?) or c2 > 1'")
tk.MustExec("set @a3=1,@b3=3")
// TableReader plan would be built, we should cache it.
tk.MustQuery("execute s3 using @b3,@a3").Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustQuery("execute s3 using @a3,@b3").Check(testkit.Rows("2 1 1"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))

Expand All @@ -2259,7 +2259,7 @@ func TestPlanCachePointGetAndTableDual(t *testing.T) {
tk.MustQuery("execute s4 using @a4,@a4").Check(testkit.Rows())
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
tk.MustQuery("execute s4 using @a4,@b4").Check(testkit.Rows("2 1 1"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))

tk.MustExec("prepare s4 from 'select /*+ use_index_merge(t4) */ * from t4 where (c1 >= ? and c1 <= ?) or c2 > 1'")
tk.MustExec("set @a4=1,@b4=3")
Expand Down Expand Up @@ -2337,7 +2337,7 @@ func TestIssue23671(t *testing.T) {
tk.MustQuery("execute s1 using @a, @b, @c").Check(testkit.Rows("1 1"))
tk.MustExec("set @a=1, @b=1, @c=10")
tk.MustQuery("execute s1 using @a, @b, @c").Check(testkit.Rows("1 1", "2 2"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
}

func TestIssue29296(t *testing.T) {
Expand Down Expand Up @@ -2842,7 +2842,7 @@ func TestCachedTable(t *testing.T) {
tk.MustExec("prepare indexScan from 'select b from t use index(i_b) where b>?'")
tk.MustExec("prepare indexLookup from 'select a from t use index(i_b) where b>? and b<?'")
tk.MustExec("prepare pointGet from 'select b from t use index(i_b) where b=?'")
tk.MustExec("set @a=1, @b=3")
tk.MustExec("set @a=1, @b=10")

lastReadFromCache := func(tk *testkit.TestKit) bool {
return tk.Session().GetSessionVars().StmtCtx.ReadFromTableCache
Expand Down
3 changes: 1 addition & 2 deletions util/ranger/detacher.go
Original file line number Diff line number Diff line change
Expand Up @@ -578,8 +578,7 @@ func ExtractEqAndInCondition(sctx sessionctx.Context, conditions []expression.Ex
columnValues[i] = &valueInfo{mutable: true}
}
if expression.MaybeOverOptimized4PlanCache(sctx, conditions) {
// TODO: optimize it more elaborately, e.g. return [2 3, 2 3] as accesses for 'where a = 2 and b = 3 and c >= ? and c <= ?'
return nil, conditions, nil, nil, false
sctx.GetSessionVars().StmtCtx.SkipPlanCache = true
}
}
}
Expand Down