27
27
import org .apache .doris .nereids .jobs .joinorder .hypergraph .node .StructInfoNode ;
28
28
import org .apache .doris .nereids .memo .Group ;
29
29
import org .apache .doris .nereids .memo .GroupExpression ;
30
+ import org .apache .doris .nereids .rules .exploration .mv .ComparisonResult ;
30
31
import org .apache .doris .nereids .rules .exploration .mv .LogicalCompatibilityContext ;
31
32
import org .apache .doris .nereids .rules .rewrite .PushDownFilterThroughJoin ;
32
33
import org .apache .doris .nereids .trees .expressions .Alias ;
44
45
45
46
import com .google .common .base .Preconditions ;
46
47
import com .google .common .collect .ImmutableList ;
48
+ import com .google .common .collect .ImmutableMap ;
47
49
import com .google .common .collect .ImmutableSet ;
48
50
import com .google .common .collect .Lists ;
51
+ import com .google .common .collect .Sets ;
49
52
50
53
import java .util .ArrayList ;
51
54
import java .util .BitSet ;
52
55
import java .util .HashMap ;
56
+ import java .util .HashSet ;
53
57
import java .util .List ;
54
58
import java .util .Map ;
59
+ import java .util .Map .Entry ;
60
+ import java .util .Optional ;
55
61
import java .util .Set ;
56
62
import java .util .stream .Collectors ;
57
- import javax .annotation .Nullable ;
58
63
59
64
/**
60
65
* The graph is a join graph, whose node is the leaf plan and edge is a join operator.
@@ -267,11 +272,11 @@ private void makeFilterConflictRules(JoinEdge joinEdge) {
267
272
filterEdges .forEach (e -> {
268
273
if (LongBitmap .isSubset (e .getReferenceNodes (), leftSubNodes )
269
274
&& !PushDownFilterThroughJoin .COULD_PUSH_THROUGH_LEFT .contains (joinEdge .getJoinType ())) {
270
- e .addRejectJoin (joinEdge );
275
+ e .addRejectEdge (joinEdge );
271
276
}
272
277
if (LongBitmap .isSubset (e .getReferenceNodes (), rightSubNodes )
273
278
&& !PushDownFilterThroughJoin .COULD_PUSH_THROUGH_RIGHT .contains (joinEdge .getJoinType ())) {
274
- e .addRejectJoin (joinEdge );
279
+ e .addRejectEdge (joinEdge );
275
280
}
276
281
});
277
282
}
@@ -288,19 +293,23 @@ private void makeJoinConflictRules(JoinEdge edgeB) {
288
293
JoinEdge childA = joinEdges .get (i );
289
294
if (!JoinType .isAssoc (childA .getJoinType (), edgeB .getJoinType ())) {
290
295
leftRequired = LongBitmap .newBitmapUnion (leftRequired , childA .getLeftSubNodes (joinEdges ));
296
+ childA .addRejectEdge (edgeB );
291
297
}
292
298
if (!JoinType .isLAssoc (childA .getJoinType (), edgeB .getJoinType ())) {
293
299
leftRequired = LongBitmap .newBitmapUnion (leftRequired , childA .getRightSubNodes (joinEdges ));
300
+ childA .addRejectEdge (edgeB );
294
301
}
295
302
}
296
303
297
304
for (int i = rightSubTreeEdges .nextSetBit (0 ); i >= 0 ; i = rightSubTreeEdges .nextSetBit (i + 1 )) {
298
305
JoinEdge childA = joinEdges .get (i );
299
306
if (!JoinType .isAssoc (edgeB .getJoinType (), childA .getJoinType ())) {
300
307
rightRequired = LongBitmap .newBitmapUnion (rightRequired , childA .getRightSubNodes (joinEdges ));
308
+ childA .addRejectEdge (edgeB );
301
309
}
302
310
if (!JoinType .isRAssoc (edgeB .getJoinType (), childA .getJoinType ())) {
303
311
rightRequired = LongBitmap .newBitmapUnion (rightRequired , childA .getLeftSubNodes (joinEdges ));
312
+ childA .addRejectEdge (edgeB );
304
313
}
305
314
}
306
315
edgeB .setLeftExtendedNodes (leftRequired );
@@ -592,57 +601,75 @@ public int edgeSize() {
592
601
* compare hypergraph
593
602
*
594
603
* @param viewHG the compared hyper graph
595
- * @return null represents not compatible, or return some expression which can
596
- * be pull up from this hyper graph
604
+ * @return Comparison result
597
605
*/
598
- public @ Nullable List <Expression > isLogicCompatible (HyperGraph viewHG , LogicalCompatibilityContext ctx ) {
599
- Map <Edge , Edge > queryToView = constructEdgeMap (viewHG , ctx .getQueryToViewEdgeExpressionMapping ());
606
+ public ComparisonResult isLogicCompatible (HyperGraph viewHG , LogicalCompatibilityContext ctx ) {
607
+ // 1 try to construct a map which can be mapped from edge to edge
608
+ Map <Edge , Edge > queryToView = constructMapWithNode (viewHG , ctx .getQueryToViewNodeIDMapping ());
600
609
601
- // All edge in view must have a mapped edge in query
602
- if (queryToView .size () != viewHG .edgeSize ()) {
603
- return null ;
610
+ // 2. compare them by expression and extract residual expr
611
+ ComparisonResult .Builder builder = new ComparisonResult .Builder ();
612
+ ComparisonResult edgeCompareRes = compareEdgesWithExpr (queryToView , ctx .getQueryToViewEdgeExpressionMapping ());
613
+ if (edgeCompareRes .isInvalid ()) {
614
+ return ComparisonResult .INVALID ;
604
615
}
616
+ builder .addComparisonResult (edgeCompareRes );
605
617
606
- boolean allMatch = queryToView .entrySet ().stream ()
607
- .allMatch (entry ->
608
- compareEdgeWithNode (entry .getKey (), entry .getValue (), ctx .getQueryToViewNodeIDMapping ()));
609
- if (!allMatch ) {
610
- return null ;
618
+ // 3. pull join edge of view is no sense, so reject them
619
+ if (!queryToView .values ().containsAll (viewHG .joinEdges )) {
620
+ return ComparisonResult .INVALID ;
611
621
}
612
622
613
- // join edges must be identical
614
- boolean isJoinIdentical = joinEdges . stream ()
615
- . allMatch ( queryToView :: containsKey );
616
- if (! isJoinIdentical ) {
617
- return null ;
623
+ // 4. process residual edges
624
+ List < Expression > residualQueryJoin =
625
+ processOrphanEdges ( Sets . difference ( Sets . newHashSet ( joinEdges ), queryToView . keySet ()) );
626
+ if (residualQueryJoin == null ) {
627
+ return ComparisonResult . INVALID ;
618
628
}
629
+ builder .addQueryExpressions (residualQueryJoin );
619
630
620
- // extract all top filters
621
- List <FilterEdge > residualFilterEdges = filterEdges .stream ()
622
- .filter (e -> !queryToView .containsKey (e ))
623
- .collect (ImmutableList .toImmutableList ());
624
- if (residualFilterEdges .stream ().anyMatch (e -> !e .isTopFilter ())) {
625
- return null ;
631
+ List <Expression > residualQueryFilter =
632
+ processOrphanEdges (Sets .difference (Sets .newHashSet (filterEdges ), queryToView .keySet ()));
633
+ if (residualQueryFilter == null ) {
634
+ return ComparisonResult .INVALID ;
626
635
}
627
- return residualFilterEdges .stream ()
628
- .flatMap (e -> e .getExpressions ().stream ())
629
- .collect (ImmutableList .toImmutableList ());
636
+ builder .addQueryExpressions (residualQueryFilter );
637
+
638
+ List <Expression > residualViewFilter =
639
+ processOrphanEdges (
640
+ Sets .difference (Sets .newHashSet (viewHG .filterEdges ), Sets .newHashSet (queryToView .values ())));
641
+ if (residualViewFilter == null ) {
642
+ return ComparisonResult .INVALID ;
643
+ }
644
+ builder .addViewExpressions (residualViewFilter );
645
+
646
+ return builder .build ();
630
647
}
631
648
632
- private Map <Edge , Edge > constructEdgeMap (HyperGraph viewHG , Map <Expression , Expression > exprMap ) {
633
- Map <Expression , Edge > exprToEdge = constructExprMap (viewHG );
634
- Map <Edge , Edge > queryToView = new HashMap <>();
635
- joinEdges .stream ()
636
- .filter (e -> !e .getExpressions ().isEmpty ()
637
- && exprMap .containsKey (e .getExpression (0 ))
638
- && compareEdgeWithExpr (e , exprToEdge .get (exprMap .get (e .getExpression (0 ))), exprMap ))
639
- .forEach (e -> queryToView .put (e , exprToEdge .get (exprMap .get (e .getExpression (0 )))));
640
- filterEdges .stream ()
641
- .filter (e -> !e .getExpressions ().isEmpty ()
642
- && exprMap .containsKey (e .getExpression (0 ))
643
- && compareEdgeWithExpr (e , exprToEdge .get (exprMap .get (e .getExpression (0 ))), exprMap ))
644
- .forEach (e -> queryToView .put (e , exprToEdge .get (exprMap .get (e .getExpression (0 )))));
645
- return queryToView ;
649
+ private List <Expression > processOrphanEdges (Set <Edge > edges ) {
650
+ List <Expression > expressions = new ArrayList <>();
651
+ for (Edge edge : edges ) {
652
+ if (!edge .canPullUp ()) {
653
+ return null ;
654
+ }
655
+ expressions .addAll (edge .getExpressions ());
656
+ }
657
+ return expressions ;
658
+ }
659
+
660
+ private Map <Edge , Edge > constructMapWithNode (HyperGraph viewHG , Map <Integer , Integer > nodeMap ) {
661
+ // TODO use hash map to reduce loop
662
+ Map <Edge , Edge > joinEdgeMap = joinEdges .stream ().map (qe -> {
663
+ Optional <JoinEdge > viewEdge = viewHG .joinEdges .stream ()
664
+ .filter (ve -> compareEdgeWithNode (qe , ve , nodeMap )).findFirst ();
665
+ return Pair .of (qe , viewEdge );
666
+ }).filter (e -> e .second .isPresent ()).collect (ImmutableMap .toImmutableMap (p -> p .first , p -> p .second .get ()));
667
+ Map <Edge , Edge > filterEdgeMap = filterEdges .stream ().map (qe -> {
668
+ Optional <FilterEdge > viewEdge = viewHG .filterEdges .stream ()
669
+ .filter (ve -> compareEdgeWithNode (qe , ve , nodeMap )).findFirst ();
670
+ return Pair .of (qe , viewEdge );
671
+ }).filter (e -> e .second .isPresent ()).collect (ImmutableMap .toImmutableMap (p -> p .first , p -> p .second .get ()));
672
+ return ImmutableMap .<Edge , Edge >builder ().putAll (joinEdgeMap ).putAll (filterEdgeMap ).build ();
646
673
}
647
674
648
675
private boolean compareEdgeWithNode (Edge t , Edge o , Map <Integer , Integer > nodeMap ) {
@@ -685,24 +712,40 @@ private boolean compareNodeMap(long bitmap1, long bitmap2, Map<Integer, Integer>
685
712
return bitmap2 == newBitmap1 ;
686
713
}
687
714
688
- private boolean compareEdgeWithExpr (Edge t , Edge o , Map <Expression , Expression > expressionMap ) {
689
- if (t .getExpressions ().size () != o .getExpressions ().size ()) {
690
- return false ;
691
- }
692
- int size = t .getExpressions ().size ();
693
- for (int i = 0 ; i < size ; i ++) {
694
- if (!expressionMap .get (t .getExpression (i )).equals (o .getExpression (i ))) {
695
- return false ;
715
+ private ComparisonResult compareEdgesWithExpr (Map <Edge , Edge > queryToViewedgeMap ,
716
+ Map <Expression , Expression > queryToView ) {
717
+ ComparisonResult .Builder builder = new ComparisonResult .Builder ();
718
+ for (Entry <Edge , Edge > e : queryToViewedgeMap .entrySet ()) {
719
+ ComparisonResult res = compareEdgeWithExpr (e .getKey (), e .getValue (), queryToView );
720
+ if (res .isInvalid ()) {
721
+ return ComparisonResult .INVALID ;
696
722
}
723
+ builder .addComparisonResult (res );
697
724
}
698
- return true ;
725
+ return builder . build () ;
699
726
}
700
727
701
- private Map <Expression , Edge > constructExprMap (HyperGraph hyperGraph ) {
702
- Map <Expression , Edge > exprToEdge = new HashMap <>();
703
- hyperGraph .joinEdges .forEach (edge -> edge .getExpressions ().forEach (expr -> exprToEdge .put (expr , edge )));
704
- hyperGraph .filterEdges .forEach (edge -> edge .getExpressions ().forEach (expr -> exprToEdge .put (expr , edge )));
705
- return exprToEdge ;
728
+ private ComparisonResult compareEdgeWithExpr (Edge query , Edge view , Map <Expression , Expression > queryToView ) {
729
+ Set <? extends Expression > queryExprSet = query .getExpressionSet ();
730
+ Set <? extends Expression > viewExprSet = view .getExpressionSet ();
731
+
732
+ Set <Expression > equalViewExpr = new HashSet <>();
733
+ List <Expression > residualQueryExpr = new ArrayList <>();
734
+ for (Expression queryExpr : queryExprSet ) {
735
+ if (queryToView .containsKey (queryExpr ) && viewExprSet .contains (queryToView .get (queryExpr ))) {
736
+ equalViewExpr .add (queryToView .get (queryExpr ));
737
+ } else {
738
+ residualQueryExpr .add (queryExpr );
739
+ }
740
+ }
741
+ List <Expression > residualViewExpr = ImmutableList .copyOf (Sets .difference (viewExprSet , equalViewExpr ));
742
+ if (!residualViewExpr .isEmpty () && !view .canPullUp ()) {
743
+ return ComparisonResult .INVALID ;
744
+ }
745
+ if (!residualQueryExpr .isEmpty () && !query .canPullUp ()) {
746
+ return ComparisonResult .INVALID ;
747
+ }
748
+ return new ComparisonResult (residualQueryExpr , residualViewExpr );
706
749
}
707
750
708
751
/**
0 commit comments