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

session, planner: add a session variable "tidb_opt_force_inline_cte" #37626

Merged
merged 9 commits into from
Sep 8, 2022
Merged
Show file tree
Hide file tree
Changes from 7 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
8 changes: 8 additions & 0 deletions executor/set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,14 @@ func TestSetVar(t *testing.T) {
tk.MustQuery("select @@global.tidb_enable_foreign_key").Check(testkit.Rows("0")) // default value
tk.MustExec("set global tidb_enable_foreign_key = 1")
tk.MustQuery("select @@global.tidb_enable_foreign_key").Check(testkit.Rows("1"))

// test variable 'tidb_opt_force_inline_cte'
tk.MustQuery("select @@session.tidb_opt_force_inline_cte").Check(testkit.Rows("0")) // default value is 0
tk.MustExec("set session tidb_opt_force_inline_cte=1")
tk.MustQuery("select @@session.tidb_opt_force_inline_cte").Check(testkit.Rows("1"))
tk.MustQuery("select @@global.tidb_opt_force_inline_cte").Check(testkit.Rows("0")) // default value is 0
tk.MustExec("set global tidb_opt_force_inline_cte=1")
tk.MustQuery("select @@global.tidb_opt_force_inline_cte").Check(testkit.Rows("1"))
}

func TestGetSetNoopVars(t *testing.T) {
Expand Down
33 changes: 24 additions & 9 deletions planner/core/logical_plan_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -3841,20 +3841,35 @@ func (b *PlanBuilder) buildSelect(ctx context.Context, sel *ast.SelectStmt) (p L
b.isForUpdateRead = true
}

// Verify Merge hints in the current query, we will update parameters for those that meet the rules, and warn those that do not.
//If the current query uses Merge Hint and the query is a CTE, we update the HINT information for the current query.
//If the current query is not a CTE query (it may be a subquery within a CTE query or an external non-CTE query), we will give a warning.
//In particular, recursive CTE have separate warnings, so they are no longer called.
if hints := b.TableHints(); hints != nil && hints.MergeHints.preferMerge {
// If the session variable "tidb_opt_force_inline_cte" is true, all of CTEs will be inlined.
// Otherwise, whether CTEs are inlined depends on whether the merge() hint is declared.
if b.ctx.GetSessionVars().EnableForceInlineCTE() {
if b.buildingCTE && b.isCTE {
b.outerCTEs[len(b.outerCTEs)-1].isInline = true
}
} else if hints := b.TableHints(); hints != nil && hints.MergeHints.preferMerge {
// Verify Merge hints in the current query,
// we will update parameters for those that meet the rules, and warn those that do not.
// If the current query uses Merge Hint and the query is a CTE,
// we update the HINT information for the current query.
// If the current query is not a CTE query (it may be a subquery within a CTE query
// or an external non-CTE query), we will give a warning.
// In particular, recursive CTE have separate warnings, so they are no longer called.
elsa0520 marked this conversation as resolved.
Show resolved Hide resolved
if b.buildingCTE {
if b.isCTE {
b.outerCTEs[len(b.outerCTEs)-1].isInline = hints.MergeHints.preferMerge
} else if !b.buildingRecursivePartForCTE {
//If there has subquery which is not CTE and using `MERGE()` hint, we will show this warning;
b.ctx.GetSessionVars().StmtCtx.AppendWarning(ErrInternal.GenWithStack("Hint merge() is inapplicable. Please check whether the hint is using in the right place, you should use this hint in CTE inner query."))
b.ctx.GetSessionVars().StmtCtx.AppendWarning(
ErrInternal.GenWithStack("Hint merge() is inapplicable. " +
"Please check whether the hint is used in the right place, " +
"you should use this hint inside the CTE."))
}
} else if !b.buildingCTE && !b.isCTE {
b.ctx.GetSessionVars().StmtCtx.AppendWarning(ErrInternal.GenWithStack("Hint merge() is inapplicable. Please check whether the hint is using in the right place, you should use this hint in CTE inner query."))
b.ctx.GetSessionVars().StmtCtx.AppendWarning(
ErrInternal.GenWithStack("Hint merge() is inapplicable. " +
"Please check whether the hint is used in the right place, " +
"you should use this hint inside the CTE."))
}
}

Expand Down Expand Up @@ -4243,10 +4258,10 @@ func (b *PlanBuilder) tryBuildCTE(ctx context.Context, tn *ast.TableName, asName
lp.SetSchema(getResultCTESchema(cte.seedLP.Schema(), b.ctx.GetSessionVars()))

if cte.recurLP != nil && cte.isInline {
b.ctx.GetSessionVars().StmtCtx.AppendWarning(ErrInternal.GenWithStack("Hint merge() is inapplicable. Please check whether the CTE use recursive."))
b.ctx.GetSessionVars().StmtCtx.AppendWarning(
ErrInternal.GenWithStack("Recursive CTE can not be inlined."))
}
if cte.recurLP == nil && cte.isInline {
lp.MergeHints.preferMerge = cte.isInline
saveCte := make([]*cteInfo, len(b.outerCTEs[i:]))
copy(saveCte, b.outerCTEs[i:])
b.outerCTEs = b.outerCTEs[:i]
Expand Down
1 change: 0 additions & 1 deletion planner/core/logical_plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -1916,7 +1916,6 @@ type LogicalCTE struct {
cteAsName model.CIStr
seedStat *property.StatsInfo
isOuterMostCTE bool
MergeHints MergeHintInfo
}

// LogicalCTETable is for CTE table
Expand Down
55 changes: 55 additions & 0 deletions planner/core/physical_plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1153,6 +1153,61 @@ func TestCTEMergeHint(t *testing.T) {
}
}

func TestForceInlineCTE(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(11));")
tk.MustExec("insert into t values (1), (5), (10), (15), (20), (30), (50);")

var (
input []string
output []struct {
SQL string
Plan []string
Warning []string
}
)
planSuiteData := core.GetPlanSuiteData()
planSuiteData.LoadTestCases(t, &input, &output)

for i, ts := range input {
testdata.OnRecord(func() {
output[i].SQL = ts
})
if strings.HasPrefix(ts, "set") {
tk.MustExec(ts)
continue
}
testdata.OnRecord(func() {
output[i].SQL = ts
output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format='brief' " + ts).Rows())
})
tk.MustQuery("explain format='brief' " + ts).Check(testkit.Rows(output[i].Plan...))

comment := fmt.Sprintf("case:%v sql:%s", i, ts)
warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings()
testdata.OnRecord(func() {
if len(warnings) > 0 {
output[i].Warning = make([]string, len(warnings))
for j, warning := range warnings {
output[i].Warning[j] = warning.Err.Error()
}
}
})
if len(output[i].Warning) == 0 {
require.Len(t, warnings, 0)
} else {
require.Len(t, warnings, len(output[i].Warning), comment)
for j, warning := range warnings {
require.Equal(t, stmtctx.WarnLevelWarning, warning.Level, comment)
require.Equal(t, output[i].Warning[j], warning.Err.Error(), comment)
}
}
}
}

func TestPushdownDistinctEnable(t *testing.T) {
var (
input []string
Expand Down
21 changes: 21 additions & 0 deletions planner/core/testdata/plan_suite_in.json
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,27 @@
"with cte1 as (with cte2 as (with cte3 as (select /*+ MERGE() */ * from t2) select /*+ MERGE() */ * from cte3) select * from cte2,(select * from t1) ttt) select * from cte1,(select * from t3) ttw;"
]
},
{
"name": "TestForceInlineCTE",
"cases": [
"set tidb_opt_force_inline_cte=1; -- enable force inline CTE",
"with cte as (select * from t) select * from cte; -- inline",
"with cte as (select /*+ MERGE() */ * from t) select * from cte; -- inline",
"with cte as (select * from t) select * from cte cte1, cte cte2; -- inline CTEs is used by multi consumers",
"with cte1 as (select * from t), cte2 as (select a from cte1 group by a) select * from cte1, cte2; -- multi inline CTEs",
"with recursive cte1(c1) as (select 1 union select c1 + 1 c1 from cte1 where c1 < 100) select * from cte1; -- Recursive CTE can not be inlined",
"with cte1 as (with cte2 as (select * from t) select * from cte2) select * from cte1; -- non-recursive 'cte2' definition inside another non-recursive 'cte1'",
"with recursive cte2(c1) as (with cte1 as (select * from t) select a c1 from cte1 union select c1+1 c1 from cte2 where c1 < 100) select * from cte2; -- non-recursive 'cte1' inside recursive 'cte2'",
"with cte1 as (with recursive cte2(c1) as (select 1 union select c1 + 1 c1 from cte2 where c1 < 100) select * from cte2) select * from cte1; -- recursive 'cte2' inside non-recursive 'cte1'",
"set tidb_opt_force_inline_cte=0; -- disable force inline CTE",
"with cte as (select * from t) select * from cte; -- cannot be inlined",
"with cte as (select /*+ MERGE() */ * from t) select * from cte; -- inline, merge hint override session variable",
"with recursive cte1(c1) as (select 1 union select /*+ MERGE() */ c1 + 1 c1 from cte1 where c1 < 100) select * from cte1; -- Recursive CTE can not be inlined",
"with cte1 as (with cte2 as (select * from t) select * from cte2) select * from cte1; -- non-recursive 'cte2' definition inside another non-recursive 'cte1'",
"with recursive cte2(c1) as (with cte1 as (select * from t) select a c1 from cte1 union select c1+1 c1 from cte2 where c1 < 100) select * from cte2; -- non-recursive 'cte1' inside recursive 'cte2'",
"with cte1 as (with recursive cte2(c1) as (select 1 union select c1 + 1 c1 from cte2 where c1 < 100) select * from cte2) select * from cte1; -- recursive 'cte2' inside non-recursive 'cte1'"
]
},
{
"name": "TestPushdownDistinctEnable",
"cases": [
Expand Down
Loading