From a57dd376405d86b09de277988b0ccb286c0dbd71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C7=94p=C3=A9ng=20Di=C4=81o?= <36835099+dayicklp@users.noreply.github.com> Date: Tue, 19 Jul 2022 14:31:07 +0800 Subject: [PATCH] planner: add a hint for CTE (#34574) close pingcap/tidb#17472 --- parser/ast/misc.go | 2 +- parser/ast/misc_test.go | 1 + parser/hintparser.go | 6 +- parser/hintparser.y | 2 +- parser/parser_test.go | 34 ++++ planner/core/logical_plan_builder.go | 50 ++++- planner/core/logical_plans.go | 1 + planner/core/physical_plan_test.go | 70 +++++++ planner/core/planbuilder.go | 8 + planner/core/testdata/plan_suite_in.json | 17 ++ planner/core/testdata/plan_suite_out.json | 231 ++++++++++++++++++++++ 11 files changed, 416 insertions(+), 6 deletions(-) diff --git a/parser/ast/misc.go b/parser/ast/misc.go index 67d353e892b19..29874bd06e7b4 100644 --- a/parser/ast/misc.go +++ b/parser/ast/misc.go @@ -3534,7 +3534,7 @@ func (n *TableOptimizerHint) Restore(ctx *format.RestoreCtx) error { } // Hints without args except query block. switch n.HintName.L { - case "hash_agg", "stream_agg", "agg_to_cop", "read_consistent_replica", "no_index_merge", "qb_name", "ignore_plan_cache", "limit_to_cop", "straight_join": + case "hash_agg", "stream_agg", "agg_to_cop", "read_consistent_replica", "no_index_merge", "qb_name", "ignore_plan_cache", "limit_to_cop", "straight_join", "merge": ctx.WritePlain(")") return nil } diff --git a/parser/ast/misc_test.go b/parser/ast/misc_test.go index b87f45c99af11..2a2b7ccb1acdc 100644 --- a/parser/ast/misc_test.go +++ b/parser/ast/misc_test.go @@ -276,6 +276,7 @@ func TestTableOptimizerHintRestore(t *testing.T) { {"AGG_TO_COP()", "AGG_TO_COP()"}, {"AGG_TO_COP(@sel_1)", "AGG_TO_COP(@`sel_1`)"}, {"LIMIT_TO_COP()", "LIMIT_TO_COP()"}, + {"MERGE()", "MERGE()"}, {"STRAIGHT_JOIN()", "STRAIGHT_JOIN()"}, {"NO_INDEX_MERGE()", "NO_INDEX_MERGE()"}, {"NO_INDEX_MERGE(@sel1)", "NO_INDEX_MERGE(@`sel1`)"}, diff --git a/parser/hintparser.go b/parser/hintparser.go index 83fdee7af18a2..548860fbec0bf 100644 --- a/parser/hintparser.go +++ b/parser/hintparser.go @@ -428,7 +428,7 @@ var ( {100, 1}, {100, 1}, {100, 1}, - {100, 1}, + {97, 1}, {97, 1}, {97, 1}, {97, 1}, @@ -546,7 +546,7 @@ var ( yyhintParseTab = [261][]uint16{ // 0 - {1: 239, 212, 204, 206, 231, 237, 218, 229, 243, 221, 214, 213, 217, 183, 201, 202, 203, 220, 240, 190, 195, 209, 222, 205, 207, 208, 224, 241, 210, 223, 225, 233, 227, 216, 191, 219, 194, 199, 242, 200, 193, 232, 245, 192, 226, 211, 244, 238, 215, 196, 235, 228, 230, 236, 234, 85: 197, 90: 184, 198, 93: 182, 189, 96: 188, 186, 181, 187, 185, 106: 180, 108: 179}, + {1: 239, 211, 204, 206, 231, 237, 218, 229, 243, 221, 214, 212, 217, 183, 201, 202, 203, 220, 240, 190, 195, 213, 222, 205, 207, 208, 224, 241, 209, 223, 225, 233, 227, 216, 191, 219, 194, 199, 242, 200, 193, 232, 245, 192, 226, 210, 244, 238, 215, 196, 235, 228, 230, 236, 234, 85: 197, 90: 184, 198, 93: 182, 189, 96: 188, 186, 181, 187, 185, 106: 180, 108: 179}, {76: 178}, {1: 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 336, 76: 177, 81: 436}, {1: 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 76: 176}, @@ -855,7 +855,7 @@ var ( {151, 70: 248, 77: 434}, {435}, {1: 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 76: 172}, - {1: 239, 212, 204, 206, 231, 237, 218, 229, 243, 221, 214, 213, 217, 183, 201, 202, 203, 220, 240, 190, 195, 209, 222, 205, 207, 208, 224, 241, 210, 223, 225, 233, 227, 216, 191, 219, 194, 199, 242, 200, 193, 232, 245, 192, 226, 211, 244, 238, 215, 196, 235, 228, 230, 236, 234, 85: 197, 90: 184, 198, 93: 438, 189, 96: 188, 186, 437, 187, 185}, + {1: 239, 211, 204, 206, 231, 237, 218, 229, 243, 221, 214, 212, 217, 183, 201, 202, 203, 220, 240, 190, 195, 213, 222, 205, 207, 208, 224, 241, 209, 223, 225, 233, 227, 216, 191, 219, 194, 199, 242, 200, 193, 232, 245, 192, 226, 210, 244, 238, 215, 196, 235, 228, 230, 236, 234, 85: 197, 90: 184, 198, 93: 438, 189, 96: 188, 186, 437, 187, 185}, {1: 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 76: 175}, // 260 {1: 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 76: 173}, diff --git a/parser/hintparser.y b/parser/hintparser.y index 76f986bce7262..3808a1fc335ae 100644 --- a/parser/hintparser.y +++ b/parser/hintparser.y @@ -531,13 +531,13 @@ UnsupportedTableLevelOptimizerHintName: | "NO_BNL" /* HASH_JOIN is supported by TiDB */ | "NO_HASH_JOIN" -| "MERGE" | "NO_MERGE" SupportedTableLevelOptimizerHintName: "MERGE_JOIN" | "BROADCAST_JOIN" | "INL_JOIN" +| "MERGE" | "INL_HASH_JOIN" | "SWAP_JOIN_INPUTS" | "NO_SWAP_JOIN_INPUTS" diff --git a/parser/parser_test.go b/parser/parser_test.go index 08b7c30595f97..7e20a19858931 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -4229,6 +4229,16 @@ func TestOptimizerHints(t *testing.T) { require.Equal(t, "limit_to_cop", hints[0].HintName.L) require.Equal(t, "limit_to_cop", hints[1].HintName.L) + // Test CTE MERGE + stmt, _, err = p.Parse("with cte(x) as (select * from t1) select /*+ MERGE(), merge() */ * from cte;", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "merge", hints[0].HintName.L) + require.Equal(t, "merge", hints[1].HintName.L) + // Test STRAIGHT_JOIN stmt, _, err = p.Parse("select /*+ STRAIGHT_JOIN(), straight_join() */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") require.NoError(t, err) @@ -6475,6 +6485,30 @@ func TestCTE(t *testing.T) { RunTest(t, table, false) } +// For CTE Merge +func TestCTEMerge(t *testing.T) { + table := []testCase{ + {"WITH `cte` AS (SELECT 1,2) SELECT `col1`,`col2` FROM `cte`", true, "WITH `cte` AS (SELECT 1,2) SELECT `col1`,`col2` FROM `cte`"}, + {"WITH `cte` (col1, col2) AS (SELECT 1,2 UNION ALL SELECT 3,4) SELECT col1, col2 FROM cte;", true, "WITH `cte` (`col1`, `col2`) AS (SELECT 1,2 UNION ALL SELECT 3,4) SELECT `col1`,`col2` FROM `cte`"}, + {"WITH `cte` AS (SELECT 1,2), cte2 as (select 3) SELECT `col1`,`col2` FROM `cte`", true, "WITH `cte` AS (SELECT 1,2), `cte2` AS (SELECT 3) SELECT `col1`,`col2` FROM `cte`"}, + {"with cte(a) as (select 1) update t, cte set t.a=1 where t.a=cte.a;", true, "WITH `cte` (`a`) AS (SELECT 1) UPDATE (`t`) JOIN `cte` SET `t`.`a`=1 WHERE `t`.`a`=`cte`.`a`"}, + {"with cte(a) as (select 1) delete t from t, cte where t.a=cte.a;", true, "WITH `cte` (`a`) AS (SELECT 1) DELETE `t` FROM (`t`) JOIN `cte` WHERE `t`.`a`=`cte`.`a`"}, + {"WITH cte1 AS (SELECT 1) SELECT * FROM (WITH cte2 AS (SELECT 2) SELECT * FROM cte2 JOIN cte1) AS dt;", true, "WITH `cte1` AS (SELECT 1) SELECT * FROM (WITH `cte2` AS (SELECT 2) SELECT * FROM `cte2` JOIN `cte1`) AS `dt`"}, + {"WITH cte AS (SELECT 1) SELECT /*+ MAX_EXECUTION_TIME(1000) */ * FROM cte;", true, "WITH `cte` AS (SELECT 1) SELECT /*+ MAX_EXECUTION_TIME(1000)*/ * FROM `cte`"}, + {"with cte as (table t) table cte;", true, "WITH `cte` AS (TABLE `t`) TABLE `cte`"}, + {"with cte as (select 1) select 1 union with cte as (select 1) select * from cte;", false, ""}, + {"with cte as (select 1) (select 1);", true, "WITH `cte` AS (SELECT 1) (SELECT 1)"}, + {"with cte as (select 1) (select 1 union select 1)", true, "WITH `cte` AS (SELECT 1) (SELECT 1 UNION SELECT 1)"}, + {"select * from (with cte as (select 1) select 1 union select 2) qn", true, "SELECT * FROM (WITH `cte` AS (SELECT 1) SELECT 1 UNION SELECT 2) AS `qn`"}, + {"select * from t where 1 > (with cte as (select 2) select * from cte)", true, "SELECT * FROM `t` WHERE 1>(WITH `cte` AS (SELECT 2) SELECT * FROM `cte`)"}, + {"( with cte(n) as ( select 1 ) select n+1 from cte union select n+2 from cte) union select 1", true, "(WITH `cte` (`n`) AS (SELECT 1) SELECT `n`+1 FROM `cte` UNION SELECT `n`+2 FROM `cte`) UNION SELECT 1"}, + {"( with cte(n) as ( select 1 ) select n+1 from cte) union select 1", true, "(WITH `cte` (`n`) AS (SELECT 1) SELECT `n`+1 FROM `cte`) UNION SELECT 1"}, + {"( with cte(n) as ( select 1 ) (select n+1 from cte)) union select 1", true, "(WITH `cte` (`n`) AS (SELECT 1) (SELECT `n`+1 FROM `cte`)) UNION SELECT 1"}, + } + + RunTest(t, table, false) +} + func TestAsOfClause(t *testing.T) { table := []testCase{ {"SELECT * FROM `t` AS /* comment */ a;", true, "SELECT * FROM `t` AS `a`"}, diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index 633f3cef4e3bd..70ab80fa6cbc1 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -117,6 +117,8 @@ const ( HintIgnorePlanCache = "ignore_plan_cache" // HintLimitToCop is a hint enforce pushing limit or topn to coprocessor. HintLimitToCop = "limit_to_cop" + //HintMerge is a hint which can switch turning inline for the CTE. + HintMerge = "merge" // HintSemiJoinRewrite is a hint to force we rewrite the semi join operator as much as possible. HintSemiJoinRewrite = "semi_join_rewrite" ) @@ -1763,7 +1765,6 @@ func (b *PlanBuilder) buildUnion(ctx context.Context, selects []LogicalPlan, aft if err != nil { return nil, err } - unionDistinctPlan, err := b.buildUnionAll(ctx, distinctSelectPlans) if err != nil { return nil, err @@ -3523,6 +3524,7 @@ func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint, currentLev aggHints aggHintInfo timeRangeHint ast.HintTimeRange limitHints limitHintInfo + MergeHints MergeHintInfo leadingJoinOrder []hintTableInfo leadingHintCnt int ) @@ -3627,6 +3629,8 @@ func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint, currentLev timeRangeHint = hint.HintData.(ast.HintTimeRange) case HintLimitToCop: limitHints.preferLimitToCop = true + case HintMerge: + MergeHints.preferMerge = true case HintLeading: if leadingHintCnt == 0 { leadingJoinOrder = append(leadingJoinOrder, tableNames2HintTableInfo(b.ctx, hint.HintName.L, hint.Tables, b.hintProcessor, currentLevel)...) @@ -3663,6 +3667,7 @@ func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint, currentLev indexMergeHintList: indexMergeHintList, timeRangeHint: timeRangeHint, limitHints: limitHints, + MergeHints: MergeHints, leadingJoinOrder: leadingJoinOrder, }) } @@ -4004,6 +4009,12 @@ func (b *PlanBuilder) buildSelect(ctx context.Context, sel *ast.SelectStmt) (p L } } + if b.buildingCTE { + if hints := b.TableHints(); hints != nil { + b.outerCTEs[len(b.outerCTEs)-1].isInline = hints.MergeHints.preferMerge + } + } + sel.Fields.Fields = originalFields if oldLen != p.Schema().Len() { proj := LogicalProjection{Exprs: expression.Column2Exprs(p.Schema().Columns[:oldLen])}.Init(b.ctx, b.getSelectOffset()) @@ -4166,6 +4177,20 @@ func (b *PlanBuilder) tryBuildCTE(ctx context.Context, tn *ast.TableName, asName lp := LogicalCTE{cteAsName: tn.Name, cte: cte.cteClass, seedStat: cte.seedStat, isOuterMostCTE: !b.buildingCTE}.Init(b.ctx, b.getSelectOffset()) prevSchema := cte.seedLP.Schema().Clone() lp.SetSchema(getResultCTESchema(cte.seedLP.Schema(), b.ctx.GetSessionVars())) + + if cte.isInline { + lp.MergeHints.preferMerge = cte.isInline + saveCte := b.outerCTEs[i:] + b.outerCTEs = b.outerCTEs[:i] + o := b.buildingCTE + b.buildingCTE = false + defer func() { + b.outerCTEs = append(b.outerCTEs, saveCte...) + b.buildingCTE = o + }() + return b.buildDataSourceFromCTEMerge(ctx, cte.def) + } + for i, col := range lp.schema.Columns { lp.cte.ColumnMap[string(col.HashCode(nil))] = prevSchema.Columns[i] } @@ -4188,6 +4213,29 @@ func (b *PlanBuilder) tryBuildCTE(ctx context.Context, tn *ast.TableName, asName return nil, nil } +func (b *PlanBuilder) buildDataSourceFromCTEMerge(ctx context.Context, cte *ast.CommonTableExpression) (LogicalPlan, error) { + p, err := b.buildResultSetNode(ctx, cte.Query.Query) + if err != nil { + return nil, err + } + outPutNames := p.OutputNames() + for _, name := range outPutNames { + name.TblName = cte.Name + name.DBName = model.NewCIStr(b.ctx.GetSessionVars().CurrentDB) + } + + if len(cte.ColNameList) > 0 { + if len(cte.ColNameList) != len(p.OutputNames()) { + return nil, errors.New("CTE columns length is not consistent") + } + for i, n := range cte.ColNameList { + outPutNames[i].ColName = n + } + } + p.SetOutputNames(outPutNames) + return p, nil +} + func (b *PlanBuilder) buildDataSource(ctx context.Context, tn *ast.TableName, asName *model.CIStr) (LogicalPlan, error) { dbName := tn.Schema sessionVars := b.ctx.GetSessionVars() diff --git a/planner/core/logical_plans.go b/planner/core/logical_plans.go index 909f950798ce9..27315d316f45f 100644 --- a/planner/core/logical_plans.go +++ b/planner/core/logical_plans.go @@ -1881,6 +1881,7 @@ type LogicalCTE struct { cteAsName model.CIStr seedStat *property.StatsInfo isOuterMostCTE bool + MergeHints MergeHintInfo } // LogicalCTETable is for CTE table diff --git a/planner/core/physical_plan_test.go b/planner/core/physical_plan_test.go index 149f647123ab6..a54cc259780a1 100644 --- a/planner/core/physical_plan_test.go +++ b/planner/core/physical_plan_test.go @@ -1110,6 +1110,76 @@ func TestLimitToCopHint(t *testing.T) { } } +func TestCTEMergeHint(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists tc") + tk.MustExec("drop table if exists te") + tk.MustExec("drop table if exists t1") + tk.MustExec("drop table if exists t2") + tk.MustExec("drop table if exists t3") + tk.MustExec("drop table if exists t4") + tk.MustExec("create table tc(a int)") + tk.MustExec("create table te(c int)") + tk.MustExec("create table t1(a int)") + tk.MustExec("create table t2(b int)") + tk.MustExec("create table t3(c int)") + tk.MustExec("create table t4(d int)") + tk.MustExec("insert into tc values (1), (5), (10), (15), (20), (30), (50);") + tk.MustExec("insert into te values (1), (5), (10), (25), (40), (60), (100);") + tk.MustExec("insert into t1 values (1), (5), (10), (25), (40), (60), (100);") + tk.MustExec("insert into t2 values (1), (5), (10), (25), (40), (60), (100);") + tk.MustExec("insert into t3 values (1), (5), (10), (25), (40), (60), (100);") + tk.MustExec("insert into t4 values (1), (5), (10), (25), (40), (60), (100);") + tk.MustExec("analyze table tc;") + tk.MustExec("analyze table te;") + tk.MustExec("analyze table t1;") + tk.MustExec("analyze table t2;") + tk.MustExec("analyze table t3;") + tk.MustExec("analyze table t4;") + var ( + input []string + output []struct { + SQL string + Plan []string + Warning []string + } + ) + + planSuiteData := core.GetPlanSuiteData() + planSuiteData.GetTestCases(t, &input, &output) + + for i, ts := range input { + 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 diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index 4e052cb7bc3a3..b5c923ff6dd27 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -97,6 +97,7 @@ type tableHintInfo struct { indexMergeHintList []indexHintInfo timeRangeHint ast.HintTimeRange limitHints limitHintInfo + MergeHints MergeHintInfo leadingJoinOrder []hintTableInfo } @@ -104,6 +105,11 @@ type limitHintInfo struct { preferLimitToCop bool } +//MergeHintInfo ...one bool flag for cte +type MergeHintInfo struct { + preferMerge bool +} + type hintTableInfo struct { dbName model.CIStr tblName model.CIStr @@ -427,6 +433,8 @@ type cteInfo struct { seedStat *property.StatsInfo // The LogicalCTEs that reference the same table should share the same CteClass. cteClass *CTEClass + + isInline bool } // PlanBuilder builds Plan from an ast.Node. diff --git a/planner/core/testdata/plan_suite_in.json b/planner/core/testdata/plan_suite_in.json index d04c9d8944158..3ae76927a2975 100644 --- a/planner/core/testdata/plan_suite_in.json +++ b/planner/core/testdata/plan_suite_in.json @@ -572,6 +572,23 @@ "select /*+ LIMIT_TO_COP() */ a from tn where a > 10 limit 1" ] }, + { + "name": "TestCTEMergeHint", + "cases": [ + "with cte as (select /*+ MERGE()*/ * from tc where tc.a < 60) select * from cte where cte.a <18", + "with cte as (select * from tc where tc.a < 60) select * from cte where cte.a <18", + "WITH cte1 AS (SELECT /*+ MERGE()*/ a FROM tc), cte2 AS (SELECT /*+ MERGE()*/ c FROM te) SELECT * FROM cte1 JOIN cte2 WHERE cte1.a = cte2.c;", + "WITH cte1 AS (SELECT a FROM tc), cte2 AS (SELECT /*+ MERGE()*/ c FROM te) SELECT * FROM cte1 JOIN cte2 WHERE cte1.a = cte2.c;", + "with recursive cte1(c1) as (select 1 union select /*+ merge */ c1 + 1 c1 from cte1 where c1 < 100) select * from cte1;", + "with cte1 as (select * from tc), cte2 as (with cte3 as (select /*+ MERGE() */ * from te) ,cte4 as (select * from tc) select * from cte3,cte4) select * from cte2;", + "with cte1 as (select * from t1), cte2 as (with cte3 as (with cte5 as (select * from t2),cte6 as (select * from t3) select * from cte5,cte6) ,cte4 as (select * from t4) select * from cte3,cte4) select * from cte1,cte2;", + "with cte1 as (select /*+ MERGE() */ * from t1), cte2 as (with cte3 as (with cte5 as (select * from t2),cte6 as (select * from t3) select * from cte5,cte6) ,cte4 as (select * from t4) select * from cte3,cte4) select * from cte1,cte2;", + "with cte1 as (select * from t1), cte2 as (with cte3 as (with cte5 as (select * from t2),cte6 as (select * from t3) select * from cte5,cte6) ,cte4 as (select /*+ MERGE() */ * from t4) select * from cte3,cte4) select * from cte1,cte2;", + "with cte1 as (select * from t1), cte2 as (with cte3 as (with cte5 as (select * from t2),cte6 as (select /*+ MERGE() */ * from t3) select * from cte5,cte6) ,cte4 as (select * from t4) select * from cte3,cte4) select * from cte1,cte2;", + "with cte2 as (with cte4 as (select * from tc) select * from te, cte4) select * from cte2;", + "with cte2 as (with cte4 as (select /*+ merge() */ * from tc) select * from te, cte4) select * from cte2;" + ] + }, { "name": "TestPushdownDistinctEnable", "cases": [ diff --git a/planner/core/testdata/plan_suite_out.json b/planner/core/testdata/plan_suite_out.json index 40c13bde56680..011c93f3885c3 100644 --- a/planner/core/testdata/plan_suite_out.json +++ b/planner/core/testdata/plan_suite_out.json @@ -1594,6 +1594,237 @@ } ] }, + { + "Name": "TestCTEMergeHint", + "Cases": [ + { + "SQL": "with cte as (select /*+ MERGE()*/ * from tc where tc.a < 60) select * from cte where cte.a <18", + "Plan": [ + "TableReader 4.00 root data:Selection", + "└─Selection 4.00 cop[tikv] lt(test.tc.a, 18), lt(test.tc.a, 60)", + " └─TableFullScan 7.00 cop[tikv] table:tc keep order:false" + ], + "Warning": null + }, + { + "SQL": "with cte as (select * from tc where tc.a < 60) select * from cte where cte.a <18", + "Plan": [ + "Selection 3.20 root lt(test.tc.a, 18)", + "└─CTEFullScan 4.00 root CTE:cte data:CTE_0", + "CTE_0 4.00 root Non-Recursive CTE", + "└─TableReader(Seed Part) 4.00 root data:Selection", + " └─Selection 4.00 cop[tikv] lt(test.tc.a, 18), lt(test.tc.a, 60)", + " └─TableFullScan 7.00 cop[tikv] table:tc keep order:false" + ], + "Warning": null + }, + { + "SQL": "WITH cte1 AS (SELECT /*+ MERGE()*/ a FROM tc), cte2 AS (SELECT /*+ MERGE()*/ c FROM te) SELECT * FROM cte1 JOIN cte2 WHERE cte1.a = cte2.c;", + "Plan": [ + "HashJoin 7.00 root inner join, equal:[eq(test.tc.a, test.te.c)]", + "├─TableReader(Build) 7.00 root data:Selection", + "│ └─Selection 7.00 cop[tikv] not(isnull(test.te.c))", + "│ └─TableFullScan 7.00 cop[tikv] table:te keep order:false", + "└─TableReader(Probe) 7.00 root data:Selection", + " └─Selection 7.00 cop[tikv] not(isnull(test.tc.a))", + " └─TableFullScan 7.00 cop[tikv] table:tc keep order:false" + ], + "Warning": null + }, + { + "SQL": "WITH cte1 AS (SELECT a FROM tc), cte2 AS (SELECT /*+ MERGE()*/ c FROM te) SELECT * FROM cte1 JOIN cte2 WHERE cte1.a = cte2.c;", + "Plan": [ + "Projection 4.48 root test.tc.a, test.te.c", + "└─HashJoin 4.48 root inner join, equal:[eq(test.te.c, test.tc.a)]", + " ├─Selection(Build) 4.48 root not(isnull(test.tc.a))", + " │ └─CTEFullScan 5.60 root CTE:cte1 data:CTE_0", + " └─TableReader(Probe) 7.00 root data:Selection", + " └─Selection 7.00 cop[tikv] not(isnull(test.te.c))", + " └─TableFullScan 7.00 cop[tikv] table:te keep order:false", + "CTE_0 5.60 root Non-Recursive CTE", + "└─Selection(Seed Part) 5.60 root not(isnull(test.tc.a))", + " └─TableReader 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:tc keep order:false" + ], + "Warning": null + }, + { + "SQL": "with recursive cte1(c1) as (select 1 union select /*+ merge */ c1 + 1 c1 from cte1 where c1 < 100) select * from cte1;", + "Plan": [ + "CTEFullScan 2.00 root CTE:cte1 data:CTE_0", + "CTE_0 2.00 root Recursive CTE", + "├─Projection(Seed Part) 1.00 root 1->Column#2", + "│ └─TableDual 1.00 root rows:1", + "└─Projection(Recursive Part) 0.80 root cast(plus(Column#3, 1), bigint(1) BINARY)->Column#5", + " └─Selection 0.80 root lt(Column#3, 100)", + " └─CTETable 1.00 root Scan on CTE_0" + ], + "Warning": [ + "[parser:1064]Optimizer hint syntax error at line 1 column 87 near \"\" " + ] + }, + { + "SQL": "with cte1 as (select * from tc), cte2 as (with cte3 as (select /*+ MERGE() */ * from te) ,cte4 as (select * from tc) select * from cte3,cte4) select * from cte2;", + "Plan": [ + "CTEFullScan 49.00 root CTE:cte2 data:CTE_1", + "CTE_1 49.00 root Non-Recursive CTE", + "└─HashJoin(Seed Part) 49.00 root CARTESIAN inner join", + " ├─CTEFullScan(Build) 7.00 root CTE:cte4 data:CTE_3", + " └─TableReader(Probe) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:te keep order:false", + "CTE_3 7.00 root Non-Recursive CTE", + "└─TableReader(Seed Part) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:tc keep order:false" + ], + "Warning": null + }, + { + "SQL": "with cte1 as (select * from t1), cte2 as (with cte3 as (with cte5 as (select * from t2),cte6 as (select * from t3) select * from cte5,cte6) ,cte4 as (select * from t4) select * from cte3,cte4) select * from cte1,cte2;", + "Plan": [ + "HashJoin 1920.80 root CARTESIAN inner join", + "├─CTEFullScan(Build) 5.60 root CTE:cte1 data:CTE_0", + "└─CTEFullScan(Probe) 343.00 root CTE:cte2 data:CTE_1", + "CTE_0 5.60 root Non-Recursive CTE", + "└─Selection(Seed Part) 5.60 root 1", + " └─TableReader 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:t1 keep order:false", + "CTE_1 343.00 root Non-Recursive CTE", + "└─Projection(Seed Part) 343.00 root test.t2.b, test.t3.c, test.t4.d", + " └─HashJoin 343.00 root CARTESIAN inner join", + " ├─CTEFullScan(Build) 7.00 root CTE:cte4 data:CTE_5", + " └─CTEFullScan(Probe) 49.00 root CTE:cte3 data:CTE_2", + "CTE_5 7.00 root Non-Recursive CTE", + "└─TableReader(Seed Part) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:t4 keep order:false", + "CTE_2 49.00 root Non-Recursive CTE", + "└─HashJoin(Seed Part) 49.00 root CARTESIAN inner join", + " ├─CTEFullScan(Build) 7.00 root CTE:cte6 data:CTE_4", + " └─CTEFullScan(Probe) 7.00 root CTE:cte5 data:CTE_3", + "CTE_4 7.00 root Non-Recursive CTE", + "└─TableReader(Seed Part) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:t3 keep order:false", + "CTE_3 7.00 root Non-Recursive CTE", + "└─TableReader(Seed Part) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:t2 keep order:false" + ], + "Warning": null + }, + { + "SQL": "with cte1 as (select /*+ MERGE() */ * from t1), cte2 as (with cte3 as (with cte5 as (select * from t2),cte6 as (select * from t3) select * from cte5,cte6) ,cte4 as (select * from t4) select * from cte3,cte4) select * from cte1,cte2;", + "Plan": [ + "HashJoin 2401.00 root CARTESIAN inner join", + "├─TableReader(Build) 7.00 root data:TableFullScan", + "│ └─TableFullScan 7.00 cop[tikv] table:t1 keep order:false", + "└─CTEFullScan(Probe) 343.00 root CTE:cte2 data:CTE_1", + "CTE_1 343.00 root Non-Recursive CTE", + "└─Projection(Seed Part) 343.00 root test.t2.b, test.t3.c, test.t4.d", + " └─HashJoin 343.00 root CARTESIAN inner join", + " ├─CTEFullScan(Build) 7.00 root CTE:cte4 data:CTE_5", + " └─CTEFullScan(Probe) 49.00 root CTE:cte3 data:CTE_2", + "CTE_5 7.00 root Non-Recursive CTE", + "└─TableReader(Seed Part) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:t4 keep order:false", + "CTE_2 49.00 root Non-Recursive CTE", + "└─HashJoin(Seed Part) 49.00 root CARTESIAN inner join", + " ├─CTEFullScan(Build) 7.00 root CTE:cte6 data:CTE_4", + " └─CTEFullScan(Probe) 7.00 root CTE:cte5 data:CTE_3", + "CTE_4 7.00 root Non-Recursive CTE", + "└─TableReader(Seed Part) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:t3 keep order:false", + "CTE_3 7.00 root Non-Recursive CTE", + "└─TableReader(Seed Part) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:t2 keep order:false" + ], + "Warning": null + }, + { + "SQL": "with cte1 as (select * from t1), cte2 as (with cte3 as (with cte5 as (select * from t2),cte6 as (select * from t3) select * from cte5,cte6) ,cte4 as (select /*+ MERGE() */ * from t4) select * from cte3,cte4) select * from cte1,cte2;", + "Plan": [ + "HashJoin 1920.80 root CARTESIAN inner join", + "├─CTEFullScan(Build) 5.60 root CTE:cte1 data:CTE_0", + "└─CTEFullScan(Probe) 343.00 root CTE:cte2 data:CTE_1", + "CTE_0 5.60 root Non-Recursive CTE", + "└─Selection(Seed Part) 5.60 root 1", + " └─TableReader 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:t1 keep order:false", + "CTE_1 343.00 root Non-Recursive CTE", + "└─Projection(Seed Part) 343.00 root test.t2.b, test.t3.c, test.t4.d", + " └─HashJoin 343.00 root CARTESIAN inner join", + " ├─TableReader(Build) 7.00 root data:TableFullScan", + " │ └─TableFullScan 7.00 cop[tikv] table:t4 keep order:false", + " └─CTEFullScan(Probe) 49.00 root CTE:cte3 data:CTE_2", + "CTE_2 49.00 root Non-Recursive CTE", + "└─HashJoin(Seed Part) 49.00 root CARTESIAN inner join", + " ├─CTEFullScan(Build) 7.00 root CTE:cte6 data:CTE_4", + " └─CTEFullScan(Probe) 7.00 root CTE:cte5 data:CTE_3", + "CTE_4 7.00 root Non-Recursive CTE", + "└─TableReader(Seed Part) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:t3 keep order:false", + "CTE_3 7.00 root Non-Recursive CTE", + "└─TableReader(Seed Part) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:t2 keep order:false" + ], + "Warning": null + }, + { + "SQL": "with cte1 as (select * from t1), cte2 as (with cte3 as (with cte5 as (select * from t2),cte6 as (select /*+ MERGE() */ * from t3) select * from cte5,cte6) ,cte4 as (select * from t4) select * from cte3,cte4) select * from cte1,cte2;", + "Plan": [ + "HashJoin 1920.80 root CARTESIAN inner join", + "├─CTEFullScan(Build) 5.60 root CTE:cte1 data:CTE_0", + "└─CTEFullScan(Probe) 343.00 root CTE:cte2 data:CTE_1", + "CTE_0 5.60 root Non-Recursive CTE", + "└─Selection(Seed Part) 5.60 root 1", + " └─TableReader 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:t1 keep order:false", + "CTE_1 343.00 root Non-Recursive CTE", + "└─Projection(Seed Part) 343.00 root test.t2.b, test.t3.c, test.t4.d", + " └─HashJoin 343.00 root CARTESIAN inner join", + " ├─CTEFullScan(Build) 7.00 root CTE:cte4 data:CTE_5", + " └─CTEFullScan(Probe) 49.00 root CTE:cte3 data:CTE_2", + "CTE_5 7.00 root Non-Recursive CTE", + "└─TableReader(Seed Part) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:t4 keep order:false", + "CTE_2 49.00 root Non-Recursive CTE", + "└─HashJoin(Seed Part) 49.00 root CARTESIAN inner join", + " ├─TableReader(Build) 7.00 root data:TableFullScan", + " │ └─TableFullScan 7.00 cop[tikv] table:t3 keep order:false", + " └─CTEFullScan(Probe) 7.00 root CTE:cte5 data:CTE_3", + "CTE_3 7.00 root Non-Recursive CTE", + "└─TableReader(Seed Part) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:t2 keep order:false" + ], + "Warning": null + }, + { + "SQL": "with cte2 as (with cte4 as (select * from tc) select * from te, cte4) select * from cte2;", + "Plan": [ + "CTEFullScan 49.00 root CTE:cte2 data:CTE_0", + "CTE_0 49.00 root Non-Recursive CTE", + "└─HashJoin(Seed Part) 49.00 root CARTESIAN inner join", + " ├─CTEFullScan(Build) 7.00 root CTE:cte4 data:CTE_1", + " └─TableReader(Probe) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:te keep order:false", + "CTE_1 7.00 root Non-Recursive CTE", + "└─TableReader(Seed Part) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:tc keep order:false" + ], + "Warning": null + }, + { + "SQL": "with cte2 as (with cte4 as (select /*+ merge() */ * from tc) select * from te, cte4) select * from cte2;", + "Plan": [ + "CTEFullScan 49.00 root CTE:cte2 data:CTE_0", + "CTE_0 49.00 root Non-Recursive CTE", + "└─HashJoin(Seed Part) 49.00 root CARTESIAN inner join", + " ├─TableReader(Build) 7.00 root data:TableFullScan", + " │ └─TableFullScan 7.00 cop[tikv] table:tc keep order:false", + " └─TableReader(Probe) 7.00 root data:TableFullScan", + " └─TableFullScan 7.00 cop[tikv] table:te keep order:false" + ], + "Warning": null + } + ] + }, { "Name": "TestPushdownDistinctEnable", "Cases": [