6969import org .apache .iotdb .db .queryengine .plan .planner .plan .node .source .SeriesScanNode ;
7070import org .apache .iotdb .db .queryengine .plan .planner .plan .node .source .SourceNode ;
7171
72+ import java .util .ArrayList ;
7273import java .util .Arrays ;
7374import java .util .HashMap ;
7475import java .util .List ;
7576import java .util .Map ;
7677import java .util .stream .Collectors ;
7778
78- import static com .google .common .collect .ImmutableList .toImmutableList ;
79-
8079public class ExchangeNodeAdder extends PlanVisitor <PlanNode , NodeGroupContext > {
8180
8281 private final Analysis analysis ;
82+ private boolean containsInnerTimeJoinInCurrentSubtree = false ;
8383
8484 public ExchangeNodeAdder (Analysis analysis ) {
8585 this .analysis = analysis ;
@@ -92,10 +92,7 @@ public PlanNode visitPlan(PlanNode node, NodeGroupContext context) {
9292 return node ;
9393 }
9494 // Visit all the children of current node
95- List <PlanNode > children =
96- node .getChildren ().stream ()
97- .map (child -> child .accept (this , context ))
98- .collect (toImmutableList ());
95+ List <PlanNode > children = visitChildrenAndRecordInnerTimeJoin (node .getChildren (), context );
9996
10097 // Calculate the node distribution info according to its children
10198
@@ -215,7 +212,13 @@ private PlanNode processNoChildSourceNode(SourceNode node, NodeGroupContext cont
215212
216213 @ Override
217214 public PlanNode visitDeviceView (DeviceViewNode node , NodeGroupContext context ) {
218- return processMultiChildNode (node , context );
215+ List <PlanNode > visitedChildren =
216+ visitChildrenAndRecordInnerTimeJoin (node .getChildren (), context );
217+ // Force Exchange for multi-child DeviceView if any child subtree contains InnerTimeJoin.
218+ if (node .getChildren ().size () > 1 && containsInnerTimeJoinInCurrentSubtree ) {
219+ return processMultiChildNodeWithForcedExchange (node , context , visitedChildren );
220+ }
221+ return processMultiChildNode (node , context , visitedChildren );
219222 }
220223
221224 @ Override
@@ -236,7 +239,13 @@ public PlanNode visitSingleDeviceView(SingleDeviceViewNode node, NodeGroupContex
236239
237240 @ Override
238241 public PlanNode visitMergeSort (MergeSortNode node , NodeGroupContext context ) {
239- return processMultiChildNode (node , context );
242+ List <PlanNode > visitedChildren =
243+ visitChildrenAndRecordInnerTimeJoin (node .getChildren (), context );
244+ // Force Exchange if any child subtree contains InnerTimeJoin.
245+ if (containsInnerTimeJoinInCurrentSubtree ) {
246+ return processMultiChildNodeWithForcedExchange (node , context , visitedChildren );
247+ }
248+ return processMultiChildNode (node , context , visitedChildren );
240249 }
241250
242251 @ Override
@@ -428,11 +437,14 @@ private PlanNode processMultiChildNode(MultiChildProcessNode node, NodeGroupCont
428437 return processMultiChildNodeByLocation (node , context );
429438 }
430439
431- MultiChildProcessNode newNode = (MultiChildProcessNode ) node .clone ();
432440 List <PlanNode > visitedChildren =
433- node .getChildren ().stream ()
434- .map (child -> visit (child , context ))
435- .collect (Collectors .toList ());
441+ visitChildrenAndRecordInnerTimeJoin (node .getChildren (), context );
442+ return processMultiChildNode (node , context , visitedChildren );
443+ }
444+
445+ private PlanNode processMultiChildNode (
446+ MultiChildProcessNode node , NodeGroupContext context , List <PlanNode > visitedChildren ) {
447+ MultiChildProcessNode newNode = (MultiChildProcessNode ) node .clone ();
436448
437449 // DataRegion in which node locates
438450 TRegionReplicaSet dataRegion ;
@@ -546,13 +558,47 @@ private PlanNode processTopKNode(
546558 }
547559
548560 private ExchangeNode genExchangeNode (NodeGroupContext context , PlanNode child ) {
561+ return genExchangeNode (context , child , false );
562+ }
563+
564+ private ExchangeNode genExchangeNode (
565+ NodeGroupContext context , PlanNode child , boolean forcedExchange ) {
549566 ExchangeNode exchangeNode = new ExchangeNode (context .queryContext .getQueryId ().genPlanNodeId ());
550567 exchangeNode .setChild (child );
551568 exchangeNode .setOutputColumnNames (child .getOutputColumnNames ());
569+ exchangeNode .setForcedExchange (forcedExchange );
552570 context .hasExchangeNode = true ;
553571 return exchangeNode ;
554572 }
555573
574+ private PlanNode processMultiChildNodeWithForcedExchange (
575+ MultiChildProcessNode node , NodeGroupContext context , List <PlanNode > visitedChildren ) {
576+ MultiChildProcessNode newNode = (MultiChildProcessNode ) node .clone ();
577+ for (PlanNode child : visitedChildren ) {
578+ newNode .addChild (genExchangeNode (context , child , true ));
579+ }
580+ context .putNodeDistribution (
581+ newNode .getPlanNodeId (),
582+ new NodeDistribution (
583+ NodeDistributionType .SAME_WITH_SOME_CHILD , context .getMostlyUsedDataRegion ()));
584+ return newNode ;
585+ }
586+
587+ private List <PlanNode > visitChildrenAndRecordInnerTimeJoin (
588+ List <PlanNode > children , NodeGroupContext context ) {
589+ List <PlanNode > result = new ArrayList <>(children .size ());
590+ boolean originalTimeJoin = containsInnerTimeJoinInCurrentSubtree ;
591+ boolean hasInnerTimeJoinInChildren = false ;
592+ for (PlanNode child : children ) {
593+ containsInnerTimeJoinInCurrentSubtree = false ;
594+ PlanNode visitedChild = visit (child , context );
595+ hasInnerTimeJoinInChildren |= containsInnerTimeJoinInCurrentSubtree ;
596+ result .add (visitedChild );
597+ }
598+ containsInnerTimeJoinInCurrentSubtree = originalTimeJoin || hasInnerTimeJoinInChildren ;
599+ return result ;
600+ }
601+
556602 @ Override
557603 public PlanNode visitSlidingWindowAggregation (
558604 SlidingWindowAggregationNode node , NodeGroupContext context ) {
@@ -584,9 +630,9 @@ private TRegionReplicaSet calculateDataRegionByChildren(
584630 if (region == null
585631 && context .getNodeDistribution (child .getPlanNodeId ()).getType ()
586632 == NodeDistributionType .SAME_WITH_ALL_CHILDREN ) {
587- return calculateSchemaRegionByChildren (child .getChildren (), context );
633+ region = calculateSchemaRegionByChildren (child .getChildren (), context );
588634 }
589- return region ;
635+ return region == null ? DataPartition . NOT_ASSIGNED : region ;
590636 },
591637 Collectors .counting ()));
592638
@@ -644,6 +690,13 @@ private boolean nodeDistributionIsSame(List<PlanNode> children, NodeGroupContext
644690 }
645691
646692 public PlanNode visit (PlanNode node , NodeGroupContext context ) {
647- return node .accept (this , context );
693+ boolean originalTimeJoin = containsInnerTimeJoinInCurrentSubtree ;
694+ containsInnerTimeJoinInCurrentSubtree = false ;
695+ PlanNode visitedNode = node .accept (this , context );
696+ containsInnerTimeJoinInCurrentSubtree =
697+ originalTimeJoin
698+ || containsInnerTimeJoinInCurrentSubtree
699+ || node instanceof InnerTimeJoinNode ;
700+ return visitedNode ;
648701 }
649702}
0 commit comments