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: plan cache support caching plan with subquery #41080

Merged
merged 19 commits into from
Feb 20, 2023
Merged
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
1 change: 1 addition & 0 deletions executor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -1519,6 +1519,7 @@ func init() {
plannercore.EvalSubqueryFirstRow = func(ctx context.Context, p plannercore.PhysicalPlan, is infoschema.InfoSchema, sctx sessionctx.Context) ([]types.Datum, error) {
defer func(begin time.Time) {
s := sctx.GetSessionVars()
s.StmtCtx.SetSkipPlanCache(errors.New("skip plan-cache: query has uncorrelated sub-queries is un-cacheable"))
s.RewritePhaseInfo.PreprocessSubQueries++
s.RewritePhaseInfo.DurationPreprocessSubQuery += time.Since(begin)
}(time.Now())
Expand Down
12 changes: 7 additions & 5 deletions executor/explainfor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1251,9 +1251,9 @@ func TestCTE4PlanCache(t *testing.T) {
tk.MustExec("set @a=5, @b=4, @c=2, @d=1;")
tk.MustQuery("execute stmt using @d, @a").Check(testkit.Rows("1", "2", "3", "4", "5"))
tk.MustQuery("execute stmt using @d, @b").Check(testkit.Rows("1", "2", "3", "4"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1"))
tk.MustQuery("execute stmt using @c, @b").Check(testkit.Rows("2", "3", "4"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1"))

// Two seed parts.
tk.MustExec("prepare stmt from 'with recursive cte1 as (" +
Expand All @@ -1266,7 +1266,7 @@ func TestCTE4PlanCache(t *testing.T) {
tk.MustExec("set @a=10, @b=2;")
tk.MustQuery("execute stmt using @a").Check(testkit.Rows("1", "2", "2", "3", "3", "4", "4", "5", "5", "6", "6", "7", "7", "8", "8", "9", "9", "10", "10"))
tk.MustQuery("execute stmt using @b").Check(testkit.Rows("1", "2", "2"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1"))

// Two recursive parts.
tk.MustExec("prepare stmt from 'with recursive cte1 as (" +
Expand All @@ -1281,7 +1281,7 @@ func TestCTE4PlanCache(t *testing.T) {
tk.MustExec("set @a=1, @b=2, @c=3, @d=4, @e=5;")
tk.MustQuery("execute stmt using @c, @b, @e;").Check(testkit.Rows("1", "2", "2", "3", "3", "3", "4", "4", "5", "5", "5", "6", "6"))
tk.MustQuery("execute stmt using @b, @a, @d;").Check(testkit.Rows("1", "2", "2", "2", "3", "3", "3", "4", "4", "4"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1"))

tk.MustExec("drop table if exists t1;")
tk.MustExec("create table t1(a int);")
Expand All @@ -1293,11 +1293,13 @@ func TestCTE4PlanCache(t *testing.T) {
tk.MustQuery("execute stmt using @f, @a, @f").Check(testkit.Rows("1"))
tk.MustQuery("execute stmt using @a, @b, @a").Sort().Check(testkit.Rows("1", "2"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0"))
tk.MustQuery("execute stmt using @a, @b, @a").Sort().Check(testkit.Rows("1", "2"))
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 skip plan-cache: PhysicalApply plan is un-cacheable"))

tk.MustExec("prepare stmt from 'with recursive c(p) as (select ?), cte(a, b) as (select 1, 1 union select a+?, 1 from cte, c where a < ?) select * from cte order by 1, 2;';")
tk.MustQuery("execute stmt using @a, @a, @e;").Check(testkit.Rows("1 1", "2 1", "3 1", "4 1", "5 1"))
tk.MustQuery("execute stmt using @b, @b, @c;").Check(testkit.Rows("1 1", "3 1"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0"))
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1"))
}

func TestValidity4PlanCache(t *testing.T) {
Expand Down
4 changes: 3 additions & 1 deletion executor/prepared_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1114,8 +1114,10 @@ func TestPreparePlanCache4DifferentSystemVars(t *testing.T) {
// Do not use the parallel apply.
require.False(t, strings.Contains(executionInfo, "Concurrency"))
tk.MustExec("execute stmt;")
// The subquery plan can not be cached.
// The subquery plan with PhysicalApply can't be cached.
tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0"))
tk.MustExec("execute stmt;")
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 skip plan-cache: PhysicalApply plan is un-cacheable"))

// test for apply cache
tk.MustExec("set @@tidb_enable_collect_execution_info=1;")
Expand Down
27 changes: 27 additions & 0 deletions executor/seqtest/prepared_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -872,3 +872,30 @@ func TestPlanCacheLimitSwitchEffective(t *testing.T) {
checkIfCached("0")
tk.MustExec("deallocate prepare stmt")
}

func TestSetPlanCacheSubquerySwitch(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")

tk.MustQuery("select @@session.tidb_enable_plan_cache_for_subquery").Check(testkit.Rows("1"))
tk.MustQuery("select @@global.tidb_enable_plan_cache_for_subquery").Check(testkit.Rows("1"))

tk.MustExec("set @@session.tidb_enable_plan_cache_for_subquery = OFF;")
tk.MustQuery("select @@session.tidb_enable_plan_cache_for_subquery").Check(testkit.Rows("0"))

tk.MustExec("set @@session.tidb_enable_plan_cache_for_subquery = 1;")
tk.MustQuery("select @@session.tidb_enable_plan_cache_for_subquery").Check(testkit.Rows("1"))

tk.MustExec("set @@global.tidb_enable_plan_cache_for_subquery = off;")
tk.MustQuery("select @@global.tidb_enable_plan_cache_for_subquery").Check(testkit.Rows("0"))

tk.MustExec("set @@global.tidb_enable_plan_cache_for_subquery = ON;")
tk.MustQuery("select @@global.tidb_enable_plan_cache_for_subquery").Check(testkit.Rows("1"))

tk.MustGetErrMsg("set @@global.tidb_enable_plan_cache_for_subquery = '';", "[variable:1231]Variable 'tidb_enable_plan_cache_for_subquery' can't be set to the value of ''")
tk.MustGetErrMsg("set @@global.tidb_enable_plan_cache_for_subquery = 11;", "[variable:1231]Variable 'tidb_enable_plan_cache_for_subquery' can't be set to the value of '11'")
tk.MustGetErrMsg("set @@global.tidb_enable_plan_cache_for_subquery = enabled;", "[variable:1231]Variable 'tidb_enable_plan_cache_for_subquery' can't be set to the value of 'enabled'")
tk.MustGetErrMsg("set @@global.tidb_enable_plan_cache_for_subquery = disabled;", "[variable:1231]Variable 'tidb_enable_plan_cache_for_subquery' can't be set to the value of 'disabled'")
tk.MustGetErrMsg("set @@global.tidb_enable_plan_cache_for_subquery = open;", "[variable:1231]Variable 'tidb_enable_plan_cache_for_subquery' can't be set to the value of 'open'")
}
61 changes: 58 additions & 3 deletions planner/core/plan_cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -534,9 +534,6 @@ func TestPlanCacheDiagInfo(t *testing.T) {
tk.MustExec("use test")
tk.MustExec("create table t (a int, b int, key(a), key(b))")

tk.MustExec("prepare stmt from 'select * from t where a in (select a from t)'")
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 skip plan-cache: query has sub-queries is un-cacheable"))

tk.MustExec("prepare stmt from 'select /*+ ignore_plan_cache() */ * from t'")
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 skip plan-cache: ignore plan cache by hint"))

Expand Down Expand Up @@ -725,3 +722,61 @@ func TestPlanCacheWithLimit(t *testing.T) {
tk.MustExec("execute stmt using @a")
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 skip plan-cache: limit count more than 10000"))
}

func TestPlanCacheWithSubquery(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, b int)")

testCases := []struct {
sql string
params []int
cacheAble string
isDecorrelated bool
}{
{"select * from t t1 where exists (select 1 from t t2 where t2.b < t1.b and t2.b < ?)", []int{1}, "1", false}, // exist
{"select * from t t1 where t1.a in (select a from t t2 where t2.b < ?)", []int{1}, "1", false}, // in
{"select * from t t1 where t1.a > (select max(a) from t t2 where t2.b < t1.b and t2.b < ?)", []int{1}, "0", false}, // scala
{"select * from t t1 where t1.a > (select 1 from t t2 where t2.b<?)", []int{1}, "0", true}, // uncorrelated
}

// switch on
for _, testCase := range testCases {
tk.MustExec(fmt.Sprintf("prepare stmt from '%s'", 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(testCase.cacheAble))
if testCase.cacheAble == "0" {
tk.MustExec("execute stmt using " + strings.Join(using, ", "))
if testCase.isDecorrelated {
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 skip plan-cache: query has uncorrelated sub-queries is un-cacheable"))
} else {
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 skip plan-cache: PhysicalApply plan is un-cacheable"))
}
}
}
// switch off
tk.MustExec("set @@session.tidb_enable_plan_cache_for_subquery = 0")
for _, testCase := range testCases {
tk.MustExec(fmt.Sprintf("prepare stmt from '%s'", testCase.sql))
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 skip plan-cache: query has sub-queries is un-cacheable"))
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("0"))
tk.MustQuery("show warnings").Check(testkit.Rows())
}
}
14 changes: 12 additions & 2 deletions planner/core/plan_cacheable_checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,18 @@ func (checker *cacheableChecker) Enter(in ast.Node) (out ast.Node, skipChildren
return in, true
}
}
case *ast.VariableExpr, *ast.ExistsSubqueryExpr, *ast.SubqueryExpr:

case *ast.VariableExpr:
checker.cacheable = false
checker.reason = "query has sub-queries is un-cacheable"
checker.reason = "query has user-defined variables is un-cacheable"
return in, true
case *ast.ExistsSubqueryExpr, *ast.SubqueryExpr:
if !checker.sctx.GetSessionVars().EnablePlanCacheForSubquery {
checker.cacheable = false
checker.reason = "query has sub-queries is un-cacheable"
return in, true
}
return in, false
case *ast.FuncCallExpr:
if _, found := expression.UnCacheableFunctions[node.FnName.L]; found {
checker.cacheable = false
Expand Down Expand Up @@ -358,6 +366,8 @@ func isPhysicalPlanCacheable(sctx sessionctx.Context, p PhysicalPlan, paramNum,
if x.AccessMVIndex {
return false, "skip plan-cache: the plan with IndexMerge accessing Multi-Valued Index is un-cacheable"
}
case *PhysicalApply:
return false, "skip plan-cache: PhysicalApply plan is un-cacheable"
}

for _, c := range p.Children() {
Expand Down
18 changes: 11 additions & 7 deletions planner/core/plan_cacheable_checker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func TestCacheable(t *testing.T) {
store := testkit.CreateMockStore(t)
mockCtx := mock.NewContext()
mockCtx.GetSessionVars().EnablePlanCacheForParamLimit = true
mockCtx.GetSessionVars().EnablePlanCacheForSubquery = true

tk := testkit.NewTestKit(t, store)

Expand Down Expand Up @@ -79,9 +80,10 @@ func TestCacheable(t *testing.T) {

stmt = &ast.DeleteStmt{
TableRefs: tableRefsClause,
Where: &ast.ExistsSubqueryExpr{},
Where: &ast.ExistsSubqueryExpr{Sel: &ast.SubqueryExpr{Query: &ast.SelectStmt{}}},
}
require.False(t, core.Cacheable(stmt, is))
c, _ := core.CacheableWithCtx(mockCtx, stmt, is)
require.True(t, c)

limitStmt := &ast.Limit{
Count: &driver.ParamMarkerExpr{},
Expand All @@ -90,7 +92,7 @@ func TestCacheable(t *testing.T) {
TableRefs: tableRefsClause,
Limit: limitStmt,
}
c, _ := core.CacheableWithCtx(mockCtx, stmt, is)
c, _ = core.CacheableWithCtx(mockCtx, stmt, is)
require.True(t, c)

limitStmt = &ast.Limit{
Expand Down Expand Up @@ -134,9 +136,10 @@ func TestCacheable(t *testing.T) {

stmt = &ast.UpdateStmt{
TableRefs: tableRefsClause,
Where: &ast.ExistsSubqueryExpr{},
Where: &ast.ExistsSubqueryExpr{Sel: &ast.SubqueryExpr{Query: &ast.SelectStmt{}}},
}
require.False(t, core.Cacheable(stmt, is))
c, _ = core.CacheableWithCtx(mockCtx, stmt, is)
require.True(t, c)

limitStmt = &ast.Limit{
Count: &driver.ParamMarkerExpr{},
Expand Down Expand Up @@ -187,9 +190,10 @@ func TestCacheable(t *testing.T) {
require.True(t, core.Cacheable(stmt, is))

stmt = &ast.SelectStmt{
Where: &ast.ExistsSubqueryExpr{},
Where: &ast.ExistsSubqueryExpr{Sel: &ast.SubqueryExpr{Query: &ast.SelectStmt{}}},
}
require.False(t, core.Cacheable(stmt, is))
c, _ = core.CacheableWithCtx(mockCtx, stmt, is)
require.True(t, c)

limitStmt = &ast.Limit{
Count: &driver.ParamMarkerExpr{},
Expand Down
3 changes: 3 additions & 0 deletions sessionctx/variable/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -1276,6 +1276,9 @@ type SessionVars struct {
// EnablePlanCacheForParamLimit controls whether the prepare statement with parameterized limit can be cached
EnablePlanCacheForParamLimit bool

// EnablePlanCacheForSubquery controls whether the prepare statement with sub query can be cached
EnablePlanCacheForSubquery bool

// EnableNonPreparedPlanCache indicates whether to enable non-prepared plan cache.
EnableNonPreparedPlanCache bool

Expand Down
4 changes: 4 additions & 0 deletions sessionctx/variable/sysvar.go
Original file line number Diff line number Diff line change
Expand Up @@ -2368,6 +2368,10 @@ var defaultSysVars = []*SysVar{
return BoolToOnOff(s.EnableINLJoinInnerMultiPattern), nil
},
},
{Scope: ScopeGlobal | ScopeSession, Name: TiDBEnablePlanCacheForSubquery, Value: BoolToOnOff(DefTiDBEnablePlanCacheForSubquery), Type: TypeBool, SetSession: func(s *SessionVars, val string) error {
s.EnablePlanCacheForSubquery = TiDBOptOn(val)
return nil
}},
}

// FeedbackProbability points to the FeedbackProbability in statistics package.
Expand Down
4 changes: 4 additions & 0 deletions sessionctx/variable/tidb_vars.go
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,9 @@ const (

// TiDBEnableINLJoinInnerMultiPattern indicates whether enable multi pattern for inner side of inl join
TiDBEnableINLJoinInnerMultiPattern = "tidb_enable_inl_join_inner_multi_pattern"

// TiDBEnablePlanCacheForSubquery controls whether prepare statement with subquery can be cached
TiDBEnablePlanCacheForSubquery = "tidb_enable_plan_cache_for_subquery"
)

// TiDB vars that have only global scope
Expand Down Expand Up @@ -1192,6 +1195,7 @@ const (
DefTiDBEnableResourceControl = false
DefTiDBPessimisticTransactionAggressiveLocking = false
DefTiDBEnablePlanCacheForParamLimit = true
DefTiDBEnablePlanCacheForSubquery = true
)

// Process global variables.
Expand Down