This is an automated email from the ASF dual-hosted git repository.

morrysnow pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new c0b39de61c [Feature](Nereids) Support join hint (#13601)
c0b39de61c is described below

commit c0b39de61c7fa93a89c463c9f5159703b8a2c82a
Author: Shuo Wang <wangshuo...@gmail.com>
AuthorDate: Wed Dec 21 21:09:13 2022 +0800

    [Feature](Nereids) Support join hint (#13601)
    
    Support join hint for nereids planner. Hints for broadcast and shuffle are 
supported by this PR.
---
 .../antlr4/org/apache/doris/nereids/DorisParser.g4 |   8 +-
 .../org/apache/doris/nereids/NereidsPlanner.java   |  20 +++
 .../doris/nereids/parser/LogicalPlanBuilder.java   |  29 +++-
 .../nereids/properties/RequestPropertyDeriver.java |  48 +++++--
 .../doris/nereids/rules/analysis/BindFunction.java |   1 +
 .../nereids/rules/analysis/BindSlotReference.java  |   4 +-
 .../rules/exploration/join/InnerJoinLAsscom.java   |   6 +-
 .../exploration/join/InnerJoinLAsscomProject.java  |   6 +-
 .../exploration/join/InnerJoinLeftAssociate.java   |   6 +-
 .../exploration/join/InnerJoinRightAssociate.java  |   6 +-
 .../rules/exploration/join/JoinCommute.java        |   3 +
 .../rules/exploration/join/JoinExchange.java       |   8 +-
 .../rules/exploration/join/OuterJoinLAsscom.java   |   6 +-
 .../exploration/join/OuterJoinLAsscomProject.java  |   6 +-
 .../join/SemiJoinLogicalJoinTranspose.java         |  13 +-
 .../join/SemiJoinLogicalJoinTransposeProject.java  |  10 +-
 .../join/SemiJoinSemiJoinTranspose.java            |   5 +-
 .../join/SemiJoinSemiJoinTransposeProject.java     |   6 +-
 .../expression/rewrite/ExpressionRewrite.java      |   2 +-
 .../implementation/LogicalJoinToHashJoin.java      |   1 +
 .../rules/rewrite/logical/ExistsApplyToJoin.java   |   3 +
 .../rewrite/logical/FindHashConditionForJoin.java  |   1 +
 .../rules/rewrite/logical/InApplyToJoin.java       |   3 +
 .../rewrite/logical/PushFilterInsideJoin.java      |   2 +-
 .../rewrite/logical/PushdownFilterThroughJoin.java |   1 +
 .../logical/PushdownJoinOtherCondition.java        |   2 +-
 .../nereids/rules/rewrite/logical/ReorderJoin.java |  36 +++--
 .../rules/rewrite/logical/ScalarApplyToJoin.java   |   2 +
 .../apache/doris/nereids/trees/plans/JoinHint.java |  72 ++++++++++
 .../doris/nereids/trees/plans/algebra/Join.java    |  26 ++++
 .../nereids/trees/plans/logical/LogicalJoin.java   | 106 ++++++++++-----
 .../trees/plans/physical/AbstractPhysicalJoin.java |  37 +++++-
 .../trees/plans/physical/PhysicalHashJoin.java     |  56 +++++---
 .../plans/physical/PhysicalNestedLoopJoin.java     |  41 ++++--
 .../org/apache/doris/nereids/JoinHintTest.java     | 148 +++++++++++++++++++++
 .../doris/nereids/parser/NereidsParserTest.java    |  44 ++++++
 .../properties/ChildOutputPropertyDeriverTest.java |  11 +-
 .../properties/RequestPropertyDeriverTest.java     |  10 +-
 .../logical/FindHashConditionForJoinTest.java      |   3 +-
 .../logical/PushdownJoinOtherConditionTest.java    |   8 +-
 .../doris/nereids/trees/plans/PlanEqualsTest.java  |   6 +-
 .../doris/nereids/util/ExceptionChecker.java       |   2 +-
 .../doris/nereids/util/LogicalPlanBuilder.java     |   3 +-
 ...{GroupMatchingUtils.java => MatchingUtils.java} |  30 ++++-
 .../org/apache/doris/nereids/util/PlanChecker.java |  15 ++-
 .../doris/nereids/util/PlanParseChecker.java       |   2 +-
 .../account_p0/test_nereids_authentication.groovy  |  12 +-
 47 files changed, 723 insertions(+), 153 deletions(-)

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 fc226c46bc..e17a593648 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
@@ -128,7 +128,13 @@ relation
     ;
 
 joinRelation
-    : (joinType) JOIN right=relationPrimary joinCriteria?
+    : (joinType) JOIN joinHint? right=relationPrimary joinCriteria?
+    ;
+
+// Just like `opt_plan_hints` in legacy CUP parser.
+joinHint
+    : LEFT_BRACKET identifier RIGHT_BRACKET                           
#bracketStyleHint
+    | HINT_START identifier HINT_END                                  
#commentStyleHint
     ;
 
 aggClause
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java
index 03847cc6e8..06b0a05d29 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java
@@ -354,4 +354,24 @@ public class NereidsPlanner extends Planner {
         ExplainLevel explainLevel = explainOptions.getExplainLevel();
         return explainLevel == null ? ExplainLevel.NONE : explainLevel;
     }
+
+    @VisibleForTesting
+    public Plan getParsedPlan() {
+        return parsedPlan;
+    }
+
+    @VisibleForTesting
+    public Plan getAnalyzedPlan() {
+        return analyzedPlan;
+    }
+
+    @VisibleForTesting
+    public Plan getRewrittenPlan() {
+        return rewrittenPlan;
+    }
+
+    @VisibleForTesting
+    public Plan getOptimizedPlan() {
+        return optimizedPlan;
+    }
 }
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 5510cb493c..a7f5fd915c 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
@@ -27,7 +27,9 @@ import 
org.apache.doris.nereids.DorisParser.AliasedRelationContext;
 import org.apache.doris.nereids.DorisParser.ArithmeticBinaryContext;
 import org.apache.doris.nereids.DorisParser.ArithmeticUnaryContext;
 import org.apache.doris.nereids.DorisParser.BooleanLiteralContext;
+import org.apache.doris.nereids.DorisParser.BracketStyleHintContext;
 import org.apache.doris.nereids.DorisParser.ColumnReferenceContext;
+import org.apache.doris.nereids.DorisParser.CommentStyleHintContext;
 import org.apache.doris.nereids.DorisParser.ComparisonContext;
 import org.apache.doris.nereids.DorisParser.CreateRowPolicyContext;
 import org.apache.doris.nereids.DorisParser.CteContext;
@@ -168,6 +170,7 @@ import 
org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.SmallIntLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.TinyIntLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.algebra.Aggregate;
@@ -976,6 +979,7 @@ public class LogicalPlanBuilder extends 
DorisParserBaseVisitor<Object> {
                                 JoinType.CROSS_JOIN,
                                 ExpressionUtils.EMPTY_CONDITION,
                                 ExpressionUtils.EMPTY_CONDITION,
+                                JoinHint.NONE,
                                 left,
                                 right);
                 left = withJoinRelations(left, relation);
@@ -1219,10 +1223,23 @@ public class LogicalPlanBuilder extends 
DorisParserBaseVisitor<Object> {
                 }
             }
 
+            JoinHint joinHint = 
Optional.ofNullable(join.joinHint()).map(hintCtx -> {
+                String hint = typedVisit(join.joinHint());
+                if 
(JoinHint.JoinHintType.SHUFFLE.toString().equalsIgnoreCase(hint)) {
+                    return JoinHint.SHUFFLE_RIGHT;
+                } else if 
(JoinHint.JoinHintType.BROADCAST.toString().equalsIgnoreCase(hint)) {
+                    return JoinHint.BROADCAST_RIGHT;
+                } else {
+                    throw new ParseException("Invalid join hint: " + hint, 
hintCtx);
+                }
+            }).orElse(JoinHint.NONE);
+
             last = new LogicalJoin<>(joinType, ExpressionUtils.EMPTY_CONDITION,
                     condition.map(ExpressionUtils::extractConjunction)
                             .orElse(ExpressionUtils.EMPTY_CONDITION),
-                    last, plan(join.relationPrimary()));
+                    joinHint,
+                    last,
+                    plan(join.relationPrimary()));
         }
         return last;
     }
@@ -1252,6 +1269,16 @@ public class LogicalPlanBuilder extends 
DorisParserBaseVisitor<Object> {
         return new LogicalSelectHint<>(hints, logicalPlan);
     }
 
