44
44
import org .apache .doris .nereids .trees .expressions .Slot ;
45
45
import org .apache .doris .nereids .trees .expressions .SlotReference ;
46
46
import org .apache .doris .nereids .trees .expressions .functions .agg .AggregateFunction ;
47
+ import org .apache .doris .nereids .trees .expressions .functions .scalar .ElementAt ;
47
48
import org .apache .doris .nereids .trees .expressions .functions .scalar .NonNullable ;
48
49
import org .apache .doris .nereids .trees .expressions .functions .scalar .Nullable ;
49
50
import org .apache .doris .nereids .trees .expressions .literal .BooleanLiteral ;
50
51
import org .apache .doris .nereids .trees .expressions .literal .Literal ;
52
+ import org .apache .doris .nereids .trees .expressions .literal .VarcharLiteral ;
51
53
import org .apache .doris .nereids .trees .plans .JoinType ;
52
54
import org .apache .doris .nereids .trees .plans .Plan ;
53
55
import org .apache .doris .nereids .trees .plans .algebra .CatalogRelation ;
56
58
import org .apache .doris .nereids .trees .plans .logical .LogicalOlapScan ;
57
59
import org .apache .doris .nereids .trees .plans .logical .LogicalProject ;
58
60
import org .apache .doris .nereids .trees .plans .logical .LogicalUnion ;
61
+ import org .apache .doris .nereids .types .VariantType ;
59
62
import org .apache .doris .nereids .util .ExpressionUtils ;
60
63
import org .apache .doris .nereids .util .TypeUtils ;
61
64
import org .apache .doris .qe .SessionVariable ;
@@ -114,7 +117,7 @@ public List<Plan> rewrite(Plan queryPlan, CascadesContext cascadesContext) {
114
117
continue ;
115
118
}
116
119
// check mv plan is valid or not
117
- if (!isMaterializationValid (cascadesContext , context )) {
120
+ if (!isMaterializationValid (queryPlan , cascadesContext , context )) {
118
121
continue ;
119
122
}
120
123
// get query struct infos according to the view strut info, if valid query struct infos is empty, bail out
@@ -238,7 +241,7 @@ protected List<Plan> doRewrite(StructInfo queryStructInfo, CascadesContext casca
238
241
// Try to rewrite compensate predicates by using mv scan
239
242
List <Expression > rewriteCompensatePredicates = rewriteExpression (compensatePredicates .toList (),
240
243
queryPlan , materializationContext .getShuttledExprToScanExprMapping (),
241
- viewToQuerySlotMapping , true , queryStructInfo .getTableBitSet ());
244
+ viewToQuerySlotMapping , queryStructInfo .getTableBitSet ());
242
245
if (rewriteCompensatePredicates .isEmpty ()) {
243
246
materializationContext .recordFailReason (queryStructInfo ,
244
247
"Rewrite compensate predicate by view fail" ,
@@ -521,33 +524,20 @@ protected Plan rewriteQueryByView(MatchMode matchMode, StructInfo queryStructInf
521
524
* @param sourcePlan the source plan witch the source expression belong to
522
525
* @param targetExpressionMapping target expression mapping, if finding the expression in key set of the mapping
523
526
* then use the corresponding value of mapping to replace it
524
- * @param targetExpressionNeedSourceBased if targetExpressionNeedSourceBased is true,
525
- * we should make the target expression map key to source based,
526
- * Note: the key expression in targetExpressionMapping should be shuttled. with the method
527
- * ExpressionUtils.shuttleExpressionWithLineage.
528
- * example as following:
529
- * source target
530
- * project(slot 1, 2) project(slot 3, 2, 1)
531
- * scan(table) scan(table)
532
- * then
533
- * transform source to:
534
- * project(slot 2, 1)
535
- * target
536
527
*/
537
528
protected List <Expression > rewriteExpression (List <? extends Expression > sourceExpressionsToWrite , Plan sourcePlan ,
538
- ExpressionMapping targetExpressionMapping , SlotMapping targetToSourceMapping ,
539
- boolean targetExpressionNeedSourceBased , BitSet sourcePlanBitSet ) {
529
+ ExpressionMapping targetExpressionMapping , SlotMapping targetToSourceMapping , BitSet sourcePlanBitSet ) {
540
530
// Firstly, rewrite the target expression using source with inverse mapping
541
531
// then try to use the target expression to represent the query. if any of source expressions
542
532
// can not be represented by target expressions, return null.
543
533
// generate target to target replacement expression mapping, and change target expression to source based
544
534
List <? extends Expression > sourceShuttledExpressions = ExpressionUtils .shuttleExpressionWithLineage (
545
535
sourceExpressionsToWrite , sourcePlan , sourcePlanBitSet );
546
- ExpressionMapping expressionMappingKeySourceBased = targetExpressionNeedSourceBased
547
- ? targetExpressionMapping .keyPermute (targetToSourceMapping ) : targetExpressionMapping ;
536
+ ExpressionMapping expressionMappingKeySourceBased = targetExpressionMapping .keyPermute (targetToSourceMapping );
548
537
// target to target replacement expression mapping, because mv is 1:1 so get first element
549
538
List <Map <Expression , Expression >> flattenExpressionMap = expressionMappingKeySourceBased .flattenMap ();
550
- Map <? extends Expression , ? extends Expression > targetToTargetReplacementMapping = flattenExpressionMap .get (0 );
539
+ Map <Expression , Expression > targetToTargetReplacementMappingQueryBased =
540
+ flattenExpressionMap .get (0 );
551
541
552
542
List <Expression > rewrittenExpressions = new ArrayList <>();
553
543
for (Expression expressionShuttledToRewrite : sourceShuttledExpressions ) {
@@ -557,8 +547,13 @@ protected List<Expression> rewriteExpression(List<? extends Expression> sourceEx
557
547
}
558
548
final Set <Object > slotsToRewrite =
559
549
expressionShuttledToRewrite .collectToSet (expression -> expression instanceof Slot );
550
+
551
+ final Set <SlotReference > variants =
552
+ expressionShuttledToRewrite .collectToSet (expression -> expression instanceof SlotReference
553
+ && ((SlotReference ) expression ).getDataType () instanceof VariantType );
554
+ extendMappingByVariant (variants , targetToTargetReplacementMappingQueryBased );
560
555
Expression replacedExpression = ExpressionUtils .replace (expressionShuttledToRewrite ,
561
- targetToTargetReplacementMapping );
556
+ targetToTargetReplacementMappingQueryBased );
562
557
if (replacedExpression .anyMatch (slotsToRewrite ::contains )) {
563
558
// if contains any slot to rewrite, which means can not be rewritten by target, bail out
564
559
return ImmutableList .of ();
@@ -568,6 +563,94 @@ protected List<Expression> rewriteExpression(List<? extends Expression> sourceEx
568
563
return rewrittenExpressions ;
569
564
}
570
565
566
+ /**
567
+ * if query contains variant slot reference, extend the expression mapping for rewrte
568
+ * such as targetToTargetReplacementMappingQueryBased is
569
+ * id#0 -> id#8
570
+ * type#1 -> type#9
571
+ * payload#4 -> payload#10
572
+ * query variants is payload['issue']['number']#20
573
+ * then we can add payload['issue']['number']#20 -> element_at(element_at(payload#10, 'issue'), 'number')
574
+ * to targetToTargetReplacementMappingQueryBased
575
+ * */
576
+ private void extendMappingByVariant (Set <SlotReference > queryVariants ,
577
+ Map <Expression , Expression > targetToTargetReplacementMappingQueryBased ) {
578
+ if (queryVariants .isEmpty ()) {
579
+ return ;
580
+ }
581
+ Map <List <String >, Expression > viewNameToExprMap = new HashMap <>();
582
+ for (Map .Entry <Expression , Expression > targetExpressionEntry :
583
+ targetToTargetReplacementMappingQueryBased .entrySet ()) {
584
+ if (targetExpressionEntry .getKey () instanceof SlotReference
585
+ && ((SlotReference ) targetExpressionEntry .getKey ()).getDataType () instanceof VariantType ) {
586
+ SlotReference targetSlotReference = (SlotReference ) targetExpressionEntry .getKey ();
587
+ List <String > nameIdentifier = new ArrayList <>(targetSlotReference .getQualifier ());
588
+ nameIdentifier .add (targetSlotReference .getName ());
589
+ nameIdentifier .addAll (targetSlotReference .getSubPath ());
590
+ viewNameToExprMap .put (nameIdentifier , targetExpressionEntry .getValue ());
591
+ }
592
+ }
593
+ if (viewNameToExprMap .isEmpty ()) {
594
+ return ;
595
+ }
596
+ Map <List <String >, SlotReference > queryNameAndExpressionMap = new HashMap <>();
597
+ for (SlotReference slotReference : queryVariants ) {
598
+ List <String > nameIdentifier = new ArrayList <>(slotReference .getQualifier ());
599
+ nameIdentifier .add (slotReference .getName ());
600
+ nameIdentifier .addAll (slotReference .getSubPath ());
601
+ queryNameAndExpressionMap .put (nameIdentifier , slotReference );
602
+ }
603
+ for (Map .Entry <List <String >, ? extends Expression > queryNameEntry : queryNameAndExpressionMap .entrySet ()) {
604
+ Expression minExpr = null ;
605
+ List <String > minCompensateName = null ;
606
+ for (Map .Entry <List <String >, Expression > entry : viewNameToExprMap .entrySet ()) {
607
+ if (!containsAllWithOrder (queryNameEntry .getKey (), entry .getKey ())) {
608
+ continue ;
609
+ }
610
+ List <String > removedQueryName = new ArrayList <>(queryNameEntry .getKey ());
611
+ removedQueryName .removeAll (entry .getKey ());
612
+ if (minCompensateName == null ) {
613
+ minCompensateName = removedQueryName ;
614
+ minExpr = entry .getValue ();
615
+ }
616
+ if (removedQueryName .size () < minCompensateName .size ()) {
617
+ minCompensateName = removedQueryName ;
618
+ minExpr = entry .getValue ();
619
+ }
620
+ }
621
+ if (minExpr != null ) {
622
+ targetToTargetReplacementMappingQueryBased .put (queryNameEntry .getValue (),
623
+ constructElementAt (minExpr , minCompensateName ));
624
+ }
625
+ }
626
+ }
627
+
628
+ private static Expression constructElementAt (Expression target , List <String > atList ) {
629
+ Expression elementAt = target ;
630
+ for (String at : atList ) {
631
+ elementAt = new ElementAt (elementAt , new VarcharLiteral (at ));
632
+ }
633
+ return elementAt ;
634
+ }
635
+
636
+ // source names is contain all target with order or not
637
+ private static boolean containsAllWithOrder (List <String > sourceNames , List <String > targetNames ) {
638
+ if (sourceNames .size () < targetNames .size ()) {
639
+ return false ;
640
+ }
641
+ for (int index = 0 ; index < targetNames .size (); index ++) {
642
+ String sourceName = sourceNames .get (index );
643
+ String targetName = targetNames .get (index );
644
+ if (sourceName == null || targetName == null ) {
645
+ return false ;
646
+ }
647
+ if (!sourceName .equals (targetName )) {
648
+ return false ;
649
+ }
650
+ }
651
+ return true ;
652
+ }
653
+
571
654
/**
572
655
* Normalize expression with query, keep the consistency of exprId and nullable props with
573
656
* query
@@ -753,7 +836,8 @@ protected boolean checkIfRewritten(Plan plan, MaterializationContext context) {
753
836
}
754
837
755
838
// check mv plan is valid or not, this can use cache for performance
756
- private boolean isMaterializationValid (CascadesContext cascadesContext , MaterializationContext context ) {
839
+ private boolean isMaterializationValid (Plan queryPlan , CascadesContext cascadesContext ,
840
+ MaterializationContext context ) {
757
841
long materializationId = context .generateMaterializationIdentifier ().hashCode ();
758
842
Boolean cachedCheckResult = cascadesContext .getMemo ().materializationHasChecked (this .getClass (),
759
843
materializationId );
@@ -764,6 +848,11 @@ private boolean isMaterializationValid(CascadesContext cascadesContext, Material
764
848
context .recordFailReason (context .getStructInfo (),
765
849
"View struct info is invalid" , () -> String .format ("view plan is %s" ,
766
850
context .getStructInfo ().getOriginalPlan ().treeString ()));
851
+ // tmp to location question
852
+ LOG .debug (String .format ("View struct info is invalid, mv identifier is %s, query plan is %s,"
853
+ + "view plan is %s" ,
854
+ context .generateMaterializationIdentifier (), queryPlan .treeString (),
855
+ context .getStructInfo ().getTopPlan ().treeString ()));
767
856
cascadesContext .getMemo ().recordMaterializationCheckResult (this .getClass (), materializationId ,
768
857
false );
769
858
return false ;
@@ -775,12 +864,20 @@ private boolean isMaterializationValid(CascadesContext cascadesContext, Material
775
864
context .recordFailReason (context .getStructInfo (),
776
865
"View struct info is invalid" , () -> String .format ("view plan is %s" ,
777
866
context .getStructInfo ().getOriginalPlan ().treeString ()));
867
+ LOG .debug (String .format ("View struct info is invalid, mv identifier is %s, query plan is %s,"
868
+ + "view plan is %s" ,
869
+ context .generateMaterializationIdentifier (), queryPlan .treeString (),
870
+ context .getStructInfo ().getTopPlan ().treeString ()));
778
871
return false ;
779
872
}
780
873
if (!context .getStructInfo ().isValid ()) {
781
874
context .recordFailReason (context .getStructInfo (),
782
- "View struct info is invalid" , () -> String .format ("view plan is %s" ,
875
+ "View original struct info is invalid" , () -> String .format ("view plan is %s" ,
783
876
context .getStructInfo ().getOriginalPlan ().treeString ()));
877
+ LOG .debug (String .format ("View struct info is invalid, mv identifier is %s, query plan is %s,"
878
+ + "view plan is %s" ,
879
+ context .generateMaterializationIdentifier (), queryPlan .treeString (),
880
+ context .getStructInfo ().getTopPlan ().treeString ()));
784
881
return false ;
785
882
}
786
883
return true ;
0 commit comments