Skip to content

Commit

Permalink
session, planner: add a session variable "tidb_opt_force_inline_cte" (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
elsa0520 authored Sep 8, 2022
1 parent 52c8476 commit ca5de89
Show file tree
Hide file tree
Showing 9 changed files with 312 additions and 18 deletions.
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 @@ -3842,20 +3842,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.
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 @@ -4244,10 +4259,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 @@ -1154,6 +1154,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

0 comments on commit ca5de89

Please sign in to comment.