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: skip plan cache if the plan contains Shuffle operators (#41185) #41239

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions planner/core/plan_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,57 @@ func generateNewPlan(ctx context.Context, sctx sessionctx.Context, isGeneralPlan
return p, names, err
}

<<<<<<< HEAD
=======
// checkPlanCacheability checks whether this plan is cacheable and set to skip plan cache if it's uncacheable.
func checkPlanCacheability(sctx sessionctx.Context, p Plan, paramNum int, limitParamNum int) {
stmtCtx := sctx.GetSessionVars().StmtCtx
var pp PhysicalPlan
switch x := p.(type) {
case *Insert:
pp = x.SelectPlan
case *Update:
pp = x.SelectPlan
case *Delete:
pp = x.SelectPlan
case PhysicalPlan:
pp = x
default:
stmtCtx.SetSkipPlanCache(errors.Errorf("skip plan-cache: unexpected un-cacheable plan %v", p.ExplainID().String()))
return
}
if pp == nil { // simple DML statements
return
}

if useTiFlash(pp) {
stmtCtx.SetSkipPlanCache(errors.Errorf("skip plan-cache: TiFlash plan is un-cacheable"))
return
}

// We only cache the tableDual plan when the number of parameters are zero.
if containTableDual(pp) && paramNum > 0 {
stmtCtx.SetSkipPlanCache(errors.New("skip plan-cache: get a TableDual plan"))
return
}

if containShuffleOperator(pp) {
stmtCtx.SetSkipPlanCache(errors.New("skip plan-cache: get a Shuffle plan"))
return
}

if accessMVIndexWithIndexMerge(pp) {
stmtCtx.SetSkipPlanCache(errors.New("skip plan-cache: the plan with IndexMerge accessing Multi-Valued Index is un-cacheable"))
return
}

// before cache the param limit plan, check switch
if limitParamNum != 0 && !sctx.GetSessionVars().EnablePlanCacheForParamLimit {
stmtCtx.SetSkipPlanCache(errors.New("skip plan-cache: the switch 'tidb_enable_plan_cache_for_param_limit' is off"))
}
}

>>>>>>> 71b7dc0cd3 (planner: skip plan cache if the plan contains Shuffle operators (#41185))
// RebuildPlan4CachedPlan will rebuild this plan under current user parameters.
func RebuildPlan4CachedPlan(p Plan) error {
sc := p.SCtx().GetSessionVars().StmtCtx
Expand Down Expand Up @@ -681,6 +732,54 @@ func containTableDual(p Plan) bool {
return childContainTableDual
}

<<<<<<< HEAD
=======
func containShuffleOperator(p PhysicalPlan) bool {
if _, isShuffle := p.(*PhysicalShuffle); isShuffle {
return true
}
if _, isShuffleRecv := p.(*PhysicalShuffleReceiverStub); isShuffleRecv {
return true
}
return false
}

func accessMVIndexWithIndexMerge(p PhysicalPlan) bool {
if idxMerge, ok := p.(*PhysicalIndexMergeReader); ok {
if idxMerge.AccessMVIndex {
return true
}
}

for _, c := range p.Children() {
if accessMVIndexWithIndexMerge(c) {
return true
}
}
return false
}

// useTiFlash used to check whether the plan use the TiFlash engine.
func useTiFlash(p PhysicalPlan) bool {
switch x := p.(type) {
case *PhysicalTableReader:
switch x.StoreType {
case kv.TiFlash:
return true
default:
return false
}
default:
if len(p.Children()) > 0 {
for _, plan := range p.Children() {
return useTiFlash(plan)
}
}
}
return false
}

>>>>>>> 71b7dc0cd3 (planner: skip plan cache if the plan contains Shuffle operators (#41185))
// GetBindSQL4PlanCache used to get the bindSQL for plan cache to build the plan cache key.
func GetBindSQL4PlanCache(sctx sessionctx.Context, stmt *PlanCacheStmt) (string, bool) {
useBinding := sctx.GetSessionVars().UsePlanBaselines
Expand Down
175 changes: 175 additions & 0 deletions planner/core/plan_cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,3 +231,178 @@ 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 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"))
}

func TestIssue38335(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec(`CREATE TABLE PK_LP9463 (
COL1 mediumint NOT NULL DEFAULT '77' COMMENT 'NUMERIC PK',
COL2 varchar(20) COLLATE utf8mb4_bin DEFAULT NULL,
COL4 datetime DEFAULT NULL,
COL3 bigint DEFAULT NULL,
COL5 float DEFAULT NULL,
PRIMARY KEY (COL1))`)
tk.MustExec(`
INSERT INTO PK_LP9463 VALUES (-7415279,'笚綷想摻癫梒偆荈湩窐曋繾鏫蘌憬稁渣½隨苆','1001-11-02 05:11:33',-3745331437675076296,-3.21618e38),
(-7153863,'鯷氤衡椻闍饑堀鱟垩啵緬氂哨笂序鉲秼摀巽茊','6800-06-20 23:39:12',-7871155140266310321,-3.04829e38),
(77,'娥藨潰眤徕菗柢礥蕶浠嶲憅榩椻鍙鑜堋ᛀ暵氎','4473-09-13 01:18:59',4076508026242316746,-1.9525e38),
(16614,'阖旕雐盬皪豧篣哙舄糗悄蟊鯴瞶珧赺潴嶽簤彉','2745-12-29 00:29:06',-4242415439257105874,2.71063e37)`)
tk.MustExec(`prepare stmt from 'SELECT *, rank() OVER (PARTITION BY col2 ORDER BY COL1) FROM PK_LP9463 WHERE col1 != ? AND col1 < ?'`)
tk.MustExec(`set @a=-8414766051197, @b=-8388608`)
tk.MustExec(`execute stmt using @a,@b`)
tk.MustExec(`set @a=16614, @b=16614`)
rows := tk.MustQuery(`execute stmt using @a,@b`).Sort()
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0"))
tk.MustQuery(`SELECT *, rank() OVER (PARTITION BY col2 ORDER BY COL1) FROM PK_LP9463 WHERE col1 != 16614 and col1 < 16614`).Sort().Check(rows.Rows())
}

func TestIssue41032(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec(`CREATE TABLE PK_SIGNED_10087 (
COL1 mediumint(8) unsigned NOT NULL,
COL2 varchar(20) DEFAULT NULL,
COL4 datetime DEFAULT NULL,
COL3 bigint(20) DEFAULT NULL,
COL5 float DEFAULT NULL,
PRIMARY KEY (COL1) )`)
tk.MustExec(`insert into PK_SIGNED_10087 values(0, "痥腜蟿鮤枓欜喧檕澙姭袐裄钭僇剕焍哓閲疁櫘", "0017-11-14 05:40:55", -4504684261333179273, 7.97449e37)`)
tk.MustExec(`prepare stmt from 'SELECT/*+ HASH_JOIN(t1, t2) */ t2.* FROM PK_SIGNED_10087 t1 JOIN PK_SIGNED_10087 t2 ON t1.col1 = t2.col1 WHERE t2.col1 >= ? AND t1.col1 >= ?;'`)
tk.MustExec(`set @a=0, @b=0`)
tk.MustQuery(`execute stmt using @a,@b`).Check(testkit.Rows("0 痥腜蟿鮤枓欜喧檕澙姭袐裄钭僇剕焍哓閲疁櫘 0017-11-14 05:40:55 -4504684261333179273 79744900000000000000000000000000000000"))
tk.MustExec(`set @a=8950167, @b=16305982`)
tk.MustQuery(`execute stmt using @a,@b`).Check(testkit.Rows())
tk.MustQuery(`select @@last_plan_from_cache`).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 1, ?'", []int{1}},
{"prepare stmt from 'select * from t limit ?, 1'", []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 < 9 {
// none point get plan
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"))
}
>>>>>>> 71b7dc0cd3 (planner: skip plan cache if the plan contains Shuffle operators (#41185))