+    @Override
+    public String visitBracketStyleHint(BracketStyleHintContext ctx) {
+        return ctx.identifier().getText();
+    }
+
+    @Override
+    public Object visitCommentStyleHint(CommentStyleHintContext ctx) {
+        return ctx.identifier().getText();
+    }
+
     private LogicalPlan withProjection(LogicalPlan input, 
SelectColumnClauseContext selectCtx,
                                        Optional<AggClauseContext> aggCtx) {
         return ParserUtils.withOrigin(selectCtx, () -> {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/RequestPropertyDeriver.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/RequestPropertyDeriver.java
index 1b8f29fa91..99c5c197bc 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/RequestPropertyDeriver.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/RequestPropertyDeriver.java
@@ -27,6 +27,7 @@ import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.SlotReference;
 import 
org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalAssertNumRows;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalHashJoin;
@@ -107,24 +108,43 @@ public class RequestPropertyDeriver extends 
PlanVisitor<Void, PlanContext> {
 
     @Override
     public Void visitPhysicalHashJoin(PhysicalHashJoin<? extends Plan, ? 
extends Plan> hashJoin, PlanContext context) {
-        // for shuffle join
-        if (JoinUtils.couldShuffle(hashJoin)) {
-            Pair<List<ExprId>, List<ExprId>> onClauseUsedSlots = 
JoinUtils.getOnClauseUsedSlots(hashJoin);
-            // shuffle join
-            addRequestPropertyToChildren(
-                    PhysicalProperties.createHash(
-                            new DistributionSpecHash(onClauseUsedSlots.first, 
ShuffleType.JOIN)),
-                    PhysicalProperties.createHash(
-                            new DistributionSpecHash(onClauseUsedSlots.second, 
ShuffleType.JOIN)));
-        }
-        // for broadcast join
-        if (JoinUtils.couldBroadcast(hashJoin)) {
-            addRequestPropertyToChildren(PhysicalProperties.ANY, 
PhysicalProperties.REPLICATED);
-        }
+        JoinHint hint = hashJoin.getHint();
+        switch (hint) {
+            case BROADCAST_RIGHT:
+                addBroadcastJoinRequestProperty();
+                break;
+            case SHUFFLE_RIGHT:
+                addShuffleJoinRequestProperty(hashJoin);
+                break;
+            case NONE:
+            default:
+                // for shuffle join
+                if (JoinUtils.couldShuffle(hashJoin)) {
+                    addShuffleJoinRequestProperty(hashJoin);
+                }
+                // for broadcast join
+                if (JoinUtils.couldBroadcast(hashJoin)) {
+                    addRequestPropertyToChildren(PhysicalProperties.ANY, 
PhysicalProperties.REPLICATED);
+                }
 
+        }
         return null;
     }
 
+    private void addBroadcastJoinRequestProperty() {
+        addRequestPropertyToChildren(PhysicalProperties.ANY, 
PhysicalProperties.REPLICATED);
+    }
+
+    private void addShuffleJoinRequestProperty(PhysicalHashJoin<? extends 
Plan, ? extends Plan> hashJoin) {
+        Pair<List<ExprId>, List<ExprId>> onClauseUsedSlots = 
JoinUtils.getOnClauseUsedSlots(hashJoin);
+        // shuffle join
+        addRequestPropertyToChildren(
+                PhysicalProperties.createHash(
+                        new DistributionSpecHash(onClauseUsedSlots.first, 
ShuffleType.JOIN)),
+                PhysicalProperties.createHash(
+                        new DistributionSpecHash(onClauseUsedSlots.second, 
ShuffleType.JOIN)));
+    }
+
     @Override
     public Void visitPhysicalNestedLoopJoin(
             PhysicalNestedLoopJoin<? extends Plan, ? extends Plan> 
nestedLoopJoin, PlanContext context) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindFunction.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindFunction.java
index 5935c76032..61e46c7f68 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindFunction.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindFunction.java
@@ -130,6 +130,7 @@ public class BindFunction implements AnalysisRuleFactory {
                     List<Expression> hashConjuncts = 
bind(join.getHashJoinConjuncts(), ctx.connectContext.getEnv());
                     List<Expression> otherConjuncts = 
bind(join.getOtherJoinConjuncts(), ctx.connectContext.getEnv());
                     return new LogicalJoin<>(join.getJoinType(), 
hashConjuncts, otherConjuncts,
+                            join.getHint(),
                             join.left(), join.right());
                 })
             ),
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSlotReference.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSlotReference.java
index b434737224..12f107c369 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSlotReference.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSlotReference.java
@@ -133,7 +133,7 @@ public class BindSlotReference implements 
AnalysisRuleFactory {
                                         .map(expr -> bind(expr, 
join.children(), join, ctx.cascadesContext))
                                         .collect(Collectors.toList());
                                 return new LogicalJoin<>(join.getJoinType(),
-                                        hashJoinConjuncts, cond, join.left(), 
join.right());
+                                        hashJoinConjuncts, cond, 
join.getHint(), join.left(), join.right());
                             })
             ),
             RuleType.BINDING_USING_JOIN_SLOT.build(
@@ -154,7 +154,7 @@ public class BindSlotReference implements 
AnalysisRuleFactory {
                             hashEqExpr.add(new EqualTo(leftSlots.get(i), 
rightSlots.get(i)));
                         }
                         return new LogicalJoin(JoinType.INNER_JOIN, hashEqExpr,
-                                join.getOtherJoinConjuncts(), join.left(), 
join.right());
+                                join.getOtherJoinConjuncts(), join.getHint(), 
join.left(), join.right());
                     })
                 ),
             RuleType.BINDING_AGGREGATE_SLOT.build(
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/InnerJoinLAsscom.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/InnerJoinLAsscom.java
index 1910ee78d0..37984db597 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/InnerJoinLAsscom.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/InnerJoinLAsscom.java
@@ -23,6 +23,7 @@ import 
org.apache.doris.nereids.rules.exploration.OneExplorationRuleFactory;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
@@ -52,6 +53,7 @@ public class InnerJoinLAsscom extends 
OneExplorationRuleFactory {
     public Rule build() {
         return innerLogicalJoin(innerLogicalJoin(), group())
                 .when(topJoin -> checkReorder(topJoin, topJoin.left()))
+                .whenNot(join -> join.hasJoinHint() || 
join.left().hasJoinHint())
                 .then(topJoin -> {
                     LogicalJoin<GroupPlan, GroupPlan> bottomJoin = 
topJoin.left();
                     GroupPlan a = bottomJoin.left();
@@ -76,13 +78,13 @@ public class InnerJoinLAsscom extends 
OneExplorationRuleFactory {
                     List<Expression> newBottomOtherConjuncts = 
splitOtherConjunts.get(false);
 
                     LogicalJoin<GroupPlan, GroupPlan> newBottomJoin = new 
LogicalJoin<>(JoinType.INNER_JOIN,
-                            newBottomHashConjuncts, newBottomOtherConjuncts,
+                            newBottomHashConjuncts, newBottomOtherConjuncts, 
JoinHint.NONE,
                             a, c, bottomJoin.getJoinReorderContext());
                     newBottomJoin.getJoinReorderContext().setHasLAsscom(false);
                     newBottomJoin.getJoinReorderContext().setHasCommute(false);
 
                     LogicalJoin<LogicalJoin<GroupPlan, GroupPlan>, GroupPlan> 
newTopJoin = new LogicalJoin<>(
-                            JoinType.INNER_JOIN, newTopHashConjuncts, 
newTopOtherConjuncts,
+                            JoinType.INNER_JOIN, newTopHashConjuncts, 
newTopOtherConjuncts, JoinHint.NONE,
                             newBottomJoin, b, topJoin.getJoinReorderContext());
                     newTopJoin.getJoinReorderContext().setHasLAsscom(true);
 
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/InnerJoinLAsscomProject.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/InnerJoinLAsscomProject.java
index 21b28acfa4..67488a0a16 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/InnerJoinLAsscomProject.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/InnerJoinLAsscomProject.java
@@ -27,6 +27,7 @@ import 
org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
 import org.apache.doris.nereids.util.ExpressionUtils;
@@ -62,6 +63,7 @@ public class InnerJoinLAsscomProject extends 
OneExplorationRuleFactory {
     public Rule build() {
         return innerLogicalJoin(logicalProject(innerLogicalJoin()), group())
                 .when(topJoin -> InnerJoinLAsscom.checkReorder(topJoin, 
topJoin.left().child()))
+                .whenNot(join -> join.hasJoinHint() || 
join.left().child().hasJoinHint())
                 .then(topJoin -> {
 
                     /* ********** init ********** */
@@ -151,7 +153,7 @@ public class InnerJoinLAsscomProject extends 
OneExplorationRuleFactory {
 
                     /* ********** new Plan ********** */
                     LogicalJoin<GroupPlan, GroupPlan> newBottomJoin = new 
LogicalJoin<>(topJoin.getJoinType(),
-                            newBottomHashJoinConjuncts, 
newBottomOtherJoinConjuncts,
+                            newBottomHashJoinConjuncts, 
newBottomOtherJoinConjuncts, JoinHint.NONE,
                             a, c, bottomJoin.getJoinReorderContext());
                     newBottomJoin.getJoinReorderContext().setHasLAsscom(false);
                     newBottomJoin.getJoinReorderContext().setHasCommute(false);
@@ -168,7 +170,7 @@ public class InnerJoinLAsscomProject extends 
OneExplorationRuleFactory {
                     }
 
                     LogicalJoin<Plan, Plan> newTopJoin = new 
LogicalJoin<>(bottomJoin.getJoinType(),
-                            newTopHashJoinConjuncts, newTopOtherJoinConjuncts,
+                            newTopHashJoinConjuncts, newTopOtherJoinConjuncts, 
JoinHint.NONE,
                             left, right, topJoin.getJoinReorderContext());
                     newTopJoin.getJoinReorderContext().setHasLAsscom(true);
 
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/InnerJoinLeftAssociate.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/InnerJoinLeftAssociate.java
index 118fc2aa3c..8fb969daa6 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/InnerJoinLeftAssociate.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/InnerJoinLeftAssociate.java
@@ -23,6 +23,7 @@ import 
org.apache.doris.nereids.rules.exploration.OneExplorationRuleFactory;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
@@ -53,6 +54,7 @@ public class InnerJoinLeftAssociate extends 
OneExplorationRuleFactory {
     public Rule build() {
         return innerLogicalJoin(group(), innerLogicalJoin())
                 .when(InnerJoinLeftAssociate::checkReorder)
+                .whenNot(join -> join.hasJoinHint() || 
join.right().hasJoinHint())
                 .then(topJoin -> {
                     LogicalJoin<GroupPlan, GroupPlan> bottomJoin = 
topJoin.right();
                     GroupPlan a = topJoin.left();
@@ -89,7 +91,7 @@ public class InnerJoinLeftAssociate extends 
OneExplorationRuleFactory {
 
                     // new join.
                     LogicalJoin<GroupPlan, GroupPlan> newBottomJoin = new 
LogicalJoin<>(JoinType.INNER_JOIN,
-                            newBottomHashJoinConjuncts, 
newBottomOtherJoinConjuncts,
+                            newBottomHashJoinConjuncts, 
newBottomOtherJoinConjuncts, JoinHint.NONE,
                             a, b, bottomJoin.getJoinReorderContext());
                     newBottomJoin.getJoinReorderContext().setHasCommute(false);
                     
newBottomJoin.getJoinReorderContext().setHasRightAssociate(false);
@@ -97,7 +99,7 @@ public class InnerJoinLeftAssociate extends 
OneExplorationRuleFactory {
                     
newBottomJoin.getJoinReorderContext().setHasExchange(false);
 
                     LogicalJoin<LogicalJoin<GroupPlan, GroupPlan>, GroupPlan> 
newTopJoin = new LogicalJoin<>(
-                            JoinType.INNER_JOIN, newTopHashJoinConjuncts, 
newTopOtherJoinConjuncts,
+                            JoinType.INNER_JOIN, newTopHashJoinConjuncts, 
newTopOtherJoinConjuncts, JoinHint.NONE,
                             newBottomJoin, c, topJoin.getJoinReorderContext());
                     
newTopJoin.getJoinReorderContext().setHasLeftAssociate(true);
                     newTopJoin.getJoinReorderContext().setHasCommute(false);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/InnerJoinRightAssociate.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/InnerJoinRightAssociate.java
index caa16258b2..ded9e6bb98 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/InnerJoinRightAssociate.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/InnerJoinRightAssociate.java
@@ -23,6 +23,7 @@ import 
org.apache.doris.nereids.rules.exploration.OneExplorationRuleFactory;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
@@ -52,6 +53,7 @@ public class InnerJoinRightAssociate extends 
OneExplorationRuleFactory {
     public Rule build() {
         return innerLogicalJoin(innerLogicalJoin(), group())
                 .when(InnerJoinRightAssociate::checkReorder)
+                .whenNot(join -> join.hasJoinHint() || 
join.left().hasJoinHint())
                 .then(topJoin -> {
                     LogicalJoin<GroupPlan, GroupPlan> bottomJoin = 
topJoin.left();
                     GroupPlan a = bottomJoin.left();
@@ -87,7 +89,7 @@ public class InnerJoinRightAssociate extends 
OneExplorationRuleFactory {
                     List<Expression> newTopOtherJoinConjuncts = 
otherConjunctsSplit.get(false);
 
                     LogicalJoin<GroupPlan, GroupPlan> newBottomJoin = new 
LogicalJoin<>(JoinType.INNER_JOIN,
-                            newBottomHashJoinConjuncts, 
newBottomOtherJoinConjuncts,
+                            newBottomHashJoinConjuncts, 
newBottomOtherJoinConjuncts, JoinHint.NONE,
                             b, c, bottomJoin.getJoinReorderContext());
                     newBottomJoin.getJoinReorderContext().setHasCommute(false);
                     
newBottomJoin.getJoinReorderContext().setHasRightAssociate(false);
@@ -95,7 +97,7 @@ public class InnerJoinRightAssociate extends 
OneExplorationRuleFactory {
                     
newBottomJoin.getJoinReorderContext().setHasExchange(false);
 
                     LogicalJoin<GroupPlan, LogicalJoin<GroupPlan, GroupPlan>> 
newTopJoin = new LogicalJoin<>(
-                            JoinType.INNER_JOIN, newTopHashJoinConjuncts, 
newTopOtherJoinConjuncts,
+                            JoinType.INNER_JOIN, newTopHashJoinConjuncts, 
newTopOtherJoinConjuncts, JoinHint.NONE,
                             a, newBottomJoin, topJoin.getJoinReorderContext());
                     
newTopJoin.getJoinReorderContext().setHasRightAssociate(true);
                     newTopJoin.getJoinReorderContext().setHasCommute(false);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinCommute.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinCommute.java
index 6705c74fbc..0dfa60dd05 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinCommute.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinCommute.java
@@ -22,6 +22,7 @@ import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.rules.exploration.OneExplorationRuleFactory;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
 
 import java.util.List;
@@ -45,11 +46,13 @@ public class JoinCommute extends OneExplorationRuleFactory {
     public Rule build() {
         return logicalJoin()
                 .when(join -> check(swapType, join))
+                .whenNot(LogicalJoin::hasJoinHint)
                 .then(join -> {
                     LogicalJoin<GroupPlan, GroupPlan> newJoin = new 
LogicalJoin<>(
                             join.getJoinType().swap(),
                             join.getHashJoinConjuncts(),
                             join.getOtherJoinConjuncts(),
+                            JoinHint.NONE,
                             join.right(), join.left(),
                             join.getJoinReorderContext());
                     newJoin.getJoinReorderContext().setHasCommute(true);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinExchange.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinExchange.java
index f2b221dc27..0ae9e30030 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinExchange.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinExchange.java
@@ -24,6 +24,7 @@ import 
org.apache.doris.nereids.rules.exploration.OneExplorationRuleFactory;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
@@ -53,6 +54,7 @@ public class JoinExchange extends OneExplorationRuleFactory {
     public Rule build() {
         return innerLogicalJoin(innerLogicalJoin(), innerLogicalJoin())
                 .when(JoinExchange::checkReorder)
+                .whenNot(join -> join.hasJoinHint() || 
join.left().hasJoinHint() || join.right().hasJoinHint())
                 .then(topJoin -> {
                     LogicalJoin<GroupPlan, GroupPlan> leftJoin = 
topJoin.left();
                     LogicalJoin<GroupPlan, GroupPlan> rightJoin = 
topJoin.right();
@@ -83,7 +85,7 @@ public class JoinExchange extends OneExplorationRuleFactory {
                         return null;
                     }
                     LogicalJoin<GroupPlan, GroupPlan> newLeftJoin = new 
LogicalJoin<>(JoinType.INNER_JOIN,
-                            newLeftJoinHashJoinConjuncts, 
newLeftJoinOtherJoinConjuncts,
+                            newLeftJoinHashJoinConjuncts, 
newLeftJoinOtherJoinConjuncts, JoinHint.NONE,
                             a, c, leftJoin.getJoinReorderContext());
                     newLeftJoin.getJoinReorderContext().setHasCommute(false);
                     
newLeftJoin.getJoinReorderContext().setHasLeftAssociate(false);
@@ -91,7 +93,7 @@ public class JoinExchange extends OneExplorationRuleFactory {
                     newLeftJoin.getJoinReorderContext().setHasExchange(false);
 
                     LogicalJoin<GroupPlan, GroupPlan> newRightJoin = new 
LogicalJoin<>(JoinType.INNER_JOIN,
-                            newRightJoinHashJoinConjuncts, 
newRightJoinOtherJoinConjuncts,
+                            newRightJoinHashJoinConjuncts, 
newRightJoinOtherJoinConjuncts, JoinHint.NONE,
                             b, d, rightJoin.getJoinReorderContext());
                     newRightJoin.getJoinReorderContext().setHasCommute(false);
                     
newRightJoin.getJoinReorderContext().setHasLeftAssociate(false);
@@ -100,7 +102,7 @@ public class JoinExchange extends OneExplorationRuleFactory 
{
 
                     LogicalJoin<LogicalJoin<GroupPlan, GroupPlan>, 
LogicalJoin<GroupPlan, GroupPlan>>
                             newTopJoin = new LogicalJoin<>(JoinType.INNER_JOIN,
-                            newTopJoinHashJoinConjuncts, 
newTopJoinOtherJoinConjuncts,
+                            newTopJoinHashJoinConjuncts, 
newTopJoinOtherJoinConjuncts, JoinHint.NONE,
                             newLeftJoin, newRightJoin, 
topJoin.getJoinReorderContext());
                     newTopJoin.getJoinReorderContext().setHasExchange(true);
 
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/OuterJoinLAsscom.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/OuterJoinLAsscom.java
index 348d8d0dd7..0abdc66dae 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/OuterJoinLAsscom.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/OuterJoinLAsscom.java
@@ -24,6 +24,7 @@ import 
org.apache.doris.nereids.rules.exploration.OneExplorationRuleFactory;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
@@ -62,6 +63,7 @@ public class OuterJoinLAsscom extends 
OneExplorationRuleFactory {
                 .when(join -> 
VALID_TYPE_PAIR_SET.contains(Pair.of(join.left().getJoinType(), 
join.getJoinType())))
                 .when(topJoin -> checkReorder(topJoin, topJoin.left()))
                 .when(topJoin -> checkCondition(topJoin, 
topJoin.left().right().getOutputSet()))
+                .whenNot(join -> join.hasJoinHint() || 
join.left().hasJoinHint())
                 .then(topJoin -> {
                     LogicalJoin<GroupPlan, GroupPlan> bottomJoin = 
topJoin.left();
                     GroupPlan a = bottomJoin.left();
@@ -69,14 +71,14 @@ public class OuterJoinLAsscom extends 
OneExplorationRuleFactory {
                     GroupPlan c = topJoin.right();
 
                     LogicalJoin<GroupPlan, GroupPlan> newBottomJoin = new 
LogicalJoin<>(topJoin.getJoinType(),
-                            topJoin.getHashJoinConjuncts(), 
topJoin.getOtherJoinConjuncts(),
+                            topJoin.getHashJoinConjuncts(), 
topJoin.getOtherJoinConjuncts(), JoinHint.NONE,
                             a, c, bottomJoin.getJoinReorderContext());
                     newBottomJoin.getJoinReorderContext().setHasLAsscom(false);
                     newBottomJoin.getJoinReorderContext().setHasCommute(false);
 
                     LogicalJoin<LogicalJoin<GroupPlan, GroupPlan>, GroupPlan> 
newTopJoin = new LogicalJoin<>(
                             bottomJoin.getJoinType(),
-                            bottomJoin.getHashJoinConjuncts(), 
bottomJoin.getOtherJoinConjuncts(),
+                            bottomJoin.getHashJoinConjuncts(), 
bottomJoin.getOtherJoinConjuncts(), JoinHint.NONE,
                             newBottomJoin, b, topJoin.getJoinReorderContext());
                     newTopJoin.getJoinReorderContext().setHasLAsscom(true);
 
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/OuterJoinLAsscomProject.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/OuterJoinLAsscomProject.java
index 239aeaf489..a1187f1098 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/OuterJoinLAsscomProject.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/OuterJoinLAsscomProject.java
@@ -28,6 +28,7 @@ import 
org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
 import org.apache.doris.nereids.util.JoinUtils;
@@ -64,6 +65,7 @@ public class OuterJoinLAsscomProject extends 
OneExplorationRuleFactory {
                 .when(join -> OuterJoinLAsscom.VALID_TYPE_PAIR_SET.contains(
                         Pair.of(join.left().child().getJoinType(), 
join.getJoinType())))
                 .when(topJoin -> OuterJoinLAsscom.checkReorder(topJoin, 
topJoin.left().child()))
+                .whenNot(join -> join.hasJoinHint() || 
join.left().child().hasJoinHint())
                 .then(topJoin -> {
 
                     /* ********** init ********** */
@@ -168,7 +170,7 @@ public class OuterJoinLAsscomProject extends 
OneExplorationRuleFactory {
 
                     /* ********** new Plan ********** */
                     LogicalJoin<GroupPlan, GroupPlan> newBottomJoin = new 
LogicalJoin<>(topJoin.getJoinType(),
-                            newBottomHashJoinConjuncts, 
newBottomOtherJoinConjuncts,
+                            newBottomHashJoinConjuncts, 
newBottomOtherJoinConjuncts, JoinHint.NONE,
                             a, c, bottomJoin.getJoinReorderContext());
                     newBottomJoin.getJoinReorderContext().setHasLAsscom(false);
                     newBottomJoin.getJoinReorderContext().setHasCommute(false);
@@ -185,7 +187,7 @@ public class OuterJoinLAsscomProject extends 
OneExplorationRuleFactory {
                     }
 
                     LogicalJoin<Plan, Plan> newTopJoin = new 
LogicalJoin<>(bottomJoin.getJoinType(),
-                            newTopHashJoinConjuncts, newTopOtherJoinConjuncts,
+                            newTopHashJoinConjuncts, newTopOtherJoinConjuncts, 
JoinHint.NONE,
                             left, right, topJoin.getJoinReorderContext());
                     newTopJoin.getJoinReorderContext().setHasLAsscom(true);
 
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/SemiJoinLogicalJoinTranspose.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/SemiJoinLogicalJoinTranspose.java
index 4a12d5d836..be0508336a 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/SemiJoinLogicalJoinTranspose.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/SemiJoinLogicalJoinTranspose.java
@@ -23,6 +23,7 @@ import 
org.apache.doris.nereids.rules.exploration.OneExplorationRuleFactory;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
 import org.apache.doris.nereids.util.ExpressionUtils;
@@ -57,6 +58,7 @@ public class SemiJoinLogicalJoinTranspose extends 
OneExplorationRuleFactory {
                         || topJoin.getJoinType() == JoinType.LEFT_ANTI_JOIN)
                 .whenNot(topJoin -> 
topJoin.left().getJoinType().isSemiOrAntiJoin())
                 .when(this::conditionChecker)
+                .whenNot(topJoin -> topJoin.hasJoinHint() || 
topJoin.left().hasJoinHint())
                 .then(topSemiJoin -> {
                     LogicalJoin<GroupPlan, GroupPlan> bottomJoin = 
topSemiJoin.left();
                     GroupPlan a = bottomJoin.left();
@@ -82,9 +84,10 @@ public class SemiJoinLogicalJoinTranspose extends 
OneExplorationRuleFactory {
                          */
                         LogicalJoin<GroupPlan, GroupPlan> newBottomSemiJoin = 
new LogicalJoin<>(
                                 topSemiJoin.getJoinType(),
-                                topSemiJoin.getHashJoinConjuncts(), 
topSemiJoin.getOtherJoinConjuncts(), a, c);
+                                topSemiJoin.getHashJoinConjuncts(), 
topSemiJoin.getOtherJoinConjuncts(), JoinHint.NONE,
+                                a, c);
                         return new LogicalJoin<>(bottomJoin.getJoinType(), 
bottomJoin.getHashJoinConjuncts(),
-                                bottomJoin.getOtherJoinConjuncts(), 
newBottomSemiJoin, b);
+                                bottomJoin.getOtherJoinConjuncts(), 
JoinHint.NONE, newBottomSemiJoin, b);
                     } else {
                         /*
                          *    topSemiJoin            newTopJoin
@@ -95,9 +98,11 @@ public class SemiJoinLogicalJoinTranspose extends 
OneExplorationRuleFactory {
                          */
                         LogicalJoin<GroupPlan, GroupPlan> newBottomSemiJoin = 
new LogicalJoin<>(
                                 topSemiJoin.getJoinType(),
-                                topSemiJoin.getHashJoinConjuncts(), 
topSemiJoin.getOtherJoinConjuncts(), b, c);
+                                topSemiJoin.getHashJoinConjuncts(), 
topSemiJoin.getOtherJoinConjuncts(),
+                                JoinHint.NONE,
+                                b, c);
                         return new LogicalJoin<>(bottomJoin.getJoinType(), 
bottomJoin.getHashJoinConjuncts(),
-                                bottomJoin.getOtherJoinConjuncts(), a, 
newBottomSemiJoin);
+                                bottomJoin.getOtherJoinConjuncts(), 
JoinHint.NONE, a, newBottomSemiJoin);
                     }
                 }).toRule(RuleType.LOGICAL_SEMI_JOIN_LOGICAL_JOIN_TRANSPOSE);
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/SemiJoinLogicalJoinTransposeProject.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/SemiJoinLogicalJoinTransposeProject.java
index f16a1c2818..3546abfa07 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/SemiJoinLogicalJoinTransposeProject.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/SemiJoinLogicalJoinTransposeProject.java
@@ -23,6 +23,7 @@ import 
org.apache.doris.nereids.rules.exploration.OneExplorationRuleFactory;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
@@ -58,6 +59,7 @@ public class SemiJoinLogicalJoinTransposeProject extends 
OneExplorationRuleFacto
                 .when(topJoin -> topJoin.getJoinType() == 
JoinType.LEFT_SEMI_JOIN
                         || topJoin.getJoinType() == JoinType.LEFT_ANTI_JOIN)
                 .whenNot(topJoin -> 
topJoin.left().child().getJoinType().isSemiOrAntiJoin())
+                .whenNot(join -> join.hasJoinHint() || 
join.left().child().hasJoinHint())
                 .when(this::conditionChecker)
                 .then(topSemiJoin -> {
                     LogicalProject<LogicalJoin<GroupPlan, GroupPlan>> project 
= topSemiJoin.left();
@@ -88,10 +90,10 @@ public class SemiJoinLogicalJoinTransposeProject extends 
OneExplorationRuleFacto
                          */
                         LogicalJoin<GroupPlan, GroupPlan> newBottomSemiJoin = 
new LogicalJoin<>(
                                 topSemiJoin.getJoinType(), 
topSemiJoin.getHashJoinConjuncts(),
-                                topSemiJoin.getOtherJoinConjuncts(), a, c);
+                                topSemiJoin.getOtherJoinConjuncts(), 
JoinHint.NONE, a, c);
 
                         LogicalJoin<Plan, Plan> newTopJoin = new 
LogicalJoin<>(bottomJoin.getJoinType(),
-                                bottomJoin.getHashJoinConjuncts(), 
bottomJoin.getOtherJoinConjuncts(),
+                                bottomJoin.getHashJoinConjuncts(), 
bottomJoin.getOtherJoinConjuncts(), JoinHint.NONE,
                                 newBottomSemiJoin, b);
 
                         return new LogicalProject<>(new 
ArrayList<>(topSemiJoin.getOutput()), newTopJoin);
@@ -107,10 +109,10 @@ public class SemiJoinLogicalJoinTransposeProject extends 
OneExplorationRuleFacto
                          */
                         LogicalJoin<GroupPlan, GroupPlan> newBottomSemiJoin = 
new LogicalJoin<>(
                                 topSemiJoin.getJoinType(), 
topSemiJoin.getHashJoinConjuncts(),
-                                topSemiJoin.getOtherJoinConjuncts(), b, c);
+                                topSemiJoin.getOtherJoinConjuncts(), 
JoinHint.NONE, b, c);
 
                         LogicalJoin<Plan, Plan> newTopJoin = new 
LogicalJoin<>(bottomJoin.getJoinType(),
-                                bottomJoin.getHashJoinConjuncts(), 
bottomJoin.getOtherJoinConjuncts(),
+                                bottomJoin.getHashJoinConjuncts(), 
bottomJoin.getOtherJoinConjuncts(), JoinHint.NONE,
                                 a, newBottomSemiJoin);
 
                         return new LogicalProject<>(new 
ArrayList<>(topSemiJoin.getOutput()), newTopJoin);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/SemiJoinSemiJoinTranspose.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/SemiJoinSemiJoinTranspose.java
index 61f86f6314..78bc186501 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/SemiJoinSemiJoinTranspose.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/SemiJoinSemiJoinTranspose.java
@@ -22,6 +22,7 @@ import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.rules.exploration.OneExplorationRuleFactory;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
 
@@ -56,6 +57,7 @@ public class SemiJoinSemiJoinTranspose extends 
OneExplorationRuleFactory {
     public Rule build() {
         return logicalJoin(logicalJoin(), group())
                 .when(this::typeChecker)
+                .whenNot(join -> join.hasJoinHint() || 
join.left().hasJoinHint())
                 .then(topJoin -> {
                     LogicalJoin<GroupPlan, GroupPlan> bottomJoin = 
topJoin.left();
                     GroupPlan a = bottomJoin.left();
@@ -63,10 +65,11 @@ public class SemiJoinSemiJoinTranspose extends 
OneExplorationRuleFactory {
                     GroupPlan c = topJoin.right();
 
                     LogicalJoin<GroupPlan, GroupPlan> newBottomJoin = new 
LogicalJoin<>(topJoin.getJoinType(),
-                            topJoin.getHashJoinConjuncts(), 
topJoin.getOtherJoinConjuncts(), a, c);
+                            topJoin.getHashJoinConjuncts(), 
topJoin.getOtherJoinConjuncts(), JoinHint.NONE, a, c);
                     LogicalJoin<LogicalJoin<GroupPlan, GroupPlan>, GroupPlan> 
newTopJoin = new LogicalJoin<>(
                             bottomJoin.getJoinType(), 
bottomJoin.getHashJoinConjuncts(),
                             bottomJoin.getOtherJoinConjuncts(),
+                            JoinHint.NONE,
                             newBottomJoin, b);
 
                     return newTopJoin;
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/SemiJoinSemiJoinTransposeProject.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/SemiJoinSemiJoinTransposeProject.java
index 79b7eb2909..448cef7221 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/SemiJoinSemiJoinTransposeProject.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/SemiJoinSemiJoinTransposeProject.java
@@ -24,6 +24,7 @@ import 
org.apache.doris.nereids.rules.exploration.OneExplorationRuleFactory;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
 import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 
@@ -52,6 +53,7 @@ public class SemiJoinSemiJoinTransposeProject extends 
OneExplorationRuleFactory
         return logicalJoin(logicalProject(logicalJoin()), group())
                 .when(this::typeChecker)
                 .when(topSemi -> InnerJoinLAsscom.checkReorder(topSemi, 
topSemi.left().child()))
+                .whenNot(join -> join.hasJoinHint() || 
join.left().child().hasJoinHint())
                 .then(topSemi -> {
                     LogicalJoin<GroupPlan, GroupPlan> bottomSemi = 
topSemi.left().child();
                     LogicalProject abProject = topSemi.left();
@@ -72,14 +74,14 @@ public class SemiJoinSemiJoinTransposeProject extends 
OneExplorationRuleFactory
                             }
                     );
                     LogicalJoin newBottomSemi = new 
LogicalJoin(topSemi.getJoinType(), topSemi.getHashJoinConjuncts(),
-                            topSemi.getOtherJoinConjuncts(), a, c,
+                            topSemi.getOtherJoinConjuncts(), JoinHint.NONE, a, 
c,
                             bottomSemi.getJoinReorderContext());
                     newBottomSemi.getJoinReorderContext().setHasCommute(false);
                     newBottomSemi.getJoinReorderContext().setHasLAsscom(false);
                     LogicalProject acProject = new 
LogicalProject(acProjects.stream().collect(Collectors.toList()),
                             newBottomSemi);
                     LogicalJoin newTopSemi = new 
LogicalJoin(bottomSemi.getJoinType(),
-                            bottomSemi.getHashJoinConjuncts(), 
bottomSemi.getOtherJoinConjuncts(),
+                            bottomSemi.getHashJoinConjuncts(), 
bottomSemi.getOtherJoinConjuncts(), JoinHint.NONE,
                             acProject, b,
                             topSemi.getJoinReorderContext());
                     newTopSemi.getJoinReorderContext().setHasLAsscom(true);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewrite.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewrite.java
index 4eb6913caf..fdf73c69d7 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewrite.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewrite.java
@@ -157,7 +157,7 @@ public class ExpressionRewrite implements 
RewriteRuleFactory {
                     return join;
                 }
                 return new LogicalJoin<>(join.getJoinType(), 
rewriteHashJoinConjuncts,
-                        rewriteOtherJoinConjuncts, join.left(), join.right());
+                        rewriteOtherJoinConjuncts, join.getHint(), 
join.left(), join.right());
             }).toRule(RuleType.REWRITE_JOIN_EXPRESSION);
         }
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalJoinToHashJoin.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalJoinToHashJoin.java
index 161ebe52b7..8657bb4801 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalJoinToHashJoin.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalJoinToHashJoin.java
@@ -34,6 +34,7 @@ public class LogicalJoinToHashJoin extends 
OneImplementationRuleFactory {
             join.getJoinType(),
             join.getHashJoinConjuncts(),
             join.getOtherJoinConjuncts(),
+            join.getHint(),
             join.getLogicalProperties(),
             join.left(),
             join.right())
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ExistsApplyToJoin.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ExistsApplyToJoin.java
index 2dd33b524b..e54949c952 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ExistsApplyToJoin.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ExistsApplyToJoin.java
@@ -26,6 +26,7 @@ import org.apache.doris.nereids.trees.expressions.Exists;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
 import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
@@ -94,12 +95,14 @@ public class ExistsApplyToJoin extends 
OneRewriteRuleFactory {
                     correlationFilter
                             .map(ExpressionUtils::extractConjunction)
                             .orElse(ExpressionUtils.EMPTY_CONDITION),
+                    JoinHint.NONE,
                     (LogicalPlan) apply.left(), (LogicalPlan) apply.right());
         } else {
             return new LogicalJoin<>(JoinType.LEFT_SEMI_JOIN, 
ExpressionUtils.EMPTY_CONDITION,
                     correlationFilter
                             .map(ExpressionUtils::extractConjunction)
                             .orElse(ExpressionUtils.EMPTY_CONDITION),
+                    JoinHint.NONE,
                     (LogicalPlan) apply.left(), (LogicalPlan) apply.right());
         }
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/FindHashConditionForJoin.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/FindHashConditionForJoin.java
index 96a8f71510..6ee7546cf0 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/FindHashConditionForJoin.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/FindHashConditionForJoin.java
@@ -69,6 +69,7 @@ public class FindHashConditionForJoin extends 
OneRewriteRuleFactory {
             return new LogicalJoin<>(joinType,
                     combinedHashJoinConjuncts,
                     remainedNonHashJoinConjuncts,
+                    join.getHint(),
                     join.left(), join.right());
         }).toRule(RuleType.FIND_HASH_CONDITION_FOR_JOIN);
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/InApplyToJoin.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/InApplyToJoin.java
index 8daab33f46..ba89fe1f0e 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/InApplyToJoin.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/InApplyToJoin.java
@@ -23,6 +23,7 @@ import 
org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
 import org.apache.doris.nereids.trees.expressions.EqualTo;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.InSubquery;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.logical.LogicalApply;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
@@ -54,10 +55,12 @@ public class InApplyToJoin extends OneRewriteRuleFactory {
             if (((InSubquery) apply.getSubqueryExpr()).isNot()) {
                 return new LogicalJoin<>(JoinType.LEFT_ANTI_JOIN, 
Lists.newArrayList(),
                         ExpressionUtils.extractConjunction(predicate),
+                        JoinHint.NONE,
                         apply.left(), apply.right());
             } else {
                 return new LogicalJoin<>(JoinType.LEFT_SEMI_JOIN, 
Lists.newArrayList(),
                         ExpressionUtils.extractConjunction(predicate),
+                        JoinHint.NONE,
                         apply.left(), apply.right());
             }
         }).toRule(RuleType.IN_APPLY_TO_JOIN);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushFilterInsideJoin.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushFilterInsideJoin.java
index eeb1699f3f..da7af4c205 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushFilterInsideJoin.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushFilterInsideJoin.java
@@ -44,7 +44,7 @@ public class PushFilterInsideJoin extends 
OneRewriteRuleFactory {
                     LogicalJoin<GroupPlan, GroupPlan> join = filter.child();
                     otherConditions.addAll(join.getOtherJoinConjuncts());
                     return new LogicalJoin<>(join.getJoinType(), 
join.getHashJoinConjuncts(),
-                            otherConditions, join.left(), join.right());
+                            otherConditions, join.getHint(), join.left(), 
join.right());
                 }).toRule(RuleType.PUSH_FILTER_INSIDE_JOIN);
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughJoin.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughJoin.java
index 4d2fa019e2..1bdc6087ec 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughJoin.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughJoin.java
@@ -137,6 +137,7 @@ public class PushdownFilterThroughJoin extends 
OneRewriteRuleFactory {
                     new LogicalJoin<>(join.getJoinType(),
                             join.getHashJoinConjuncts(),
                             joinConditions,
+                            join.getHint(),
                             PlanUtils.filterOrSelf(leftPredicates, 
join.left()),
                             PlanUtils.filterOrSelf(rightPredicates, 
join.right())));
         }).toRule(RuleType.PUSHDOWN_FILTER_THROUGH_JOIN);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownJoinOtherCondition.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownJoinOtherCondition.java
index 3d6c3b3143..cbe3eebe17 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownJoinOtherCondition.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownJoinOtherCondition.java
@@ -85,7 +85,7 @@ public class PushdownJoinOtherCondition extends 
OneRewriteRuleFactory {
                     Plan right = PlanUtils.filterOrSelf(rightConjuncts, 
join.right());
 
                     return new LogicalJoin<>(join.getJoinType(), 
join.getHashJoinConjuncts(),
-                            remainingOther, left, right);
+                            remainingOther, join.getHint(), left, right);
 
                 }).toRule(RuleType.PUSHDOWN_JOIN_OTHER_CONDITION);
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ReorderJoin.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ReorderJoin.java
index 122f73e463..cfd5b2ca1c 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ReorderJoin.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ReorderJoin.java
@@ -24,6 +24,8 @@ import 
org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.JoinHint;
+import org.apache.doris.nereids.trees.plans.JoinHint.JoinHintType;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
@@ -36,6 +38,7 @@ import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableList.Builder;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -70,10 +73,11 @@ public class ReorderJoin extends OneRewriteRuleFactory {
         return logicalFilter(subTree(LogicalJoin.class, 
LogicalFilter.class)).thenApply(ctx -> {
             LogicalFilter<Plan> filter = ctx.root;
 
-            Plan plan = joinToMultiJoin(filter);
+            Map<Plan, JoinHintType> planToHintType = Maps.newHashMap();
+            Plan plan = joinToMultiJoin(filter, planToHintType);
             Preconditions.checkState(plan instanceof MultiJoin);
             MultiJoin multiJoin = (MultiJoin) plan;
-            Plan after = multiJoinToJoin(multiJoin);
+            Plan after = multiJoinToJoin(multiJoin, planToHintType);
             return after;
         }).toRule(RuleType.REORDER_JOIN);
     }
@@ -83,7 +87,7 @@ public class ReorderJoin extends OneRewriteRuleFactory {
      * {@link LogicalJoin} or {@link LogicalFilter}--{@link LogicalJoin}
      * --> {@link MultiJoin}
      */
-    public Plan joinToMultiJoin(Plan plan) {
+    public Plan joinToMultiJoin(Plan plan, Map<Plan, JoinHintType> 
planToHintType) {
         // subtree can't specify the end of Pattern. so end can be GroupPlan 
or Filter
         if (plan instanceof GroupPlan
                 || (plan instanceof LogicalFilter && plan.child(0) instanceof 
GroupPlan)) {
@@ -113,8 +117,10 @@ public class ReorderJoin extends OneRewriteRuleFactory {
         }
 
         // recursively convert children.
-        Plan left = joinToMultiJoin(join.left());
-        Plan right = joinToMultiJoin(join.right());
+        planToHintType.put(join.left(), join.getLeftHint());
+        Plan left = joinToMultiJoin(join.left(), planToHintType);
+        planToHintType.put(join.right(), join.getRightHint());
+        Plan right = joinToMultiJoin(join.right(), planToHintType);
 
         boolean changeLeft = join.getJoinType().isRightJoin()
                 || join.getJoinType().isFullOuterJoin();
@@ -197,7 +203,7 @@ public class ReorderJoin extends OneRewriteRuleFactory {
      * A  B  C  D  F   ──►   A  B  C │ D  F    ──►  MJ(FOJ MJ(A,B,C) MJ(D,F))
      * </pre>
      */
-    public Plan multiJoinToJoin(MultiJoin multiJoin) {
+    public Plan multiJoinToJoin(MultiJoin multiJoin, Map<Plan, JoinHintType> 
planToHintType) {
         if (multiJoin.arity() == 1) {
             return PlanUtils.filterOrSelf(multiJoin.getJoinFilter(), 
multiJoin.child(0));
         }
@@ -207,7 +213,7 @@ public class ReorderJoin extends OneRewriteRuleFactory {
         for (Plan child : multiJoin.children()) {
             if (child instanceof MultiJoin) {
                 MultiJoin childMultiJoin = (MultiJoin) child;
-                builder.add(multiJoinToJoin(childMultiJoin));
+                builder.add(multiJoinToJoin(childMultiJoin, planToHintType));
             } else {
                 builder.add(child);
             }
@@ -232,7 +238,7 @@ public class ReorderJoin extends OneRewriteRuleFactory {
                         multiJoinHandleChildren.children().subList(0, 
multiJoinHandleChildren.arity() - 1),
                         pushedFilter,
                         JoinType.INNER_JOIN,
-                        ExpressionUtils.EMPTY_CONDITION));
+                        ExpressionUtils.EMPTY_CONDITION), planToHintType);
             } else if (multiJoinHandleChildren.getJoinType().isRightJoin()) {
                 left = multiJoinHandleChildren.child(0);
                 Set<Slot> leftOutputSet = left.getOutputSet();
@@ -246,13 +252,13 @@ public class ReorderJoin extends OneRewriteRuleFactory {
                         multiJoinHandleChildren.children().subList(1, 
multiJoinHandleChildren.arity()),
                         pushedFilter,
                         JoinType.INNER_JOIN,
-                        ExpressionUtils.EMPTY_CONDITION));
+                        ExpressionUtils.EMPTY_CONDITION), planToHintType);
             } else {
                 remainingFilter = multiJoin.getJoinFilter();
                 Preconditions.checkState(multiJoinHandleChildren.arity() == 2);
                 List<Plan> children = 
multiJoinHandleChildren.children().stream().map(child -> {
                     if (child instanceof MultiJoin) {
-                        return multiJoinToJoin((MultiJoin) child);
+                        return multiJoinToJoin((MultiJoin) child, 
planToHintType);
                     } else {
                         return child;
                     }
@@ -264,6 +270,7 @@ public class ReorderJoin extends OneRewriteRuleFactory {
             return PlanUtils.filterOrSelf(remainingFilter, new LogicalJoin<>(
                     multiJoinHandleChildren.getJoinType(),
                     ExpressionUtils.EMPTY_CONDITION, 
multiJoinHandleChildren.getNotInnerJoinConditions(),
+                    
JoinHint.fromRightPlanHintType(planToHintType.getOrDefault(right, 
JoinHintType.NONE)),
                     left, right));
         }
 
@@ -276,7 +283,7 @@ public class ReorderJoin extends OneRewriteRuleFactory {
 
         while (usedPlansIndex.size() != 
multiJoinHandleChildren.children().size()) {
             LogicalJoin<? extends Plan, ? extends Plan> join = 
findInnerJoin(left, multiJoinHandleChildren.children(),
-                    joinFilter, usedPlansIndex);
+                    joinFilter, usedPlansIndex, planToHintType);
             join.getHashJoinConjuncts().forEach(joinFilter::remove);
             join.getOtherJoinConjuncts().forEach(joinFilter::remove);
 
@@ -306,7 +313,7 @@ public class ReorderJoin extends OneRewriteRuleFactory {
      * @return InnerJoin or CrossJoin{left, last of [candidates]}
      */
     private LogicalJoin<? extends Plan, ? extends Plan> findInnerJoin(Plan 
left, List<Plan> candidates,
-            Set<Expression> joinFilter, Set<Integer> usedPlansIndex) {
+            Set<Expression> joinFilter, Set<Integer> usedPlansIndex, Map<Plan, 
JoinHintType> planToHintType) {
         List<Expression> otherJoinConditions = Lists.newArrayList();
         Set<Slot> leftOutputSet = left.getOutputSet();
         for (int i = 0; i < candidates.size(); i++) {
@@ -335,6 +342,7 @@ public class ReorderJoin extends OneRewriteRuleFactory {
                 usedPlansIndex.add(i);
                 return new LogicalJoin<>(JoinType.INNER_JOIN,
                         hashJoinConditions, otherJoinConditions,
+                        
JoinHint.fromRightPlanHintType(planToHintType.getOrDefault(candidate, 
JoinHintType.NONE)),
                         left, candidate);
             }
         }
@@ -345,10 +353,12 @@ public class ReorderJoin extends OneRewriteRuleFactory {
                 continue;
             }
             usedPlansIndex.add(i);
+            Plan right = candidates.get(i);
             return new LogicalJoin<>(JoinType.CROSS_JOIN,
                     ExpressionUtils.EMPTY_CONDITION,
                     otherJoinConditions,
-                    left, candidates.get(i));
+                    
JoinHint.fromRightPlanHintType(planToHintType.getOrDefault(right, 
JoinHintType.NONE)),
+                    left, right);
         }
 
         throw new RuntimeException("findInnerJoin: can't reach here");
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ScalarApplyToJoin.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ScalarApplyToJoin.java
index f77ca4ac07..141f2e4203 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ScalarApplyToJoin.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ScalarApplyToJoin.java
@@ -22,6 +22,7 @@ import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
 import org.apache.doris.nereids.trees.expressions.AssertNumRowsElement;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalApply;
@@ -67,6 +68,7 @@ public class ScalarApplyToJoin extends OneRewriteRuleFactory {
                 correlationFilter
                         .map(ExpressionUtils::extractConjunction)
                         .orElse(ExpressionUtils.EMPTY_CONDITION),
+                JoinHint.NONE,
                 (LogicalPlan) apply.left(),
                 (LogicalPlan) apply.right());
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/JoinHint.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/JoinHint.java
new file mode 100644
index 0000000000..37e246f1a6
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/JoinHint.java
@@ -0,0 +1,72 @@
+// 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.
+
+package org.apache.doris.nereids.trees.plans;
+
+/**
+ * Hints for join.
+ * <p>
+ * Hints for the right child of join are supported currently.
+ * Left input and right input of join could have different hints for further 
extension.
+ */
+public enum JoinHint {
+    NONE(JoinHintType.NONE, JoinHintType.NONE),
+    BROADCAST_RIGHT(JoinHintType.NONE, JoinHintType.BROADCAST),
+    SHUFFLE_RIGHT(JoinHintType.NONE, JoinHintType.SHUFFLE);
+
+    /**
+     * Join hint type for single join input plan.
+     */
+    public enum JoinHintType {
+        // No join hint.
+        NONE,
+        // Shuffle join hint.
+        SHUFFLE,
+        // Broadcast join hint.
+        BROADCAST,
+    }
+
+    private final JoinHintType leftHint;
+    private final JoinHintType rightHint;
+
+    JoinHint(JoinHintType leftHint, JoinHintType rightHint) {
+        this.leftHint = leftHint;
+        this.rightHint = rightHint;
+    }
+
+    public JoinHintType getLeftHint() {
+        return leftHint;
+    }
+
+    public JoinHintType getRightHint() {
+        return rightHint;
+    }
+
+    /**
+     * Create join hint from join right child's join hint type.
+     */
+    public static JoinHint fromRightPlanHintType(JoinHintType hintType) {
+        switch (hintType) {
+            case SHUFFLE:
+                return SHUFFLE_RIGHT;
+            case BROADCAST:
+                return BROADCAST_RIGHT;
+            default:
+                return NONE;
+        }
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Join.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Join.java
index 6bf8810344..6de43e7db3 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Join.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Join.java
@@ -18,6 +18,8 @@
 package org.apache.doris.nereids.trees.plans.algebra;
 
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.plans.JoinHint;
+import org.apache.doris.nereids.trees.plans.JoinHint.JoinHintType;
 import org.apache.doris.nereids.trees.plans.JoinType;
 
 import java.util.List;
@@ -35,10 +37,34 @@ public interface Join {
 
     Optional<Expression> getOnClauseCondition();
 
+    JoinHint getHint();
+
+    default boolean hasJoinHint() {
+        return getHint() != JoinHint.NONE;
+    }
+
     /**
      * The join plan has join condition or not.
      */
     default boolean hasJoinCondition() {
         return !getHashJoinConjuncts().isEmpty() || 
!getOtherJoinConjuncts().isEmpty();
     }
+
+    default JoinHintType getLeftHint() {
+        return JoinHintType.NONE;
+    }
+
+    /**
+     * Get the hint type of join's right child.
+     */
+    default JoinHintType getRightHint() {
+        switch (getHint()) {
+            case SHUFFLE_RIGHT:
+                return JoinHintType.SHUFFLE;
+            case BROADCAST_RIGHT:
+                return JoinHintType.BROADCAST;
+            default:
+                return JoinHintType.NONE;
+        }
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalJoin.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalJoin.java
index 638973d2aa..db9ed1816b 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalJoin.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalJoin.java
@@ -22,6 +22,7 @@ import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.rules.exploration.join.JoinReorderContext;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.PlanType;
@@ -33,6 +34,7 @@ import org.apache.doris.nereids.util.Utils;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.collect.Lists;
 
 import java.util.List;
 import java.util.Objects;
@@ -47,6 +49,7 @@ public class LogicalJoin<LEFT_CHILD_TYPE extends Plan, 
RIGHT_CHILD_TYPE extends
     private final JoinType joinType;
     private final ImmutableList<Expression> otherJoinConjuncts;
     private final ImmutableList<Expression> hashJoinConjuncts;
+    private final JoinHint hint;
 
     // Use for top-to-down join reorder
     private final JoinReorderContext joinReorderContext = new 
JoinReorderContext();
@@ -57,40 +60,62 @@ public class LogicalJoin<LEFT_CHILD_TYPE extends Plan, 
RIGHT_CHILD_TYPE extends
      * @param joinType logical type for join
      */
     public LogicalJoin(JoinType joinType, LEFT_CHILD_TYPE leftChild, 
RIGHT_CHILD_TYPE rightChild) {
-        this(joinType, ExpressionUtils.EMPTY_CONDITION, 
ExpressionUtils.EMPTY_CONDITION, Optional.empty(),
-                Optional.empty(), leftChild, rightChild);
+        this(joinType, ExpressionUtils.EMPTY_CONDITION, 
ExpressionUtils.EMPTY_CONDITION, JoinHint.NONE,
+                Optional.empty(), Optional.empty(), leftChild, rightChild);
     }
 
     public LogicalJoin(JoinType joinType, List<Expression> hashJoinConjuncts, 
LEFT_CHILD_TYPE leftChild,
             RIGHT_CHILD_TYPE rightChild) {
-        this(joinType, hashJoinConjuncts, ExpressionUtils.EMPTY_CONDITION, 
Optional.empty(), Optional.empty(),
-                leftChild, rightChild);
+        this(joinType, hashJoinConjuncts, ExpressionUtils.EMPTY_CONDITION, 
JoinHint.NONE, Optional.empty(),
+                Optional.empty(), leftChild, rightChild);
     }
 
-    public LogicalJoin(JoinType joinType, List<Expression> hashJoinConjuncts, 
List<Expression> otherJoinConjuncts,
+    public LogicalJoin(
+            JoinType joinType,
+            List<Expression> hashJoinConjuncts,
+            List<Expression> otherJoinConjuncts,
+            JoinHint hint,
             LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
         this(joinType, hashJoinConjuncts,
-                otherJoinConjuncts, Optional.empty(), Optional.empty(), 
leftChild, rightChild);
+                otherJoinConjuncts, hint, Optional.empty(), Optional.empty(), 
leftChild, rightChild);
     }
 
-    public LogicalJoin(JoinType joinType, List<Expression> hashJoinConjuncts, 
LEFT_CHILD_TYPE leftChild,
-            RIGHT_CHILD_TYPE rightChild, JoinReorderContext 
joinReorderContext) {
-        this(joinType, hashJoinConjuncts, ExpressionUtils.EMPTY_CONDITION,
+    public LogicalJoin(
+            JoinType joinType,
+            List<Expression> hashJoinConjuncts,
+            JoinHint hint,
+            LEFT_CHILD_TYPE leftChild,
+            RIGHT_CHILD_TYPE rightChild,
+            JoinReorderContext joinReorderContext) {
+        this(joinType, hashJoinConjuncts, ExpressionUtils.EMPTY_CONDITION, 
hint,
                 Optional.empty(), Optional.empty(), leftChild, rightChild);
         this.joinReorderContext.copyFrom(joinReorderContext);
     }
 
-    public LogicalJoin(JoinType joinType, List<Expression> hashJoinConjuncts, 
List<Expression> otherJoinConjuncts,
-            LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild, 
JoinReorderContext joinReorderContext) {
-        this(joinType, hashJoinConjuncts, otherJoinConjuncts,
+    public LogicalJoin(
+            JoinType joinType,
+            List<Expression> hashJoinConjuncts,
+            List<Expression> otherJoinConjuncts,
+            JoinHint hint,
+            LEFT_CHILD_TYPE leftChild,
+            RIGHT_CHILD_TYPE rightChild,
+            JoinReorderContext joinReorderContext) {
+        this(joinType, hashJoinConjuncts, otherJoinConjuncts, hint,
                 Optional.empty(), Optional.empty(), leftChild, rightChild);
         this.joinReorderContext.copyFrom(joinReorderContext);
     }
 
-    public LogicalJoin(JoinType joinType, List<Expression> hashJoinConjuncts, 
List<Expression> otherJoinConjuncts,
-            Optional<GroupExpression> groupExpression, 
Optional<LogicalProperties> logicalProperties,
-            LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild, 
JoinReorderContext joinReorderContext) {
-        this(joinType, hashJoinConjuncts, otherJoinConjuncts, groupExpression, 
logicalProperties, leftChild,
+    public LogicalJoin(
+            JoinType joinType,
+            List<Expression> hashJoinConjuncts,
+            List<Expression> otherJoinConjuncts,
+            JoinHint hint,
+            Optional<GroupExpression> groupExpression,
+            Optional<LogicalProperties> logicalProperties,
+            LEFT_CHILD_TYPE leftChild,
+            RIGHT_CHILD_TYPE rightChild,
+            JoinReorderContext joinReorderContext) {
+        this(joinType, hashJoinConjuncts, otherJoinConjuncts, hint, 
groupExpression, logicalProperties, leftChild,
                 rightChild);
         this.joinReorderContext.copyFrom(joinReorderContext);
     }
@@ -100,13 +125,20 @@ public class LogicalJoin<LEFT_CHILD_TYPE extends Plan, 
RIGHT_CHILD_TYPE extends
      *
      * @param joinType logical type for join
      */
-    public LogicalJoin(JoinType joinType, List<Expression> hashJoinConjuncts, 
List<Expression> otherJoinConjuncts,
-            Optional<GroupExpression> groupExpression, 
Optional<LogicalProperties> logicalProperties,
-            LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
+    public LogicalJoin(
+            JoinType joinType,
+            List<Expression> hashJoinConjuncts,
+            List<Expression> otherJoinConjuncts,
+            JoinHint hint,
+            Optional<GroupExpression> groupExpression,
+            Optional<LogicalProperties> logicalProperties,
+            LEFT_CHILD_TYPE leftChild,
+            RIGHT_CHILD_TYPE rightChild) {
         super(PlanType.LOGICAL_JOIN, groupExpression, logicalProperties, 
leftChild, rightChild);
         this.joinType = Objects.requireNonNull(joinType, "joinType can not be 
null");
         this.hashJoinConjuncts = ImmutableList.copyOf(hashJoinConjuncts);
         this.otherJoinConjuncts = ImmutableList.copyOf(otherJoinConjuncts);
+        this.hint = Objects.requireNonNull(hint, "hint can not be null");
     }
 
     public List<Expression> getOtherJoinConjuncts() {
@@ -126,6 +158,10 @@ public class LogicalJoin<LEFT_CHILD_TYPE extends Plan, 
RIGHT_CHILD_TYPE extends
         return joinType;
     }
 
+    public JoinHint getHint() {
+        return hint;
+    }
+
     @Override
     public List<Slot> computeOutput() {
 
@@ -167,11 +203,15 @@ public class LogicalJoin<LEFT_CHILD_TYPE extends Plan, 
RIGHT_CHILD_TYPE extends
 
     @Override
     public String toString() {
-        return Utils.toSqlString("LogicalJoin",
+        List<Object> args = Lists.newArrayList(
                 "type", joinType,
                 "hashJoinConjuncts", hashJoinConjuncts,
-                "otherJoinConjuncts", otherJoinConjuncts
-        );
+                "otherJoinConjuncts", otherJoinConjuncts);
+        if (hint != JoinHint.NONE) {
+            args.add("hint");
+            args.add(hint);
+        }
+        return Utils.toSqlString("LogicalJoin", args.toArray());
     }
 
     // TODO:
@@ -190,7 +230,8 @@ public class LogicalJoin<LEFT_CHILD_TYPE extends Plan, 
RIGHT_CHILD_TYPE extends
                 // TODO: why use containsAll?
                 && that.getHashJoinConjuncts().containsAll(hashJoinConjuncts)
                 && hashJoinConjuncts.containsAll(that.getHashJoinConjuncts())
-                && Objects.equals(otherJoinConjuncts, that.otherJoinConjuncts);
+                && Objects.equals(otherJoinConjuncts, that.otherJoinConjuncts)
+                && hint.equals(that.hint);
     }
 
     @Override
@@ -218,19 +259,19 @@ public class LogicalJoin<LEFT_CHILD_TYPE extends Plan, 
RIGHT_CHILD_TYPE extends
     @Override
     public LogicalBinary<Plan, Plan> withChildren(List<Plan> children) {
         Preconditions.checkArgument(children.size() == 2);
-        return new LogicalJoin<>(joinType, hashJoinConjuncts, 
otherJoinConjuncts, children.get(0), children.get(1),
-                joinReorderContext);
+        return new LogicalJoin<>(joinType, hashJoinConjuncts, 
otherJoinConjuncts, hint, children.get(0),
+                children.get(1), joinReorderContext);
     }
 
     @Override
     public Plan withGroupExpression(Optional<GroupExpression> groupExpression) 
{
-        return new LogicalJoin<>(joinType, hashJoinConjuncts, 
otherJoinConjuncts, groupExpression,
+        return new LogicalJoin<>(joinType, hashJoinConjuncts, 
otherJoinConjuncts, hint, groupExpression,
                 Optional.of(getLogicalProperties()), left(), right(), 
joinReorderContext);
     }
 
     @Override
     public Plan withLogicalProperties(Optional<LogicalProperties> 
logicalProperties) {
-        return new LogicalJoin<>(joinType, hashJoinConjuncts, 
otherJoinConjuncts,
+        return new LogicalJoin<>(joinType, hashJoinConjuncts, 
otherJoinConjuncts, hint,
                 Optional.empty(), logicalProperties, left(), right(), 
joinReorderContext);
     }
 
@@ -246,21 +287,22 @@ public class LogicalJoin<LEFT_CHILD_TYPE extends Plan, 
RIGHT_CHILD_TYPE extends
 
     public LogicalJoin withHashJoinConjuncts(List<Expression> 
hashJoinConjuncts) {
         return new LogicalJoin<>(
-                joinType, hashJoinConjuncts, this.otherJoinConjuncts, left(), 
right(), joinReorderContext);
+                joinType, hashJoinConjuncts, this.otherJoinConjuncts, hint, 
left(), right(), joinReorderContext);
     }
 
     public LogicalJoin withhashJoinConjunctsAndChildren(List<Expression> 
hashJoinConjuncts, List<Plan> children) {
         Preconditions.checkArgument(children.size() == 2);
-        return new LogicalJoin<>(joinType, hashJoinConjuncts, 
otherJoinConjuncts, children.get(0), children.get(1),
-                joinReorderContext);
+        return new LogicalJoin<>(joinType, hashJoinConjuncts, 
otherJoinConjuncts, hint, children.get(0),
+                children.get(1), joinReorderContext);
     }
 
     public LogicalJoin withJoinType(JoinType joinType) {
-        return new LogicalJoin<>(joinType, hashJoinConjuncts, 
otherJoinConjuncts, left(), right(), joinReorderContext);
+        return new LogicalJoin<>(joinType, hashJoinConjuncts, 
otherJoinConjuncts, hint, left(), right(),
+                joinReorderContext);
     }
 
     public LogicalJoin withOtherJoinConjuncts(List<Expression> 
otherJoinConjuncts) {
-        return new LogicalJoin<>(joinType, hashJoinConjuncts, 
otherJoinConjuncts, left(), right(),
+        return new LogicalJoin<>(joinType, hashJoinConjuncts, 
otherJoinConjuncts, hint, left(), right(),
                 joinReorderContext);
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/AbstractPhysicalJoin.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/AbstractPhysicalJoin.java
index ccb9a68e6b..293670eb53 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/AbstractPhysicalJoin.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/AbstractPhysicalJoin.java
@@ -21,6 +21,7 @@ import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.properties.PhysicalProperties;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.PlanType;
@@ -49,30 +50,46 @@ public abstract class AbstractPhysicalJoin<
     protected final ImmutableList<Expression> otherJoinConjuncts;
 
     protected boolean shouldTranslateOutput = true;
+    protected final JoinHint hint;
 
     /**
      * Constructor of PhysicalJoin.
      */
-    public AbstractPhysicalJoin(PlanType type, JoinType joinType, 
List<Expression> hashJoinConjuncts,
-            List<Expression> otherJoinConjuncts, Optional<GroupExpression> 
groupExpression,
+    public AbstractPhysicalJoin(
+            PlanType type,
+            JoinType joinType,
+            List<Expression> hashJoinConjuncts,
+            List<Expression> otherJoinConjuncts,
+            JoinHint hint,
+            Optional<GroupExpression> groupExpression,
             LogicalProperties logicalProperties, LEFT_CHILD_TYPE leftChild, 
RIGHT_CHILD_TYPE rightChild) {
         super(type, groupExpression, logicalProperties, leftChild, rightChild);
         this.joinType = Objects.requireNonNull(joinType, "joinType can not be 
null");
         this.hashJoinConjuncts = ImmutableList.copyOf(hashJoinConjuncts);
         this.otherJoinConjuncts = ImmutableList.copyOf(otherJoinConjuncts);
+        this.hint = Objects.requireNonNull(hint, "hint can not be null");
     }
 
     /**
      * Constructor of PhysicalJoin.
      */
-    public AbstractPhysicalJoin(PlanType type, JoinType joinType, 
List<Expression> hashJoinConjuncts,
-            List<Expression> otherJoinConjuncts, Optional<GroupExpression> 
groupExpression,
-            LogicalProperties logicalProperties, PhysicalProperties 
physicalProperties,
-            StatsDeriveResult statsDeriveResult, LEFT_CHILD_TYPE leftChild, 
RIGHT_CHILD_TYPE rightChild) {
+    public AbstractPhysicalJoin(
+            PlanType type,
+            JoinType joinType,
+            List<Expression> hashJoinConjuncts,
+            List<Expression> otherJoinConjuncts,
+            JoinHint hint,
+            Optional<GroupExpression> groupExpression,
+            LogicalProperties logicalProperties,
+            PhysicalProperties physicalProperties,
+            StatsDeriveResult statsDeriveResult,
+            LEFT_CHILD_TYPE leftChild,
+            RIGHT_CHILD_TYPE rightChild) {
         super(type, groupExpression, logicalProperties, physicalProperties, 
statsDeriveResult, leftChild, rightChild);
         this.joinType = Objects.requireNonNull(joinType, "joinType can not be 
null");
         this.hashJoinConjuncts = ImmutableList.copyOf(hashJoinConjuncts);
         this.otherJoinConjuncts = ImmutableList.copyOf(otherJoinConjuncts);
+        this.hint = hint;
     }
 
     public List<Expression> getHashJoinConjuncts() {
@@ -118,7 +135,8 @@ public abstract class AbstractPhysicalJoin<
         AbstractPhysicalJoin<?, ?> that = (AbstractPhysicalJoin<?, ?>) o;
         return joinType == that.joinType
                 && hashJoinConjuncts.equals(that.hashJoinConjuncts)
-                && otherJoinConjuncts.equals(that.otherJoinConjuncts);
+                && otherJoinConjuncts.equals(that.otherJoinConjuncts)
+                && hint.equals(that.hint);
     }
 
     @Override
@@ -134,4 +152,9 @@ public abstract class AbstractPhysicalJoin<
     public Optional<Expression> getOnClauseCondition() {
         return ExpressionUtils.optionalAnd(hashJoinConjuncts, 
otherJoinConjuncts);
     }
+
+    @Override
+    public JoinHint getHint() {
+        return hint;
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalHashJoin.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalHashJoin.java
index cfc7cdbfe8..81f6814685 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalHashJoin.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalHashJoin.java
@@ -21,6 +21,7 @@ import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.properties.PhysicalProperties;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.PlanType;
@@ -45,10 +46,15 @@ public class PhysicalHashJoin<
     // TODO: What's purpose? it's alway empty.
     private final List<Expression> filterConjuncts = Lists.newArrayList();
 
-    public PhysicalHashJoin(JoinType joinType, List<Expression> 
hashJoinConjuncts,
-            List<Expression> otherJoinConjuncts, LogicalProperties 
logicalProperties,
-            LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
-        this(joinType, hashJoinConjuncts, otherJoinConjuncts, 
Optional.empty(), logicalProperties, leftChild,
+    public PhysicalHashJoin(
+            JoinType joinType,
+            List<Expression> hashJoinConjuncts,
+            List<Expression> otherJoinConjuncts,
+            JoinHint hint,
+            LogicalProperties logicalProperties,
+            LEFT_CHILD_TYPE leftChild,
+            RIGHT_CHILD_TYPE rightChild) {
+        this(joinType, hashJoinConjuncts, otherJoinConjuncts, hint, 
Optional.empty(), logicalProperties, leftChild,
                 rightChild);
     }
 
@@ -58,10 +64,15 @@ public class PhysicalHashJoin<
      * @param joinType Which join type, left semi join, inner join...
      * @param hashJoinConjuncts conjunct list could use for build hash table 
in hash join
      */
-    public PhysicalHashJoin(JoinType joinType, List<Expression> 
hashJoinConjuncts, List<Expression> otherJoinConjuncts,
-            Optional<GroupExpression> groupExpression, LogicalProperties 
logicalProperties,
+    public PhysicalHashJoin(
+            JoinType joinType,
+            List<Expression> hashJoinConjuncts,
+            List<Expression> otherJoinConjuncts,
+            JoinHint hint,
+            Optional<GroupExpression> groupExpression,
+            LogicalProperties logicalProperties,
             LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
-        super(PlanType.PHYSICAL_HASH_JOIN, joinType, hashJoinConjuncts, 
otherJoinConjuncts,
+        super(PlanType.PHYSICAL_HASH_JOIN, joinType, hashJoinConjuncts, 
otherJoinConjuncts, hint,
                 groupExpression, logicalProperties, leftChild, rightChild);
     }
 
@@ -71,11 +82,18 @@ public class PhysicalHashJoin<
      * @param joinType Which join type, left semi join, inner join...
      * @param hashJoinConjuncts conjunct list could use for build hash table 
in hash join
      */
-    public PhysicalHashJoin(JoinType joinType, List<Expression> 
hashJoinConjuncts, List<Expression> otherJoinConjuncts,
-            Optional<GroupExpression> groupExpression, LogicalProperties 
logicalProperties,
-            PhysicalProperties physicalProperties, StatsDeriveResult 
statsDeriveResult, LEFT_CHILD_TYPE leftChild,
+    public PhysicalHashJoin(
+            JoinType joinType,
+            List<Expression> hashJoinConjuncts,
+            List<Expression> otherJoinConjuncts,
+            JoinHint hint,
+            Optional<GroupExpression> groupExpression,
+            LogicalProperties logicalProperties,
+            PhysicalProperties physicalProperties,
+            StatsDeriveResult statsDeriveResult,
+            LEFT_CHILD_TYPE leftChild,
             RIGHT_CHILD_TYPE rightChild) {
-        super(PlanType.PHYSICAL_HASH_JOIN, joinType, hashJoinConjuncts, 
otherJoinConjuncts,
+        super(PlanType.PHYSICAL_HASH_JOIN, joinType, hashJoinConjuncts, 
otherJoinConjuncts, hint,
                 groupExpression, logicalProperties, physicalProperties, 
statsDeriveResult, leftChild, rightChild);
     }
 
@@ -86,38 +104,42 @@ public class PhysicalHashJoin<
 
     @Override
     public String toString() {
-        return Utils.toSqlString("PhysicalHashJoin",
-                "type", joinType,
+        List<Object> args = Lists.newArrayList("type", joinType,
                 "hashJoinCondition", hashJoinConjuncts,
                 "otherJoinCondition", otherJoinConjuncts,
                 "stats", statsDeriveResult);
+        if (hint != JoinHint.NONE) {
+            args.add("hint");
+            args.add(hint);
+        }
+        return Utils.toSqlString("PhysicalHashJoin", args.toArray());
     }
 
     @Override
     public PhysicalHashJoin<Plan, Plan> withChildren(List<Plan> children) {
         Preconditions.checkArgument(children.size() == 2);
-        return new PhysicalHashJoin<>(joinType, hashJoinConjuncts, 
otherJoinConjuncts,
+        return new PhysicalHashJoin<>(joinType, hashJoinConjuncts, 
otherJoinConjuncts, hint,
                 getLogicalProperties(), children.get(0), children.get(1));
     }
 
     @Override
     public PhysicalHashJoin<LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE> 
withGroupExpression(
             Optional<GroupExpression> groupExpression) {
-        return new PhysicalHashJoin<>(joinType, hashJoinConjuncts, 
otherJoinConjuncts,
+        return new PhysicalHashJoin<>(joinType, hashJoinConjuncts, 
otherJoinConjuncts, hint,
                 groupExpression, getLogicalProperties(), left(), right());
     }
 
     @Override
     public PhysicalHashJoin<LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE> 
withLogicalProperties(
             Optional<LogicalProperties> logicalProperties) {
-        return new PhysicalHashJoin<>(joinType, hashJoinConjuncts, 
otherJoinConjuncts,
+        return new PhysicalHashJoin<>(joinType, hashJoinConjuncts, 
otherJoinConjuncts, hint,
                 Optional.empty(), logicalProperties.get(), left(), right());
     }
 
     @Override
     public PhysicalHashJoin<LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE> 
withPhysicalPropertiesAndStats(
             PhysicalProperties physicalProperties, StatsDeriveResult 
statsDeriveResult) {
-        return new PhysicalHashJoin<>(joinType, hashJoinConjuncts, 
otherJoinConjuncts,
+        return new PhysicalHashJoin<>(joinType, hashJoinConjuncts, 
otherJoinConjuncts, hint,
                 Optional.empty(), getLogicalProperties(), physicalProperties, 
statsDeriveResult, left(), right());
     }
 
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalNestedLoopJoin.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalNestedLoopJoin.java
index e9b54e65b8..525f40ebe7 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalNestedLoopJoin.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalNestedLoopJoin.java
@@ -21,6 +21,7 @@ import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.properties.PhysicalProperties;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.PlanType;
@@ -41,11 +42,15 @@ public class PhysicalNestedLoopJoin<
         RIGHT_CHILD_TYPE extends Plan>
         extends AbstractPhysicalJoin<LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE> {
 
-    public PhysicalNestedLoopJoin(JoinType joinType,
-            List<Expression> hashJoinConjuncts, List<Expression> 
otherJoinConjuncts,
-            LogicalProperties logicalProperties, LEFT_CHILD_TYPE leftChild, 
RIGHT_CHILD_TYPE rightChild) {
-        this(joinType, hashJoinConjuncts, otherJoinConjuncts,
-                Optional.empty(), logicalProperties, leftChild, rightChild);
+    public PhysicalNestedLoopJoin(
+            JoinType joinType,
+            List<Expression> hashJoinConjuncts,
+            List<Expression> otherJoinConjuncts,
+            LogicalProperties logicalProperties,
+            LEFT_CHILD_TYPE leftChild,
+            RIGHT_CHILD_TYPE rightChild) {
+        this(joinType, hashJoinConjuncts, otherJoinConjuncts, 
Optional.empty(), logicalProperties, leftChild,
+                rightChild);
     }
 
     /**
@@ -54,11 +59,16 @@ public class PhysicalNestedLoopJoin<
      * @param joinType Which join type, left semi join, inner join...
      * @param hashJoinConjuncts conjunct list could use for build hash table 
in hash join
      */
-    public PhysicalNestedLoopJoin(JoinType joinType,
-            List<Expression> hashJoinConjuncts, List<Expression> 
otherJoinConjuncts,
-            Optional<GroupExpression> groupExpression, LogicalProperties 
logicalProperties,
+    public PhysicalNestedLoopJoin(
+            JoinType joinType,
+            List<Expression> hashJoinConjuncts,
+            List<Expression> otherJoinConjuncts,
+            Optional<GroupExpression> groupExpression,
+            LogicalProperties logicalProperties,
             LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
         super(PlanType.PHYSICAL_NESTED_LOOP_JOIN, joinType, hashJoinConjuncts, 
otherJoinConjuncts,
+                // nested loop join ignores join hints.
+                JoinHint.NONE,
                 groupExpression, logicalProperties, leftChild, rightChild);
     }
 
@@ -68,12 +78,19 @@ public class PhysicalNestedLoopJoin<
      * @param joinType Which join type, left semi join, inner join...
      * @param hashJoinConjuncts conjunct list could use for build hash table 
in hash join
      */
-    public PhysicalNestedLoopJoin(JoinType joinType, List<Expression> 
hashJoinConjuncts,
-            List<Expression> otherJoinConjuncts, Optional<GroupExpression> 
groupExpression,
-            LogicalProperties logicalProperties, PhysicalProperties 
physicalProperties,
-            StatsDeriveResult statsDeriveResult, LEFT_CHILD_TYPE leftChild,
+    public PhysicalNestedLoopJoin(
+            JoinType joinType,
+            List<Expression> hashJoinConjuncts,
+            List<Expression> otherJoinConjuncts,
+            Optional<GroupExpression> groupExpression,
+            LogicalProperties logicalProperties,
+            PhysicalProperties physicalProperties,
+            StatsDeriveResult statsDeriveResult,
+            LEFT_CHILD_TYPE leftChild,
             RIGHT_CHILD_TYPE rightChild) {
         super(PlanType.PHYSICAL_NESTED_LOOP_JOIN, joinType, hashJoinConjuncts, 
otherJoinConjuncts,
+                // nested loop join ignores join hints.
+                JoinHint.NONE,
                 groupExpression, logicalProperties, physicalProperties, 
statsDeriveResult, leftChild, rightChild);
     }
 
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/JoinHintTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/JoinHintTest.java
new file mode 100644
index 0000000000..76b2cf6dd0
--- /dev/null
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/JoinHintTest.java
@@ -0,0 +1,148 @@
+// 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.
+
+package org.apache.doris.nereids;
+
+import org.apache.doris.nereids.properties.DistributionSpec;
+import org.apache.doris.nereids.properties.DistributionSpecHash;
+import org.apache.doris.nereids.properties.DistributionSpecHash.ShuffleType;
+import org.apache.doris.nereids.trees.plans.JoinHint;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.util.MatchingUtils;
+import org.apache.doris.nereids.util.PatternMatchSupported;
+import org.apache.doris.nereids.util.PlanChecker;
+import org.apache.doris.planner.HashJoinNode;
+import org.apache.doris.planner.HashJoinNode.DistributionMode;
+import org.apache.doris.planner.PlanFragment;
+import org.apache.doris.planner.PlanNode;
+import org.apache.doris.utframe.TestWithFeService;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+class JoinHintTest extends TestWithFeService implements PatternMatchSupported {
+
+    @Override
+    protected void runBeforeAll() throws Exception {
+        createDatabase("test");
+        useDatabase("test");
+
+        createTable("CREATE TABLE `t1` (\n"
+                + "  `a` int(11) NULL,\n"
+                + "  `b` int(11) NULL,\n"
+                + "  `c` int(11) NULL\n"
+                + ") ENGINE=OLAP\n"
+                + "DUPLICATE KEY(`a`, `b`, `c`)\n"
+                + "COMMENT 'OLAP'\n"
+                + "DISTRIBUTED BY HASH(`b`) BUCKETS 3\n"
+                + "PROPERTIES (\n"
+                + "\"replication_allocation\" = \"tag.location.default: 1\",\n"
+                + "\"in_memory\" = \"false\",\n"
+                + "\"storage_format\" = \"V2\",\n"
+                + "\"disable_auto_compaction\" = \"false\"\n"
+                + ");");
+
+        createTable("CREATE TABLE `t2` (\n"
+                + "  `x` int(11) NULL,\n"
+                + "  `y` int(11) NULL,\n"
+                + "  `z` int(11) NULL\n"
+                + ") ENGINE=OLAP\n"
+                + "DUPLICATE KEY(`x`, `y`, `z`)\n"
+                + "COMMENT 'OLAP'\n"
+                + "DISTRIBUTED BY HASH(`y`) BUCKETS 3\n"
+                + "PROPERTIES (\n"
+                + "\"replication_allocation\" = \"tag.location.default: 1\",\n"
+                + "\"in_memory\" = \"false\",\n"
+                + "\"storage_format\" = \"V2\",\n"
+                + "\"disable_auto_compaction\" = \"false\"\n"
+                + ");");
+
+        createTable("CREATE TABLE `t3` (\n"
+                + "  `x` int(11) NULL,\n"
+                + "  `y` int(11) NULL,\n"
+                + "  `z` int(11) NULL\n"
+                + ") ENGINE=OLAP\n"
+                + "DUPLICATE KEY(`x`, `y`, `z`)\n"
+                + "COMMENT 'OLAP'\n"
+                + "DISTRIBUTED BY HASH(`y`) BUCKETS 3\n"
+                + "PROPERTIES (\n"
+                + "\"replication_allocation\" = \"tag.location.default: 1\",\n"
+                + "\"in_memory\" = \"false\",\n"
+                + "\"storage_format\" = \"V2\",\n"
+                + "\"disable_auto_compaction\" = \"false\"\n"
+                + ");");
+    }
+
+    @Test
+    public void testBroadcastJoinHint() {
+        PlanChecker.from(connectContext).checkPlannerResult(
+                "select * from t1 join [broadcast] t2 on t1.a=t2.x",
+                planner -> checkPlannerResult(planner, 
DistributionMode.BROADCAST)
+        );
+    }
+
+    @Test
+    public void testShuffleJoinHint() {
+        PlanChecker.from(connectContext).checkPlannerResult(
+                "select * from t1 join [shuffle] t2 on t1.a=t2.x",
+                planner -> checkPlannerResult(planner, 
DistributionMode.PARTITIONED)
+        );
+    }
+
+    @Test
+    public void testHintWithReorderCrossJoin() throws Exception {
+        String sql = "select t1.a , t2.x, t.x from "
+                + "t1 join [shuffle] t2, (select x from t3) t where t1.a=t.x 
and t2.x=t.x";
+        PlanChecker.from(connectContext).checkExplain(sql, planner -> {
+            Plan plan = planner.getOptimizedPlan();
+            MatchingUtils.assertMatches(plan,
+                    physicalDistribute(
+                            physicalProject(
+                                    physicalHashJoin(
+                                            physicalHashJoin(),
+                                            physicalDistribute().when(dis -> {
+                                                DistributionSpec spec = 
dis.getDistributionSpec();
+                                                Assertions.assertTrue(spec 
instanceof DistributionSpecHash);
+                                                DistributionSpecHash hashSpec 
= (DistributionSpecHash) spec;
+                                                
Assertions.assertEquals(ShuffleType.ENFORCED,
+                                                        
hashSpec.getShuffleType());
+                                                return true;
+                                            })
+                                    ).when(join -> join.getHint() == 
JoinHint.SHUFFLE_RIGHT)
+                            )
+                    )
+            );
+        });
+    }
+
+    private void checkPlannerResult(NereidsPlanner planner, DistributionMode 
mode) {
+        List<PlanFragment> fragments = planner.getFragments();
+        Set<HashJoinNode> hashJoins = new HashSet<>();
+        for (PlanFragment fragment : fragments) {
+            PlanNode plan = fragment.getPlanRoot();
+            plan.collect(HashJoinNode.class, hashJoins);
+        }
+
+        Assertions.assertEquals(1, hashJoins.size());
+        HashJoinNode join = hashJoins.iterator().next();
+        Assertions.assertEquals(mode, join.getDistributionMode());
+    }
+}
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 d11a82d3c3..f4d4222da8 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
@@ -24,6 +24,7 @@ import org.apache.doris.nereids.StatementContext;
 import org.apache.doris.nereids.exceptions.ParseException;
 import org.apache.doris.nereids.glue.LogicalPlanAdapter;
 import org.apache.doris.nereids.trees.expressions.literal.DecimalLiteral;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.PlanType;
@@ -255,4 +256,47 @@ public class NereidsParserTest extends ParserTestBase {
         LogicalPlan logicalPlan1 = nereidsParser1.parseSingle(union1);
         System.out.println(logicalPlan1.treeString());
     }
+
+    @Test
+    public void testJoinHint() {
+        // no hint
+        parsePlan("select * from t1 join t2 on t1.key=t2.key")
+                .matches(logicalJoin().when(j -> j.getHint() == 
JoinHint.NONE));
+
+        // valid hint
+        parsePlan("select * from t1 join [shuffle] t2 on t1.key=t2.key")
+                .matches(logicalJoin().when(j -> j.getHint() == 
JoinHint.SHUFFLE_RIGHT));
+
+        parsePlan("select * from t1 join [  shuffle ] t2 on t1.key=t2.key")
+                .matches(logicalJoin().when(j -> j.getHint() == 
JoinHint.SHUFFLE_RIGHT));
+
+        parsePlan("select * from t1 join [broadcast] t2 on t1.key=t2.key")
+                .matches(logicalJoin().when(j -> j.getHint() == 
JoinHint.BROADCAST_RIGHT));
+
+        parsePlan("select * from t1 join /*+ broadcast   */ t2 on 
t1.key=t2.key")
+                .matches(logicalJoin().when(j -> j.getHint() == 
JoinHint.BROADCAST_RIGHT));
+
+        // invalid hint position
+        parsePlan("select * from [shuffle] t1 join t2 on t1.key=t2.key")
+                .assertThrowsExactly(ParseException.class);
+
+        parsePlan("select * from /*+ shuffle */ t1 join t2 on t1.key=t2.key")
+                .assertThrowsExactly(ParseException.class);
+
+        // invalid hint content
+        parsePlan("select * from t1 join [bucket] t2 on t1.key=t2.key")
+                .assertThrowsExactly(ParseException.class)
+                .assertMessageContains("Invalid join hint: bucket(line 1, pos 
22)\n"
+                        + "\n"
+                        + "== SQL ==\n"
+                        + "select * from t1 join [bucket] t2 on 
t1.key=t2.key\n"
+                        + "----------------------^^^");
+
+        // invalid multiple hints
+        parsePlan("select * from t1 join /*+ shuffle , broadcast */ t2 on 
t1.key=t2.key")
+                .assertThrowsExactly(ParseException.class);
+
+        parsePlan("select * from t1 join [shuffle,broadcast] t2 on 
t1.key=t2.key")
+                .assertThrowsExactly(ParseException.class);
+    }
 }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/ChildOutputPropertyDeriverTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/ChildOutputPropertyDeriverTest.java
index 0810b5ed0a..a1bcda875b 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/ChildOutputPropertyDeriverTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/ChildOutputPropertyDeriverTest.java
@@ -31,6 +31,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.agg.AggregateParam;
 import org.apache.doris.nereids.trees.plans.AggMode;
 import org.apache.doris.nereids.trees.plans.AggPhase;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.physical.AbstractPhysicalJoin;
@@ -112,7 +113,8 @@ public class ChildOutputPropertyDeriverTest {
         };
 
         PhysicalHashJoin<GroupPlan, GroupPlan> join = new 
PhysicalHashJoin<>(JoinType.RIGHT_OUTER_JOIN,
-                ExpressionUtils.EMPTY_CONDITION, 
ExpressionUtils.EMPTY_CONDITION, logicalProperties, groupPlan, groupPlan);
+                ExpressionUtils.EMPTY_CONDITION, 
ExpressionUtils.EMPTY_CONDITION, JoinHint.NONE, logicalProperties,
+                groupPlan, groupPlan);
         GroupExpression groupExpression = new GroupExpression(join);
 
         PhysicalProperties left = new PhysicalProperties(
@@ -161,7 +163,7 @@ public class ChildOutputPropertyDeriverTest {
                         new SlotReference(new ExprId(0), "left", 
IntegerType.INSTANCE, false, Collections.emptyList()),
                         new SlotReference(new ExprId(2), "right", 
IntegerType.INSTANCE, false,
                                 Collections.emptyList()))),
-                ExpressionUtils.EMPTY_CONDITION, logicalProperties, groupPlan, 
groupPlan);
+                ExpressionUtils.EMPTY_CONDITION, JoinHint.NONE, 
logicalProperties, groupPlan, groupPlan);
         GroupExpression groupExpression = new GroupExpression(join);
 
         Map<ExprId, Integer> leftMap = Maps.newHashMap();
@@ -207,7 +209,7 @@ public class ChildOutputPropertyDeriverTest {
                         new SlotReference(new ExprId(0), "left", 
IntegerType.INSTANCE, false, Collections.emptyList()),
                         new SlotReference(new ExprId(2), "right", 
IntegerType.INSTANCE, false,
                                 Collections.emptyList()))),
-                ExpressionUtils.EMPTY_CONDITION, logicalProperties, groupPlan, 
groupPlan);
+                ExpressionUtils.EMPTY_CONDITION, JoinHint.NONE, 
logicalProperties, groupPlan, groupPlan);
         GroupExpression groupExpression = new GroupExpression(join);
 
         Map<ExprId, Integer> leftMap = Maps.newHashMap();
@@ -244,7 +246,8 @@ public class ChildOutputPropertyDeriverTest {
     @Test
     public void testNestedLoopJoin() {
         PhysicalNestedLoopJoin<GroupPlan, GroupPlan> join = new 
PhysicalNestedLoopJoin<>(JoinType.CROSS_JOIN,
-                ExpressionUtils.EMPTY_CONDITION, 
ExpressionUtils.EMPTY_CONDITION, logicalProperties, groupPlan, groupPlan);
+                ExpressionUtils.EMPTY_CONDITION, 
ExpressionUtils.EMPTY_CONDITION, logicalProperties, groupPlan,
+                groupPlan);
         GroupExpression groupExpression = new GroupExpression(join);
 
         Map<ExprId, Integer> leftMap = Maps.newHashMap();
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/RequestPropertyDeriverTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/RequestPropertyDeriverTest.java
index 9e007f8bea..2639dcfa18 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/RequestPropertyDeriverTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/RequestPropertyDeriverTest.java
@@ -28,6 +28,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.agg.AggregateParam;
 import org.apache.doris.nereids.trees.plans.AggMode;
 import org.apache.doris.nereids.trees.plans.AggPhase;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.physical.AbstractPhysicalJoin;
@@ -78,7 +79,8 @@ public class RequestPropertyDeriverTest {
     @Test
     public void testNestedLoopJoin() {
         PhysicalNestedLoopJoin<GroupPlan, GroupPlan> join = new 
PhysicalNestedLoopJoin<>(JoinType.CROSS_JOIN,
-                ExpressionUtils.EMPTY_CONDITION, 
ExpressionUtils.EMPTY_CONDITION, logicalProperties, groupPlan, groupPlan);
+                ExpressionUtils.EMPTY_CONDITION, 
ExpressionUtils.EMPTY_CONDITION, logicalProperties, groupPlan,
+                groupPlan);
         GroupExpression groupExpression = new GroupExpression(join);
 
         RequestPropertyDeriver requestPropertyDeriver = new 
RequestPropertyDeriver(jobContext);
@@ -101,7 +103,8 @@ public class RequestPropertyDeriverTest {
         };
 
         PhysicalHashJoin<GroupPlan, GroupPlan> join = new 
PhysicalHashJoin<>(JoinType.RIGHT_OUTER_JOIN,
-                ExpressionUtils.EMPTY_CONDITION, 
ExpressionUtils.EMPTY_CONDITION, logicalProperties, groupPlan, groupPlan);
+                ExpressionUtils.EMPTY_CONDITION, 
ExpressionUtils.EMPTY_CONDITION, JoinHint.NONE, logicalProperties,
+                groupPlan, groupPlan);
         GroupExpression groupExpression = new GroupExpression(join);
 
         RequestPropertyDeriver requestPropertyDeriver = new 
RequestPropertyDeriver(jobContext);
@@ -127,7 +130,8 @@ public class RequestPropertyDeriverTest {
         };
 
         PhysicalHashJoin<GroupPlan, GroupPlan> join = new 
PhysicalHashJoin<>(JoinType.INNER_JOIN,
-                ExpressionUtils.EMPTY_CONDITION, 
ExpressionUtils.EMPTY_CONDITION, logicalProperties, groupPlan, groupPlan);
+                ExpressionUtils.EMPTY_CONDITION, 
ExpressionUtils.EMPTY_CONDITION, JoinHint.NONE, logicalProperties,
+                groupPlan, groupPlan);
         GroupExpression groupExpression = new GroupExpression(join);
 
         RequestPropertyDeriver requestPropertyDeriver = new 
RequestPropertyDeriver(jobContext);
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/FindHashConditionForJoinTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/FindHashConditionForJoinTest.java
index 88f5e0570f..42d34f2cd0 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/FindHashConditionForJoinTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/FindHashConditionForJoinTest.java
@@ -26,6 +26,7 @@ import org.apache.doris.nereids.trees.expressions.LessThan;
 import org.apache.doris.nereids.trees.expressions.Or;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
@@ -74,7 +75,7 @@ class FindHashConditionForJoinTest {
         Expression less = new LessThan(scoreId, studentId);
         List<Expression> expr = ImmutableList.of(eq1, eq2, eq3, or, less);
         LogicalJoin join = new LogicalJoin<>(JoinType.INNER_JOIN, new 
ArrayList<>(),
-                expr, student, score);
+                expr, JoinHint.NONE, student, score);
         CascadesContext context = MemoTestUtils.createCascadesContext(join);
         List<Rule> rules = Lists.newArrayList(new 
FindHashConditionForJoin().build());
 
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownJoinOtherConditionTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownJoinOtherConditionTest.java
index 387edc747c..9b7d20b84a 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownJoinOtherConditionTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownJoinOtherConditionTest.java
@@ -22,6 +22,7 @@ import org.apache.doris.nereids.memo.Memo;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.GreaterThan;
 import org.apache.doris.nereids.trees.expressions.literal.Literal;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
@@ -83,7 +84,7 @@ public class PushdownJoinOtherConditionTest {
             right = rStudent;
         }
 
-        Plan join = new LogicalJoin<>(joinType, 
ExpressionUtils.EMPTY_CONDITION, condition, left, right);
+        Plan join = new LogicalJoin<>(joinType, 
ExpressionUtils.EMPTY_CONDITION, condition, JoinHint.NONE, left, right);
         Plan root = new LogicalProject<>(Lists.newArrayList(), join);
 
         Memo memo = rewrite(root);
@@ -123,7 +124,8 @@ public class PushdownJoinOtherConditionTest {
         Expression rightSide = new GreaterThan(rScore.getOutput().get(2), 
Literal.of(60));
         List<Expression> condition = ImmutableList.of(leftSide, rightSide);
 
-        Plan join = new LogicalJoin<>(joinType, 
ExpressionUtils.EMPTY_CONDITION, condition, rStudent, rScore);
+        Plan join = new LogicalJoin<>(joinType, 
ExpressionUtils.EMPTY_CONDITION, condition, JoinHint.NONE, rStudent,
+                rScore);
         Plan root = new LogicalProject<>(Lists.newArrayList(), join);
 
         Memo memo = rewrite(root);
@@ -165,7 +167,7 @@ public class PushdownJoinOtherConditionTest {
             right = rStudent;
         }
 
-        Plan join = new LogicalJoin<>(joinType, 
ExpressionUtils.EMPTY_CONDITION, condition, left, right);
+        Plan join = new LogicalJoin<>(joinType, 
ExpressionUtils.EMPTY_CONDITION, condition, JoinHint.NONE, left, right);
         Plan root = new LogicalProject<>(Lists.newArrayList(), join);
 
         Memo memo = rewrite(root);
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/PlanEqualsTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/PlanEqualsTest.java
index c91cffabf9..f084777bf7 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/PlanEqualsTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/PlanEqualsTest.java
@@ -227,20 +227,20 @@ public class PlanEqualsTest {
                 Lists.newArrayList(new EqualTo(
                         new SlotReference(new ExprId(0), "a", 
BigIntType.INSTANCE, true, Lists.newArrayList()),
                         new SlotReference(new ExprId(1), "b", 
BigIntType.INSTANCE, true, Lists.newArrayList()))),
-                ExpressionUtils.EMPTY_CONDITION, logicalProperties, left, 
right);
+                ExpressionUtils.EMPTY_CONDITION, JoinHint.NONE, 
logicalProperties, left, right);
 
         PhysicalHashJoin<Plan, Plan> expected = new 
PhysicalHashJoin<>(JoinType.INNER_JOIN,
                 Lists.newArrayList(new EqualTo(
                         new SlotReference(new ExprId(0), "a", 
BigIntType.INSTANCE, true, Lists.newArrayList()),
                         new SlotReference(new ExprId(1), "b", 
BigIntType.INSTANCE, true, Lists.newArrayList()))),
-                ExpressionUtils.EMPTY_CONDITION, logicalProperties, left, 
right);
+                ExpressionUtils.EMPTY_CONDITION, JoinHint.NONE, 
logicalProperties, left, right);
         Assertions.assertEquals(expected, actual);
 
         PhysicalHashJoin<Plan, Plan> unexpected = new 
PhysicalHashJoin<>(JoinType.INNER_JOIN,
                 Lists.newArrayList(new EqualTo(
                         new SlotReference(new ExprId(2), "a", 
BigIntType.INSTANCE, false, Lists.newArrayList()),
                         new SlotReference(new ExprId(3), "b", 
BigIntType.INSTANCE, true, Lists.newArrayList()))),
-                ExpressionUtils.EMPTY_CONDITION, logicalProperties, left, 
right);
+                ExpressionUtils.EMPTY_CONDITION, JoinHint.NONE, 
logicalProperties, left, right);
         Assertions.assertNotEquals(unexpected, actual);
     }
 
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/ExceptionChecker.java 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/ExceptionChecker.java
index ebea43fee5..0ae5615e78 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/ExceptionChecker.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/ExceptionChecker.java
@@ -37,7 +37,7 @@ public class ExceptionChecker {
     }
 
     public ExceptionChecker assertMessageContains(String context) {
-        Assertions.assertTrue(exception.getMessage().contains(context));
+        Assertions.assertTrue(exception.getMessage().contains(context), 
exception.getMessage());
         return this;
     }
 
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/LogicalPlanBuilder.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/LogicalPlanBuilder.java
index fb9f9a7b49..2a8aa72952 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/LogicalPlanBuilder.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/LogicalPlanBuilder.java
@@ -22,6 +22,7 @@ import org.apache.doris.nereids.trees.expressions.Alias;
 import org.apache.doris.nereids.trees.expressions.EqualTo;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.plans.JoinHint;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
@@ -104,7 +105,7 @@ public class LogicalPlanBuilder {
     public LogicalPlanBuilder hashJoinUsing(LogicalPlan right, JoinType 
joinType, List<Expression> hashJoinConjuncts,
             List<Expression> otherJoinConjucts) {
         LogicalJoin<LogicalPlan, LogicalPlan> join = new 
LogicalJoin<>(joinType, hashJoinConjuncts, otherJoinConjucts,
-                this.plan, right);
+                JoinHint.NONE, this.plan, right);
         return from(join);
     }
 
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/GroupMatchingUtils.java
 b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/MatchingUtils.java
similarity index 61%
rename from 
fe/fe-core/src/test/java/org/apache/doris/nereids/util/GroupMatchingUtils.java
rename to 
fe/fe-core/src/test/java/org/apache/doris/nereids/util/MatchingUtils.java
index 15861f724e..bc5bb1d78d 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/GroupMatchingUtils.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/MatchingUtils.java
@@ -19,11 +19,39 @@ package org.apache.doris.nereids.util;
 
 import org.apache.doris.nereids.memo.Group;
 import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.memo.Memo;
 import org.apache.doris.nereids.pattern.GroupExpressionMatching;
 import org.apache.doris.nereids.pattern.Pattern;
+import org.apache.doris.nereids.pattern.PatternDescriptor;
 import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalPlan;
 
-public class GroupMatchingUtils {
+import com.google.common.base.Supplier;
+import org.junit.jupiter.api.Assertions;
+
+public class MatchingUtils {
+
+    public static void assertMatches(Plan plan, PatternDescriptor<? extends 
Plan> patternDesc) {
+        Memo memo = new Memo(plan);
+        if (plan instanceof PhysicalPlan) {
+            assertMatches(memo, () -> new 
GroupExpressionMatching(patternDesc.pattern,
+                    
memo.getRoot().getPhysicalExpressions().get(0)).iterator().hasNext());
+        } else if (plan instanceof LogicalPlan) {
+            assertMatches(memo, () -> new 
GroupExpressionMatching(patternDesc.pattern,
+                    
memo.getRoot().getLogicalExpression()).iterator().hasNext());
+        } else {
+            throw new IllegalStateException("Input plan should be LogicalPlan 
or PhysicalPlan, but meet " + plan);
+        }
+    }
+
+    private static void assertMatches(Memo memo, Supplier<Boolean> asserter) {
+        Assertions.assertTrue(asserter.get(),
+                () -> "pattern not match, plan :\n"
+                        + 
memo.getRoot().getLogicalExpression().getPlan().treeString()
+                        + "\n"
+        );
+    }
 
     public static boolean topDownFindMatching(Group group, Pattern<? extends 
Plan> pattern) {
         for (GroupExpression logicalExpr : group.getLogicalExpressions()) {
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanChecker.java 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanChecker.java
index 87754d1642..362ca94f42 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanChecker.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanChecker.java
@@ -17,6 +17,7 @@
 
 package org.apache.doris.nereids.util;
 
+import org.apache.doris.analysis.ExplainOptions;
 import org.apache.doris.nereids.CascadesContext;
 import org.apache.doris.nereids.NereidsPlanner;
 import org.apache.doris.nereids.StatementContext;
@@ -41,6 +42,7 @@ import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.Plan;
+import 
org.apache.doris.nereids.trees.plans.commands.ExplainCommand.ExplainLevel;
 import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalPlan;
@@ -345,7 +347,7 @@ public class PlanChecker {
 
     public PlanChecker matches(PatternDescriptor<? extends Plan> patternDesc) {
         Memo memo = cascadesContext.getMemo();
-        assertMatches(memo, () -> 
GroupMatchingUtils.topDownFindMatching(memo.getRoot(), patternDesc.pattern));
+        assertMatches(memo, () -> 
MatchingUtils.topDownFindMatching(memo.getRoot(), patternDesc.pattern));
         return this;
     }
 
@@ -414,6 +416,17 @@ public class PlanChecker {
         return this;
     }
 
+    public PlanChecker checkExplain(String sql, Consumer<NereidsPlanner> 
consumer) {
+        LogicalPlan parsed = new NereidsParser().parseSingle(sql);
+        NereidsPlanner nereidsPlanner = new NereidsPlanner(
+                new StatementContext(connectContext, new OriginStatement(sql, 
0)));
+        LogicalPlanAdapter adapter = LogicalPlanAdapter.of(parsed);
+        adapter.setIsExplain(new ExplainOptions(ExplainLevel.ALL_PLAN));
+        nereidsPlanner.plan(adapter);
+        consumer.accept(nereidsPlanner);
+        return this;
+    }
+
     public PlanChecker checkPlannerResult(String sql, Consumer<NereidsPlanner> 
consumer) {
         LogicalPlan parsed = new NereidsParser().parseSingle(sql);
         NereidsPlanner nereidsPlanner = new NereidsPlanner(
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanParseChecker.java 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanParseChecker.java
index 55032a2864..1e3484ae40 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanParseChecker.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanParseChecker.java
@@ -35,7 +35,7 @@ public class PlanParseChecker extends ParseChecker {
     }
 
     public PlanParseChecker matches(PatternDescriptor<? extends Plan> 
patternDesc) {
-        assertMatches(() -> GroupMatchingUtils.topDownFindMatching(
+        assertMatches(() -> MatchingUtils.topDownFindMatching(
                 new Memo(parsedSupplier.get()).getRoot(), 
patternDesc.pattern));
         return this;
     }
diff --git 
a/regression-test/suites/account_p0/test_nereids_authentication.groovy 
b/regression-test/suites/account_p0/test_nereids_authentication.groovy
index 6a2a980401..bd9449cf53 100644
--- a/regression-test/suites/account_p0/test_nereids_authentication.groovy
+++ b/regression-test/suites/account_p0/test_nereids_authentication.groovy
@@ -41,17 +41,17 @@ suite("test_nereids_authentication", "query") {
 
     def user='nereids_user'
     try_sql "DROP USER ${user}"
-    sql "CREATE USER ${user} IDENTIFIED BY '123456'"
+    sql "CREATE USER ${user} IDENTIFIED BY 'Doris_123456'"
     sql "GRANT SELECT_PRIV ON internal.${dbName}.${tableName1} TO ${user}"
 
     def tokens = context.config.jdbcUrl.split('/')
     def url=tokens[0] + "//" + tokens[2] + "/" + dbName + "?"
-    def result = connect(user=user, password='123456', url=url) {
+    def result = connect(user=user, password='Doris_123456', url=url) {
         sql "SELECT * FROM ${tableName1}"
     }
     assertEquals(result.size(), 0)
 
-    connect(user=user, password='123456', url=url) {
+    connect(user=user, password='Doris_123456', url=url) {
         try {
             sql "SELECT * FROM ${tableName2}"
             fail()
@@ -61,7 +61,7 @@ suite("test_nereids_authentication", "query") {
         }
     }
 
-    connect(user=user, password='123456', url=url) {
+    connect(user=user, password='Doris_123456', url=url) {
         try {
             sql "SELECT * FROM ${tableName1}, ${tableName2} WHERE 
${tableName1}.`key` = ${tableName2}.`key`"
             fail()
@@ -72,11 +72,11 @@ suite("test_nereids_authentication", "query") {
     }
 
     sql "GRANT SELECT_PRIV ON internal.${dbName}.${tableName2} TO ${user}"
-    connect(user=user, password='123456', url=url) {
+    connect(user=user, password='Doris_123456', url=url) {
         sql "SELECT * FROM ${tableName2}"
     }
     assertEquals(result.size(), 0)
-    connect(user=user, password='123456', url=url) {
+    connect(user=user, password='Doris_123456', url=url) {
         sql "SELECT * FROM ${tableName1}, ${tableName2} WHERE 
${tableName1}.`key` = ${tableName2}.`key`"
     }
     assertEquals(result.size(), 0)


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org
For additional commands, e-mail: commits-h...@doris.apache.org

Reply via email to