This is an automated email from the ASF dual-hosted git repository. yiguolei pushed a commit to branch branch-2.1 in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-2.1 by this push: new 9997911ec93 [feat](Nereids) support nereids hint position detaction (#39113) (#39417) 9997911ec93 is described below commit 9997911ec9302e96432e4980e22c7460134eea9e Author: LiBinfeng <46676950+libinfeng...@users.noreply.github.com> AuthorDate: Sat Aug 24 23:59:54 2024 +0800 [feat](Nereids) support nereids hint position detaction (#39113) (#39417) cherry-pick: #39113 When use hint in wrong position or use unsupport hint, use channel(2) to filter it out ## Proposed changes Issue Number: close #xxx <!--Describe your changes.--> --- .../antlr4/org/apache/doris/nereids/DorisLexer.g4 | 20 +---- .../antlr4/org/apache/doris/nereids/DorisParser.g4 | 5 +- .../doris/nereids/parser/LogicalPlanBuilder.java | 90 +++++++++++++--------- .../parser/LogicalPlanBuilderForCreateView.java | 5 ++ .../apache/doris/nereids/parser/NereidsParser.java | 30 +++++++- .../parser/plsql/PLSqlLogicalPlanBuilder.java | 4 + .../doris/nereids/parser/NereidsParserTest.java | 8 -- regression-test/data/nereids_p0/hint/test_hint.out | 81 +++++++++++++++++++ .../data/nereids_p0/hint/test_leading.out | 4 +- .../suites/nereids_p0/hint/test_hint.groovy | 60 +++++++++++++++ .../suites/nereids_p0/hint/test_leading.groovy | 2 +- 11 files changed, 242 insertions(+), 67 deletions(-) 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 72542ba0874..3ed51ca8fbf 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 @@ -47,22 +47,6 @@ lexer grammar DorisLexer; } } - /** - * This method will be called when we see '/*' and try to match it as a bracketed comment. - * If the next character is '+', it should be parsed as hint later, and we cannot match - * it as a bracketed comment. - * - * Returns true if the next character is '+'. - */ - public boolean isHint() { - int nextChar = _input.LA(1); - if (nextChar == '+') { - return true; - } else { - return false; - } - } - /** * This method will be called when the character stream ends and try to find out the * unclosed bracketed comment. @@ -599,6 +583,7 @@ COLON: ':'; ARROW: '->'; HINT_START: '/*+'; HINT_END: '*/'; +COMMENT_START: '/*'; ATSIGN: '@'; DOUBLEATSIGN: '@@'; @@ -678,9 +663,10 @@ SIMPLE_COMMENT ; BRACKETED_COMMENT - : '/*' {!isHint()}? ( BRACKETED_COMMENT | . )*? ('*/' | {markUnclosedComment();} EOF) -> channel(HIDDEN) + : COMMENT_START ( BRACKETED_COMMENT | . )*? ('*/' | {markUnclosedComment();} EOF) -> channel(2) ; + FROM_DUAL : 'FROM' WS+ 'DUAL' -> channel(HIDDEN); diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 index 33d04fb8550..d94a0f4f680 100644 --- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 +++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 @@ -457,7 +457,7 @@ havingClause : HAVING booleanExpression ; -selectHint: HINT_START hintStatements+=hintStatement (COMMA? hintStatements+=hintStatement)* HINT_END; +selectHint: hintStatements+=hintStatement (COMMA? hintStatements+=hintStatement)* HINT_END; hintStatement : hintName=identifier (LEFT_PAREN parameters+=hintAssignment (COMMA? parameters+=hintAssignment)* RIGHT_PAREN)? @@ -1065,6 +1065,7 @@ nonReserved | COLOCATE | COLUMNS | COMMENT + | COMMENT_START | COMMIT | COMMITTED | COMPACT @@ -1147,6 +1148,8 @@ nonReserved | HASH | HDFS | HELP + | HINT_END + | HINT_START | HISTOGRAM | HLL_UNION | HOSTNAME 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 ad2aa783307..c4403a98bd0 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 @@ -499,6 +499,12 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> { return pos1.getCharPositionInLine() - pos2.getCharPositionInLine(); }); + private final Map<Integer, ParserRuleContext> selectHintMap; + + public LogicalPlanBuilder(Map<Integer, ParserRuleContext> selectHintMap) { + this.selectHintMap = selectHintMap; + } + @SuppressWarnings("unchecked") protected <T> T typedVisit(ParseTree ctx) { return (T) ctx.accept(this); @@ -1331,7 +1337,16 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> { 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<ParserRuleContext> 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); }); } @@ -3068,47 +3083,50 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> { return last; } - private LogicalPlan withSelectHint(LogicalPlan logicalPlan, SelectHintContext hintContext) { - if (hintContext == null) { + private LogicalPlan withSelectHint(LogicalPlan logicalPlan, List<ParserRuleContext> hintContexts) { + if (hintContexts.isEmpty()) { return logicalPlan; } Map<String, SelectHint> hints = Maps.newLinkedHashMap(); - for (HintStatementContext hintStatement : hintContext.hintStatements) { - String hintName = hintStatement.hintName.getText().toLowerCase(Locale.ROOT); - switch (hintName) { - case "set_var": - Map<String, Optional<String>> parameters = Maps.newLinkedHashMap(); - for (HintAssignmentContext kv : hintStatement.parameters) { - if (kv.key != null) { - String parameterName = visitIdentifierOrText(kv.key); - Optional<String> 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<String, Optional<String>> parameters = Maps.newLinkedHashMap(); + for (HintAssignmentContext kv : hintStatement.parameters) { + if (kv.key != null) { + String parameterName = visitIdentifierOrText(kv.key); + Optional<String> 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<String> leadingParameters = new ArrayList<String>(); - 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<String> leadingParameters = new ArrayList<String>(); + 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; - default: - break; + hints.put(hintName, new SelectHintLeading(hintName, leadingParameters)); + break; + case "ordered": + hints.put(hintName, new SelectHintOrdered(hintName)); + 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 3989a8f8988..12c206dc084 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 org.apache.doris.qe.ConnectContext; 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<Integer, ParserRuleContext> 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/NereidsParser.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/NereidsParser.java index 58854b1a293..edcbb76ad84 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.ConnectContext; 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,16 +274,41 @@ public class NereidsParser { Function<DorisParser, ParserRuleContext> 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); } + /** get hint map */ + public static Map<Integer, ParserRuleContext> getHintMap(String sql, + Function<DorisParser, ParserRuleContext> parseFunction) { + // parse hint first round + DorisLexer hintLexer = new DorisLexer(new CaseInsensitiveStream(CharStreams.fromString(sql))); + CommonTokenStream hintTokenStream = new CommonTokenStream(hintLexer); + + Map<Integer, ParserRuleContext> selectHintMap = Maps.newHashMap(); + + Token hintToken = hintTokenStream.getTokenSource().nextToken(); + while (hintToken != null && hintToken.getType() != DorisLexer.EOF) { + if (hintToken.getChannel() == 2 && sql.charAt(hintToken.getStartIndex() + 2) == '+') { + String hintSql = sql.substring(hintToken.getStartIndex() + 3, hintToken.getStopIndex() + 1); + DorisLexer newHintLexer = new DorisLexer(new CaseInsensitiveStream(CharStreams.fromString(hintSql))); + 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<DorisParser, ParserRuleContext> parseFunction) { DorisLexer lexer = new DorisLexer(new CaseInsensitiveStream(CharStreams.fromString(sql))); 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 5841c451dd1..8825f5abd93 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 @@ import java.util.List; */ public class PLSqlLogicalPlanBuilder extends LogicalPlanBuilder { + public PLSqlLogicalPlanBuilder() { + super(null); + } + public List<String> 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 2bbcbc4290d..aa6ff756b20 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 @@ -351,16 +351,10 @@ public class NereidsParserTest extends ParserTestBase { 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) @@ -371,8 +365,6 @@ public class NereidsParserTest extends ParserTestBase { + "----------------------^^^"); // 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 00000000000..2145d4edae6 --- /dev/null +++ b/regression-test/data/nereids_p0/hint/test_hint.out @@ -0,0 +1,81 @@ +-- 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] hashCondition=((t1.c1 = t2.c2)) otherCondition=() +----------PhysicalOlapScan[t2] +----------PhysicalDistribute[DistributionSpecReplicated] +------------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] hashCondition=((t1.c1 = t2.c2)) otherCondition=() +----------PhysicalOlapScan[t2] +----------PhysicalDistribute[DistributionSpecReplicated] +------------PhysicalOlapScan[t1] + +Hint log: +Used: leading(t2 broadcast t1 ) +UnUsed: +SyntaxError: + +-- !select1_6 -- +PhysicalResultSink +--hashAgg[GLOBAL] +----PhysicalDistribute[DistributionSpecGather] +------hashAgg[LOCAL] +--------hashJoin[INNER_JOIN] hashCondition=((t1.c1 = t2.c2)) otherCondition=() +----------PhysicalOlapScan[t2] +----------PhysicalDistribute[DistributionSpecReplicated] +------------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 470dc086812..07cc7c16284 100644 --- a/regression-test/data/nereids_p0/hint/test_leading.out +++ b/regression-test/data/nereids_p0/hint/test_leading.out @@ -2538,8 +2538,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 00000000000..d847f17fe64 --- /dev/null +++ b/regression-test/suites/nereids_p0/hint/test_hint.groovy @@ -0,0 +1,60 @@ +/* + * 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 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 cbbff148c3a..ec7d568362e 100644 --- a/regression-test/suites/nereids_p0/hint/test_leading.groovy +++ b/regression-test/suites/nereids_p0/hint/test_leading.groovy @@ -1000,7 +1000,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 })") --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org