From 9f2756f61cef5725e024f37ee8c4ff0aa4ff2321 Mon Sep 17 00:00:00 2001 From: LiBinfeng <46676950+LiBinfeng-01@users.noreply.github.com> Date: Thu, 15 Aug 2024 15:11:12 +0800 Subject: [PATCH] [feat](Nereids) support nereids hint position detaction (#39113) When use hint in wrong position or use unsupport hint, use channel(2) to filter it out --- .../org/apache/doris/nereids/DorisLexer.g4 | 18 +++ .../nereids/parser/LogicalPlanBuilder.java | 126 ++++++++++-------- .../LogicalPlanBuilderForCreateView.java | 5 + .../parser/LogicalPlanBuilderForSyncMv.java | 5 + .../doris/nereids/parser/NereidsParser.java | 37 ++++- .../parser/plsql/PLSqlLogicalPlanBuilder.java | 4 + .../nereids/parser/NereidsParserTest.java | 8 -- .../data/nereids_p0/hint/test_hint.out | 78 +++++++++++ .../data/nereids_p0/hint/test_leading.out | 4 +- .../suites/nereids_p0/hint/test_hint.groovy | 61 +++++++++ .../nereids_p0/hint/test_leading.groovy | 2 +- 11 files changed, 280 insertions(+), 68 deletions(-) create mode 100644 regression-test/data/nereids_p0/hint/test_hint.out create mode 100644 regression-test/suites/nereids_p0/hint/test_hint.groovy diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4 b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4 index 5ed655c212a7ce..b4a4c7ddefe16b 100644 --- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4 +++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4 @@ -72,6 +72,19 @@ lexer grammar DorisLexer; public void markUnclosedComment() { has_unclosed_bracketed_comment = true; } + + // This variable will hold the external state + private boolean channel2; + + // Method to set the external state + public void setChannel2(boolean value) { + this.channel2 = value; + } + + // Method to decide the channel based on external state + private boolean isChannel2() { + return this.channel2; + } } SEMICOLON: ';'; @@ -686,6 +699,11 @@ BRACKETED_COMMENT : '/*' {!isHint()}? ( BRACKETED_COMMENT | . )*? ('*/' | {markUnclosedComment();} EOF) -> channel(HIDDEN) ; +HINT_WITH_CHANNEL + : {isChannel2()}? HINT_START .*? HINT_END -> channel(2) + ; + + FROM_DUAL : 'FROM' WS+ 'DUAL' -> channel(HIDDEN); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java index de23ba16e82e3a..104acf7fe4c6ef 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java @@ -514,6 +514,12 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor { return pos1.getCharPositionInLine() - pos2.getCharPositionInLine(); }); + private final Map selectHintMap; + + public LogicalPlanBuilder(Map selectHintMap) { + this.selectHintMap = selectHintMap; + } + @SuppressWarnings("unchecked") protected T typedVisit(ParseTree ctx) { return (T) ctx.accept(this); @@ -1352,7 +1358,16 @@ public LogicalPlan visitRegularQuerySpecification(RegularQuerySpecificationConte Optional.ofNullable(ctx.aggClause()), Optional.ofNullable(ctx.havingClause())); selectPlan = withQueryOrganization(selectPlan, ctx.queryOrganization()); - return withSelectHint(selectPlan, selectCtx.selectHint()); + if ((selectHintMap == null) || selectHintMap.isEmpty()) { + return selectPlan; + } + List selectHintContexts = Lists.newArrayList(); + for (Integer key : selectHintMap.keySet()) { + if (key > selectCtx.getStart().getStopIndex() && key < selectCtx.getStop().getStartIndex()) { + selectHintContexts.add(selectHintMap.get(key)); + } + } + return withSelectHint(selectPlan, selectHintContexts); }); } @@ -3110,67 +3125,70 @@ private LogicalPlan withJoinRelations(LogicalPlan input, RelationContext ctx) { return last; } - private LogicalPlan withSelectHint(LogicalPlan logicalPlan, SelectHintContext hintContext) { - if (hintContext == null) { + private LogicalPlan withSelectHint(LogicalPlan logicalPlan, List hintContexts) { + if (hintContexts.isEmpty()) { return logicalPlan; } Map hints = Maps.newLinkedHashMap(); - for (HintStatementContext hintStatement : hintContext.hintStatements) { - String hintName = hintStatement.hintName.getText().toLowerCase(Locale.ROOT); - switch (hintName) { - case "set_var": - Map> parameters = Maps.newLinkedHashMap(); - for (HintAssignmentContext kv : hintStatement.parameters) { - if (kv.key != null) { - String parameterName = visitIdentifierOrText(kv.key); - Optional value = Optional.empty(); - if (kv.constantValue != null) { - Literal literal = (Literal) visit(kv.constantValue); - value = Optional.ofNullable(literal.toLegacyLiteral().getStringValue()); - } else if (kv.identifierValue != null) { - // maybe we should throw exception when the identifierValue is quoted identifier - value = Optional.ofNullable(kv.identifierValue.getText()); + for (ParserRuleContext hintContext : hintContexts) { + SelectHintContext selectHintContext = (SelectHintContext) hintContext; + for (HintStatementContext hintStatement : selectHintContext.hintStatements) { + String hintName = hintStatement.hintName.getText().toLowerCase(Locale.ROOT); + switch (hintName) { + case "set_var": + Map> parameters = Maps.newLinkedHashMap(); + for (HintAssignmentContext kv : hintStatement.parameters) { + if (kv.key != null) { + String parameterName = visitIdentifierOrText(kv.key); + Optional value = Optional.empty(); + if (kv.constantValue != null) { + Literal literal = (Literal) visit(kv.constantValue); + value = Optional.ofNullable(literal.toLegacyLiteral().getStringValue()); + } else if (kv.identifierValue != null) { + // maybe we should throw exception when the identifierValue is quoted identifier + value = Optional.ofNullable(kv.identifierValue.getText()); + } + parameters.put(parameterName, value); } - parameters.put(parameterName, value); } - } - hints.put(hintName, new SelectHintSetVar(hintName, parameters)); - break; - case "leading": - List leadingParameters = new ArrayList(); - for (HintAssignmentContext kv : hintStatement.parameters) { - if (kv.key != null) { - String parameterName = visitIdentifierOrText(kv.key); - leadingParameters.add(parameterName); + hints.put(hintName, new SelectHintSetVar(hintName, parameters)); + break; + case "leading": + List leadingParameters = new ArrayList(); + for (HintAssignmentContext kv : hintStatement.parameters) { + if (kv.key != null) { + String parameterName = visitIdentifierOrText(kv.key); + leadingParameters.add(parameterName); + } } - } - hints.put(hintName, new SelectHintLeading(hintName, leadingParameters)); - break; - case "ordered": - hints.put(hintName, new SelectHintOrdered(hintName)); - break; - case "use_cbo_rule": - List useRuleParameters = new ArrayList(); - for (HintAssignmentContext kv : hintStatement.parameters) { - if (kv.key != null) { - String parameterName = visitIdentifierOrText(kv.key); - useRuleParameters.add(parameterName); + hints.put(hintName, new SelectHintLeading(hintName, leadingParameters)); + break; + case "ordered": + hints.put(hintName, new SelectHintOrdered(hintName)); + break; + case "use_cbo_rule": + List useRuleParameters = new ArrayList(); + for (HintAssignmentContext kv : hintStatement.parameters) { + if (kv.key != null) { + String parameterName = visitIdentifierOrText(kv.key); + useRuleParameters.add(parameterName); + } } - } - hints.put(hintName, new SelectHintUseCboRule(hintName, useRuleParameters, false)); - break; - case "no_use_cbo_rule": - List noUseRuleParameters = new ArrayList(); - for (HintAssignmentContext kv : hintStatement.parameters) { - String parameterName = visitIdentifierOrText(kv.key); - if (kv.key != null) { - noUseRuleParameters.add(parameterName); + hints.put(hintName, new SelectHintUseCboRule(hintName, useRuleParameters, false)); + break; + case "no_use_cbo_rule": + List noUseRuleParameters = new ArrayList(); + for (HintAssignmentContext kv : hintStatement.parameters) { + String parameterName = visitIdentifierOrText(kv.key); + if (kv.key != null) { + noUseRuleParameters.add(parameterName); + } } - } - hints.put(hintName, new SelectHintUseCboRule(hintName, noUseRuleParameters, true)); - break; - default: - break; + hints.put(hintName, new SelectHintUseCboRule(hintName, noUseRuleParameters, true)); + break; + default: + break; + } } } return new LogicalSelectHint<>(hints, logicalPlan); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilderForCreateView.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilderForCreateView.java index 3989a8f89882ab..12c206dc0845a7 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilderForCreateView.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilderForCreateView.java @@ -52,10 +52,15 @@ import com.google.common.collect.ImmutableList; import org.antlr.v4.runtime.ParserRuleContext; +import java.util.Map; import java.util.Optional; /**LogicalPlanBuilderForCreateView*/ public class LogicalPlanBuilderForCreateView extends LogicalPlanBuilder { + public LogicalPlanBuilderForCreateView(Map selectHintMap) { + super(selectHintMap); + } + @Override protected LogicalPlan withGenerate(LogicalPlan plan, LateralViewContext ctx) { ConnectContext.get().getStatementContext().addIndexInSqlToString( diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilderForSyncMv.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilderForSyncMv.java index 4cdf33f9ff7655..840b9fdacc3642 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilderForSyncMv.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilderForSyncMv.java @@ -37,6 +37,7 @@ import org.apache.doris.nereids.util.PlanUtils; import com.google.common.collect.ImmutableList; +import org.antlr.v4.runtime.ParserRuleContext; import java.util.List; import java.util.Map; @@ -49,6 +50,10 @@ public class LogicalPlanBuilderForSyncMv extends LogicalPlanBuilder { private Optional querySql; + public LogicalPlanBuilderForSyncMv(Map selectHintMap) { + super(selectHintMap); + } + @Override public Expression visitFunctionCallExpression(DorisParser.FunctionCallExpressionContext ctx) { Expression expression = super.visitFunctionCallExpression(ctx); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/NereidsParser.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/NereidsParser.java index 0cfea1e8eb7c5e..3ba1e0e54316c7 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/NereidsParser.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/NereidsParser.java @@ -36,6 +36,7 @@ import org.apache.doris.qe.SessionVariable; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.ParserRuleContext; @@ -273,26 +274,56 @@ private T parse(String sql, @Nullable LogicalPlanBuilder logicalPlanBuilder, Function parseFunction) { ParserRuleContext tree = toAst(sql, parseFunction); LogicalPlanBuilder realLogicalPlanBuilder = logicalPlanBuilder == null - ? new LogicalPlanBuilder() : logicalPlanBuilder; + ? new LogicalPlanBuilder(getHintMap(sql, DorisParser::selectHint)) : logicalPlanBuilder; return (T) realLogicalPlanBuilder.visit(tree); } public LogicalPlan parseForCreateView(String sql) { ParserRuleContext tree = toAst(sql, DorisParser::singleStatement); - LogicalPlanBuilder realLogicalPlanBuilder = new LogicalPlanBuilderForCreateView(); + LogicalPlanBuilder realLogicalPlanBuilder = new LogicalPlanBuilderForCreateView( + getHintMap(sql, DorisParser::selectHint)); return (LogicalPlan) realLogicalPlanBuilder.visit(tree); } public Optional parseForSyncMv(String sql) { ParserRuleContext tree = toAst(sql, DorisParser::singleStatement); - LogicalPlanBuilderForSyncMv logicalPlanBuilderForSyncMv = new LogicalPlanBuilderForSyncMv(); + LogicalPlanBuilderForSyncMv logicalPlanBuilderForSyncMv = new LogicalPlanBuilderForSyncMv( + getHintMap(sql, DorisParser::selectHint)); logicalPlanBuilderForSyncMv.visit(tree); return logicalPlanBuilderForSyncMv.getQuerySql(); } + /** get hint map */ + public static Map getHintMap(String sql, + Function parseFunction) { + // parse hint first round + DorisLexer hintLexer = new DorisLexer(new CaseInsensitiveStream(CharStreams.fromString(sql))); + hintLexer.setChannel2(true); + CommonTokenStream hintTokenStream = new CommonTokenStream(hintLexer); + + Map selectHintMap = Maps.newHashMap(); + + Token hintToken = hintTokenStream.getTokenSource().nextToken(); + while (hintToken != null && hintToken.getType() != DorisLexer.EOF) { + int tokenType = hintToken.getType(); + if (tokenType == DorisLexer.HINT_WITH_CHANNEL) { + String hintSql = sql.substring(hintToken.getStartIndex(), hintToken.getStopIndex() + 1); + DorisLexer newHintLexer = new DorisLexer(new CaseInsensitiveStream(CharStreams.fromString(hintSql))); + newHintLexer.setChannel2(false); + CommonTokenStream newHintTokenStream = new CommonTokenStream(newHintLexer); + DorisParser hintParser = new DorisParser(newHintTokenStream); + ParserRuleContext hintContext = parseFunction.apply(hintParser); + selectHintMap.put(hintToken.getStartIndex(), hintContext); + } + hintToken = hintTokenStream.getTokenSource().nextToken(); + } + return selectHintMap; + } + /** toAst */ public static ParserRuleContext toAst(String sql, Function parseFunction) { DorisLexer lexer = new DorisLexer(new CaseInsensitiveStream(CharStreams.fromString(sql))); + lexer.setChannel2(true); CommonTokenStream tokenStream = new CommonTokenStream(lexer); DorisParser parser = new DorisParser(tokenStream); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/plsql/PLSqlLogicalPlanBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/plsql/PLSqlLogicalPlanBuilder.java index 5841c451dd1428..8825f5abd93a6c 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/plsql/PLSqlLogicalPlanBuilder.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/plsql/PLSqlLogicalPlanBuilder.java @@ -36,6 +36,10 @@ */ public class PLSqlLogicalPlanBuilder extends LogicalPlanBuilder { + public PLSqlLogicalPlanBuilder() { + super(null); + } + public List visitMultipartIdentifier(MultipartIdentifierContext ctx) { return ctx.parts.stream() .map(RuleContext::getText) diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java index f838b3006495e4..0907fec8f28e9f 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java @@ -352,16 +352,10 @@ public void testJoinHint() { parsePlan("select * from t1 join [broadcast] t2 on t1.keyy=t2.keyy") .matches(logicalJoin().when(j -> j.getDistributeHint().distributeType == DistributeType.BROADCAST_RIGHT)); - parsePlan("select * from t1 join /*+ broadcast */ t2 on t1.keyy=t2.keyy") - .matches(logicalJoin().when(j -> j.getDistributeHint().distributeType == DistributeType.BROADCAST_RIGHT)); - // invalid hint position parsePlan("select * from [shuffle] t1 join t2 on t1.keyy=t2.keyy") .assertThrowsExactly(ParseException.class); - parsePlan("select * from /*+ shuffle */ t1 join t2 on t1.keyy=t2.keyy") - .assertThrowsExactly(ParseException.class); - // invalid hint content parsePlan("select * from t1 join [bucket] t2 on t1.keyy=t2.keyy") .assertThrowsExactly(ParseException.class) @@ -372,8 +366,6 @@ public void testJoinHint() { + "----------------------^^^"); // invalid multiple hints - parsePlan("select * from t1 join /*+ shuffle , broadcast */ t2 on t1.keyy=t2.keyy") - .assertThrowsExactly(ParseException.class); parsePlan("select * from t1 join [shuffle,broadcast] t2 on t1.keyy=t2.keyy") .assertThrowsExactly(ParseException.class); diff --git a/regression-test/data/nereids_p0/hint/test_hint.out b/regression-test/data/nereids_p0/hint/test_hint.out new file mode 100644 index 00000000000000..66a218b09fe45d --- /dev/null +++ b/regression-test/data/nereids_p0/hint/test_hint.out @@ -0,0 +1,78 @@ +-- This file is automatically generated. You should know what you did if you want to edit this +-- !select1_1 -- +PhysicalResultSink +--hashAgg[GLOBAL] +----PhysicalDistribute[DistributionSpecGather] +------hashAgg[LOCAL] +--------hashJoin[INNER_JOIN broadcast] hashCondition=((t1.c1 = t2.c2)) otherCondition=() +----------PhysicalOlapScan[t2] +----------PhysicalOlapScan[t1] + +Hint log: +Used: leading(t2 broadcast t1 ) +UnUsed: +SyntaxError: + +-- !select1_2 -- +PhysicalResultSink +--hashAgg[GLOBAL] +----PhysicalDistribute[DistributionSpecGather] +------hashAgg[LOCAL] +--------PhysicalStorageLayerAggregate[t1] + +-- !select1_3 -- +PhysicalResultSink +--hashAgg[GLOBAL] +----PhysicalDistribute[DistributionSpecGather] +------hashAgg[LOCAL] +--------PhysicalStorageLayerAggregate[t1] + +-- !select1_4 -- +PhysicalResultSink +--hashAgg[GLOBAL] +----PhysicalDistribute[DistributionSpecGather] +------hashAgg[LOCAL] +--------PhysicalStorageLayerAggregate[t1] + +-- !select1_5 -- +PhysicalResultSink +--hashAgg[GLOBAL] +----PhysicalDistribute[DistributionSpecGather] +------hashAgg[LOCAL] +--------hashJoin[INNER_JOIN broadcast] hashCondition=((t1.c1 = t2.c2)) otherCondition=() +----------PhysicalOlapScan[t2] +----------PhysicalOlapScan[t1] + +Hint log: +Used: leading(t2 broadcast t1 ) +UnUsed: +SyntaxError: + +-- !select1_6 -- +PhysicalResultSink +--hashAgg[GLOBAL] +----PhysicalDistribute[DistributionSpecGather] +------hashAgg[LOCAL] +--------hashJoin[INNER_JOIN broadcast] hashCondition=((t1.c1 = t2.c2)) otherCondition=() +----------PhysicalOlapScan[t2] +----------PhysicalOlapScan[t1] + +Hint log: +Used: leading(t2 broadcast t1 ) +UnUsed: +SyntaxError: + +-- !select1_7 -- +PhysicalResultSink +--hashAgg[GLOBAL] +----PhysicalDistribute[DistributionSpecGather] +------hashAgg[LOCAL] +--------PhysicalStorageLayerAggregate[t1] + +-- !select1_8 -- +PhysicalResultSink +--hashAgg[GLOBAL] +----PhysicalDistribute[DistributionSpecGather] +------hashAgg[LOCAL] +--------PhysicalStorageLayerAggregate[t1] + diff --git a/regression-test/data/nereids_p0/hint/test_leading.out b/regression-test/data/nereids_p0/hint/test_leading.out index 45de51519048e4..7d397578838e25 100644 --- a/regression-test/data/nereids_p0/hint/test_leading.out +++ b/regression-test/data/nereids_p0/hint/test_leading.out @@ -2493,8 +2493,8 @@ PhysicalResultSink ----------PhysicalOlapScan[t3] Hint log: -Used: leading(t1 broadcast t2 t3 ) -UnUsed: +Used: leading(t1 broadcast t2 broadcast t3 ) +UnUsed: SyntaxError: -- !select95_4 -- diff --git a/regression-test/suites/nereids_p0/hint/test_hint.groovy b/regression-test/suites/nereids_p0/hint/test_hint.groovy new file mode 100644 index 00000000000000..d279b7c1a1d905 --- /dev/null +++ b/regression-test/suites/nereids_p0/hint/test_hint.groovy @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +suite("test_hint") { + // create database and tables + sql 'DROP DATABASE IF EXISTS test_hint' + sql 'CREATE DATABASE IF NOT EXISTS test_hint' + sql 'use test_hint' + + // setting planner to nereids + sql 'set exec_mem_limit=21G' + sql 'set be_number_for_test=1' + sql 'set parallel_pipeline_task_num=1' + sql "set disable_nereids_rules=PRUNE_EMPTY_PARTITION" + sql 'set enable_nereids_planner=true' + sql 'set enable_nereids_distribute_planner=false' + sql "set ignore_shape_nodes='PhysicalProject'" + sql 'set enable_fallback_to_original_planner=false' + sql 'set runtime_filter_mode=OFF' + + // create tables + sql """drop table if exists t1;""" + sql """drop table if exists t2;""" + + sql """create table t1 (c1 int, c11 int) distributed by hash(c1) buckets 3 properties('replication_num' = '1');""" + sql """create table t2 (c2 int, c22 int) distributed by hash(c2) buckets 3 properties('replication_num' = '1');""" + +// test hint positions, remove join in order to make sure shape stable when no use hint + qt_select1_1 """explain shape plan select /*+ leading(t2 broadcast t1) */ count(*) from t1 join t2 on c1 = c2;""" + + qt_select1_2 """explain shape plan /*+ leading(t2 broadcast t1) */ select count(*) from t1;""" + + qt_select1_3 """explain shape plan select /*+DBP: ROUTE={GROUP_ID(zjaq)}*/ count(*) from t1;""" + + qt_select1_4 """explain shape plan/*+DBP: ROUTE={GROUP_ID(zjaq)}*/ select count(*) from t1;""" + + qt_select1_5 """explain shape plan /*+ leading(t2 broadcast t1) */ select /*+ leading(t2 broadcast t1) */ count(*) from t1 join t2 on c1 = c2;""" + + qt_select1_6 """explain shape plan/*+DBP: ROUTE={GROUP_ID(zjaq)}*/ select /*+ leading(t2 broadcast t1) */ count(*) from t1 join t2 on c1 = c2;""" + + qt_select1_7 """explain shape plan /*+ leading(t2 broadcast t1) */ select /*+DBP: ROUTE={GROUP_ID(zjaq)}*/ count(*) from t1;""" + + qt_select1_8 """explain shape plan /*+DBP: ROUTE={GROUP_ID(zjaq)}*/ select /*+DBP: ROUTE={GROUP_ID(zjaq)}*/ count(*) from t1;""" + +} diff --git a/regression-test/suites/nereids_p0/hint/test_leading.groovy b/regression-test/suites/nereids_p0/hint/test_leading.groovy index 4a873afbf7336a..0013eb4e63c6cc 100644 --- a/regression-test/suites/nereids_p0/hint/test_leading.groovy +++ b/regression-test/suites/nereids_p0/hint/test_leading.groovy @@ -1001,7 +1001,7 @@ suite("test_leading") { qt_select94_2 """explain shape plan select /*+ leading(t2 shuffle {t3 t1}) */ count(*) from t1 join t2 on c1 = c2 join t3 on c2 = c3;""" // outer join - qt_select95_1 """explain shape plan select /*+ leading(t1 broadcast t2 t3) */ count(*) from t1 left outer join t2 on c1 = c2 join t3 on c2 = c3;""" + qt_select95_1 """explain shape plan select /*+ leading(t1 broadcast t2 broadcast t3) */ count(*) from t1 left outer join t2 on c1 = c2 join t3 on c2 = c3;""" explain { sql """shape plan select /*+ leading(t1 broadcast {t2 t3}) */ count(*) from t1 left outer join t2 on c1 = c2 join t3 on c2 = c3;""" contains("UnUsed: leading(t1 broadcast { t2 t3 })")