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 ;
55
- import java .util .Objects ;
59
+ import java .util .Map .Entry ;
60
+ import java .util .Optional ;
56
61
import java .util .Set ;
57
62
import java .util .stream .Collectors ;
58
- import javax .annotation .Nullable ;
59
63
60
64
/**
61
65
* The graph is a join graph, whose node is the leaf plan and edge is a join operator.
@@ -268,11 +272,11 @@ private void makeFilterConflictRules(JoinEdge joinEdge) {
268
272
filterEdges .forEach (e -> {
269
273
if (LongBitmap .isSubset (e .getReferenceNodes (), leftSubNodes )
270
274
&& !PushDownFilterThroughJoin .COULD_PUSH_THROUGH_LEFT .contains (joinEdge .getJoinType ())) {
271
- e .addRejectJoin (joinEdge );
275
+ e .addRejectEdge (joinEdge );
272
276
}
273
277
if (LongBitmap .isSubset (e .getReferenceNodes (), rightSubNodes )
274
278
&& !PushDownFilterThroughJoin .COULD_PUSH_THROUGH_RIGHT .contains (joinEdge .getJoinType ())) {
275
- e .addRejectJoin (joinEdge );
279
+ e .addRejectEdge (joinEdge );
276
280
}
277
281
});
278
282
}
@@ -289,19 +293,23 @@ private void makeJoinConflictRules(JoinEdge edgeB) {
289
293
JoinEdge childA = joinEdges .get (i );
290
294
if (!JoinType .isAssoc (childA .getJoinType (), edgeB .getJoinType ())) {
291
295
leftRequired = LongBitmap .newBitmapUnion (leftRequired , childA .getLeftSubNodes (joinEdges ));
296
+ childA .addRejectEdge (edgeB );
292
297
}
293
298
if (!JoinType .isLAssoc (childA .getJoinType (), edgeB .getJoinType ())) {
294
299
leftRequired = LongBitmap .newBitmapUnion (leftRequired , childA .getRightSubNodes (joinEdges ));
300
+ childA .addRejectEdge (edgeB );
295
301
}
296
302
}
297
303
298
304
for (int i = rightSubTreeEdges .nextSetBit (0 ); i >= 0 ; i = rightSubTreeEdges .nextSetBit (i + 1 )) {
299
305
JoinEdge childA = joinEdges .get (i );
300
306
if (!JoinType .isAssoc (edgeB .getJoinType (), childA .getJoinType ())) {
301
307
rightRequired = LongBitmap .newBitmapUnion (rightRequired , childA .getRightSubNodes (joinEdges ));
308
+ childA .addRejectEdge (edgeB );
302
309
}
303
310
if (!JoinType .isRAssoc (edgeB .getJoinType (), childA .getJoinType ())) {
304
311
rightRequired = LongBitmap .newBitmapUnion (rightRequired , childA .getLeftSubNodes (joinEdges ));
312
+ childA .addRejectEdge (edgeB );
305
313
}
306
314
}
307
315
edgeB .setLeftExtendedNodes (leftRequired );
@@ -593,57 +601,75 @@ public int edgeSize() {
593
601
* compare hypergraph
594
602
*
595
603
* @param viewHG the compared hyper graph
596
- * @return null represents not compatible, or return some expression which can
597
- * be pull up from this hyper graph
604
+ * @return Comparison result
598
605
*/
599
- public @ Nullable List <Expression > isLogicCompatible (HyperGraph viewHG , LogicalCompatibilityContext ctx ) {
600
- 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 ());
601
609
602
- // All edge in view must have a mapped edge in query
603
- if (queryToView .size () != viewHG .edgeSize ()) {
604
- 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 ;
605
615
}
616
+ builder .addComparisonResult (edgeCompareRes );
606
617
607
- boolean allMatch = queryToView .entrySet ().stream ()
608
- .allMatch (entry ->
609
- compareEdgeWithNode (entry .getKey (), entry .getValue (), ctx .getQueryToViewNodeIDMapping ()));
610
- if (!allMatch ) {
611
- 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 ;
612
621
}
613
622
614
- // join edges must be identical
615
- boolean isJoinIdentical = joinEdges . stream ()
616
- . allMatch ( queryToView :: containsKey );
617
- if (! isJoinIdentical ) {
618
- 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 ;
619
628
}
629
+ builder .addQueryExpressions (residualQueryJoin );
620
630
621
- // extract all top filters
622
- List <FilterEdge > residualFilterEdges = filterEdges .stream ()
623
- .filter (e -> !queryToView .containsKey (e ))
624
- .collect (ImmutableList .toImmutableList ());
625
- if (residualFilterEdges .stream ().anyMatch (e -> !e .isTopFilter ())) {
626
- return null ;
631
+ List <Expression > residualQueryFilter =
632
+ processOrphanEdges (Sets .difference (Sets .newHashSet (filterEdges ), queryToView .keySet ()));
633
+ if (residualQueryFilter == null ) {
634
+ return ComparisonResult .INVALID ;
627
635
}
628
- return residualFilterEdges .stream ()
629
- .flatMap (e -> e .getExpressions ().stream ())
630
- .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 ();
631
647
}
632
648
633
- private Map <Edge , Edge > constructEdgeMap (HyperGraph viewHG , Map <Expression , Expression > exprMap ) {
634
- Map <Expression , Edge > exprToEdge = constructExprMap (viewHG );
635
- Map <Edge , Edge > queryToView = new HashMap <>();
636
- joinEdges .stream ()
637
- .filter (e -> !e .getExpressions ().isEmpty ()
638
- && exprMap .containsKey (e .getExpression (0 ))
639
- && compareEdgeWithExpr (e , exprToEdge .get (exprMap .get (e .getExpression (0 ))), exprMap ))
640
- .forEach (e -> queryToView .put (e , exprToEdge .get (exprMap .get (e .getExpression (0 )))));
641
- filterEdges .stream ()
642
- .filter (e -> !e .getExpressions ().isEmpty ()
643
- && exprMap .containsKey (e .getExpression (0 ))
644
- && compareEdgeWithExpr (e , exprToEdge .get (exprMap .get (e .getExpression (0 ))), exprMap ))
645
- .forEach (e -> queryToView .put (e , exprToEdge .get (exprMap .get (e .getExpression (0 )))));
646
- 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 ();
647
673
}
648
674
649
675
private boolean compareEdgeWithNode (Edge t , Edge o , Map <Integer , Integer > nodeMap ) {
@@ -686,24 +712,40 @@ private boolean compareNodeMap(long bitmap1, long bitmap2, Map<Integer, Integer>
686
712
return bitmap2 == newBitmap1 ;
687
713
}
688
714
689
- private boolean compareEdgeWithExpr (Edge t , Edge o , Map <Expression , Expression > expressionMap ) {
690
- if (t .getExpressions ().size () != o .getExpressions ().size ()) {
691
- return false ;
692
- }
693
- int size = t .getExpressions ().size ();
694
- for (int i = 0 ; i < size ; i ++) {
695
- if (!Objects .equals (expressionMap .get (t .getExpression (i )), o .getExpression (i ))) {
696
- 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 ;
697
722
}
723
+ builder .addComparisonResult (res );
698
724
}
699
- return true ;
725
+ return builder . build () ;
700
726
}
701
727
702
- private Map <Expression , Edge > constructExprMap (HyperGraph hyperGraph ) {
703
- Map <Expression , Edge > exprToEdge = new HashMap <>();
704
- hyperGraph .joinEdges .forEach (edge -> edge .getExpressions ().forEach (expr -> exprToEdge .put (expr , edge )));
705
- hyperGraph .filterEdges .forEach (edge -> edge .getExpressions ().forEach (expr -> exprToEdge .put (expr , edge )));
706
- 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 );
707
749
}
708
750
709
751
/**
0 commit comments