From f4ee90d06f5e89a634f4ea0e24f7c8eb27aabb47 Mon Sep 17 00:00:00 2001 From: Haibin Xie Date: Mon, 11 Nov 2019 17:46:29 +0800 Subject: [PATCH] planner: correct block offset for table as names (#12996) --- planner/core/hints.go | 8 +++++--- planner/core/logical_plan_builder.go | 23 ++++++++++++++++------- planner/core/testdata/plan_suite_in.json | 4 +++- planner/core/testdata/plan_suite_out.json | 9 +++++++++ 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/planner/core/hints.go b/planner/core/hints.go index c94da58f0663f..a19a89404a652 100644 --- a/planner/core/hints.go +++ b/planner/core/hints.go @@ -247,13 +247,15 @@ func extractTableAsName(p PhysicalPlan) (*model.CIStr, *model.CIStr) { func getJoinHints(sctx sessionctx.Context, joinType string, parentOffset int, nodeType nodeType, children ...PhysicalPlan) (res []*ast.TableOptimizerHint) { for _, child := range children { - if child.SelectBlockOffset() == -1 { + blockOffset := child.SelectBlockOffset() + if blockOffset == -1 { continue } var dbName, tableName *model.CIStr if child.SelectBlockOffset() != parentOffset { hintTable := sctx.GetSessionVars().PlannerSelectBlockAsName[child.SelectBlockOffset()] - dbName, tableName = &hintTable.DBName, &hintTable.TableName + // For sub-queries like `(select * from t) t1`, t1 should belong to its surrounding select block. + dbName, tableName, blockOffset = &hintTable.DBName, &hintTable.TableName, parentOffset } else { dbName, tableName = extractTableAsName(child) } @@ -261,7 +263,7 @@ func getJoinHints(sctx sessionctx.Context, joinType string, parentOffset int, no continue } res = append(res, &ast.TableOptimizerHint{ - QBName: generateQBName(nodeType, child.SelectBlockOffset()), + QBName: generateQBName(nodeType, blockOffset), HintName: model.NewCIStr(joinType), Tables: []ast.HintTable{{DBName: *dbName, TableName: *tableName}}, }) diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index 883d8a5921f00..d7facea93a46f 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -173,6 +173,7 @@ func (b *PlanBuilder) buildResultSetNode(ctx context.Context, node ast.ResultSet case *ast.Join: return b.buildJoin(ctx, x) case *ast.TableSource: + var isTableName bool switch v := x.Source.(type) { case *ast.SelectStmt: p, err = b.buildSelect(ctx, v) @@ -180,6 +181,7 @@ func (b *PlanBuilder) buildResultSetNode(ctx context.Context, node ast.ResultSet p, err = b.buildUnion(ctx, v) case *ast.TableName: p, err = b.buildDataSource(ctx, v, &x.AsName) + isTableName = true default: err = ErrUnsupportedType.GenWithStackByArgs(v) } @@ -196,7 +198,8 @@ func (b *PlanBuilder) buildResultSetNode(ctx context.Context, node ast.ResultSet name.TblName = x.AsName } } - if b.ctx.GetSessionVars().PlannerSelectBlockAsName != nil { + // `TableName` is not a select block, so we do not need to handle it. + if !isTableName && b.ctx.GetSessionVars().PlannerSelectBlockAsName != nil { b.ctx.GetSessionVars().PlannerSelectBlockAsName[p.SelectBlockOffset()] = ast.HintTable{DBName: p.OutputNames()[0].DBName, TableName: p.OutputNames()[0].TblName} } // Duplicate column name in one table is not allowed. @@ -345,7 +348,7 @@ func (p *LogicalJoin) extractOnCondition(conditions []expression.Expression, der // extractTableAlias returns table alias of the LogicalPlan's columns. // It will return nil when there are multiple table alias, because the alias is only used to check if // the logicalPlan match some optimizer hints, and hints are not expected to take effect in this case. -func extractTableAlias(p Plan) *hintTableInfo { +func extractTableAlias(p Plan, parentOffset int) *hintTableInfo { if len(p.OutputNames()) > 0 && p.OutputNames()[0].TblName.L != "" { firstName := p.OutputNames()[0] for _, name := range p.OutputNames() { @@ -353,7 +356,13 @@ func extractTableAlias(p Plan) *hintTableInfo { return nil } } - return &hintTableInfo{dbName: firstName.DBName, tblName: firstName.TblName, selectOffset: p.SelectBlockOffset()} + blockOffset := p.SelectBlockOffset() + blockAsNames := p.SCtx().GetSessionVars().PlannerSelectBlockAsName + // For sub-queries like `(select * from t) t1`, t1 should belong to its surrounding select block. + if blockOffset != parentOffset && blockAsNames != nil && blockAsNames[blockOffset].TableName.L != "" { + blockOffset = parentOffset + } + return &hintTableInfo{dbName: firstName.DBName, tblName: firstName.TblName, selectOffset: blockOffset} } return nil } @@ -363,8 +372,8 @@ func (p *LogicalJoin) setPreferredJoinType(hintInfo *tableHintInfo) { return } - lhsAlias := extractTableAlias(p.children[0]) - rhsAlias := extractTableAlias(p.children[1]) + lhsAlias := extractTableAlias(p.children[0], p.blockOffset) + rhsAlias := extractTableAlias(p.children[1], p.blockOffset) if hintInfo.ifPreferMergeJoin(lhsAlias, rhsAlias) { p.preferJoinType |= preferMergeJoin } @@ -2782,8 +2791,8 @@ func (b *PlanBuilder) buildSemiJoin(outerPlan, innerPlan LogicalPlan, onConditio } // Apply forces to choose hash join currently, so don't worry the hints will take effect if the semi join is in one apply. if b.TableHints() != nil { - outerAlias := extractTableAlias(outerPlan) - innerAlias := extractTableAlias(innerPlan) + outerAlias := extractTableAlias(outerPlan, joinPlan.blockOffset) + innerAlias := extractTableAlias(innerPlan, joinPlan.blockOffset) if b.TableHints().ifPreferMergeJoin(outerAlias, innerAlias) { joinPlan.preferJoinType |= preferMergeJoin } diff --git a/planner/core/testdata/plan_suite_in.json b/planner/core/testdata/plan_suite_in.json index 066c780e3e076..86a4740083ff8 100644 --- a/planner/core/testdata/plan_suite_in.json +++ b/planner/core/testdata/plan_suite_in.json @@ -15,6 +15,7 @@ "select /*+ SM_JOIN(t1) */ t1.a, t1.b from t t1, (select t2.a from t t2, t t3 where t2.a = t3.c) s where t1.a=s.a", "select /*+ INL_JOIN(t1) */ t1.a, t1.b from t t1, (select t2.a from t t2, t t3 where t2.a = t3.c) s where t1.a=s.a", "select /*+ HASH_JOIN(t1) */ t1.a, t1.b from t t1, (select t2.a from t t2, t t3 where t2.a = t3.c) s where t1.a=s.a", + "select /*+ HASH_JOIN(@sel_2 t1@sel_2, t2@sel_2), SM_JOIN(@sel_1 t1@sel_1, t2@sel_1) */ * from (select t1.a, t1.b from t t1, t t2 where t1.a = t2.a) t1, t t2 where t1.b = t2.b", // aggregation hints "select /*+ STREAM_AGG() */ s, count(s) from (select /*+ HASH_AGG() */ sum(t1.a) as s from t t1, t t2 where t1.a = t2.b group by t1.a) p group by s", "select /*+ HASH_AGG() */ s, count(s) from (select /*+ STREAM_AGG() */ sum(t1.a) as s from t t1, t t2 where t1.a = t2.b group by t1.a) p group by s", @@ -468,7 +469,8 @@ "select /*+ HASH_AGG(@sel_1), STREAM_AGG(@sel_2) */ count(*) from t t1 where t1.a < (select count(*) from t t2 where t1.a > t2.a)", "select /*+ STREAM_AGG(@sel_1), HASH_AGG(@qb) */ count(*) from t t1 where t1.a < (select /*+ QB_NAME(qb) */ count(*) from t t2 where t1.a > t2.a)", "select /*+ HASH_AGG(@sel_2) */ a, (select count(*) from t t1 where t1.b > t.a) from t where b > (select b from t t2 where t2.b = t.a limit 1)", - "select /*+ HASH_JOIN(@sel_1 t1), HASH_JOIN(@sel_2 t1) */ t1.b, t2.a, t2.aa from t t1, (select t1.a as a, t2.a as aa from t t1, t t2) t2 where t1.a = t2.aa;" + "select /*+ HASH_JOIN(@sel_1 t1), HASH_JOIN(@sel_2 t1) */ t1.b, t2.a, t2.aa from t t1, (select t1.a as a, t2.a as aa from t t1, t t2) t2 where t1.a = t2.aa;", + "select /*+ HASH_JOIN(@sel_2 t1@sel_2, t2@sel_2), SM_JOIN(@sel_1 t1@sel_1, t2@sel_1) */ * from (select t1.a, t1.b from t t1, t t2 where t1.a = t2.a) t1, t t2 where t1.b = t2.b" ] }, { diff --git a/planner/core/testdata/plan_suite_out.json b/planner/core/testdata/plan_suite_out.json index 36335fe6a7f3a..c878c188db5f9 100644 --- a/planner/core/testdata/plan_suite_out.json +++ b/planner/core/testdata/plan_suite_out.json @@ -50,6 +50,10 @@ "SQL": "select /*+ HASH_JOIN(t1) */ t1.a, t1.b from t t1, (select t2.a from t t2, t t3 where t2.a = t3.c) s where t1.a=s.a", "Best": "RightHashJoin{TableReader(Table(t))->LeftHashJoin{IndexReader(Index(t.f)[[NULL,+inf]])->IndexReader(Index(t.c_d_e)[[NULL,+inf]])}(Column#13,Column#27)}(Column#1,Column#13)->Projection" }, + { + "SQL": "select /*+ HASH_JOIN(@sel_2 t1@sel_2, t2@sel_2), SM_JOIN(@sel_1 t1@sel_1, t2@sel_1) */ * from (select t1.a, t1.b from t t1, t t2 where t1.a = t2.a) t1, t t2 where t1.b = t2.b", + "Best": "MergeInnerJoin{LeftHashJoin{TableReader(Table(t))->IndexReader(Index(t.f)[[NULL,+inf]])}(Column#1,Column#13)->Sort->TableReader(Table(t))->Sort}(Column#2,Column#28)->Projection" + }, { "SQL": "select /*+ STREAM_AGG() */ s, count(s) from (select /*+ HASH_AGG() */ sum(t1.a) as s from t t1, t t2 where t1.a = t2.b group by t1.a) p group by s", "Best": "LeftHashJoin{IndexReader(Index(t.f)[[NULL,+inf]])->TableReader(Table(t))}(Column#1,Column#14)->Projection->HashAgg->Sort->StreamAgg->Projection" @@ -1243,6 +1247,11 @@ "SQL": "select /*+ HASH_JOIN(@sel_1 t1), HASH_JOIN(@sel_2 t1) */ t1.b, t2.a, t2.aa from t t1, (select t1.a as a, t2.a as aa from t t1, t t2) t2 where t1.a = t2.aa;", "Plan": "RightHashJoin{TableReader(Table(t))->LeftHashJoin{IndexReader(Index(t.f)[[NULL,+inf]])->IndexReader(Index(t.f)[[NULL,+inf]])}}(Column#1,Column#25)->Projection", "Hints": "USE_INDEX(@`sel_1` `test`.`t1` ), USE_INDEX(@`sel_2` `test`.`t1` `f`), USE_INDEX(@`sel_2` `test`.`t2` `f`), HASH_JOIN(@`sel_2` `test`.`t1`), HASH_JOIN(@`sel_1` `test`.`t1`)" + }, + { + "SQL": "select /*+ HASH_JOIN(@sel_2 t1@sel_2, t2@sel_2), SM_JOIN(@sel_1 t1@sel_1, t2@sel_1) */ * from (select t1.a, t1.b from t t1, t t2 where t1.a = t2.a) t1, t t2 where t1.b = t2.b", + "Plan": "MergeInnerJoin{LeftHashJoin{TableReader(Table(t))->IndexReader(Index(t.f)[[NULL,+inf]])}(Column#1,Column#13)->Sort->TableReader(Table(t))->Sort}(Column#2,Column#28)->Projection", + "Hints": "USE_INDEX(@`sel_2` `test`.`t1` ), USE_INDEX(@`sel_2` `test`.`t2` `f`), HASH_JOIN(@`sel_2` `test`.`t1`), USE_INDEX(@`sel_1` `test`.`t2` ), SM_JOIN(@`sel_1` `test`.`t1`)" } ] },