20
20
import org .apache .doris .common .Pair ;
21
21
import org .apache .doris .nereids .CascadesContext ;
22
22
import org .apache .doris .nereids .jobs .executor .Rewriter ;
23
+ import org .apache .doris .nereids .properties .DataTrait ;
23
24
import org .apache .doris .nereids .rules .analysis .NormalizeRepeat ;
24
25
import org .apache .doris .nereids .rules .exploration .mv .AbstractMaterializedViewAggregateRule .AggregateExpressionRewriteContext .ExpressionRewriteMode ;
25
26
import org .apache .doris .nereids .rules .exploration .mv .StructInfo .PlanCheckContext ;
45
46
import org .apache .doris .nereids .trees .plans .Plan ;
46
47
import org .apache .doris .nereids .trees .plans .algebra .Repeat ;
47
48
import org .apache .doris .nereids .trees .plans .logical .LogicalAggregate ;
49
+ import org .apache .doris .nereids .trees .plans .logical .LogicalFilter ;
50
+ import org .apache .doris .nereids .trees .plans .logical .LogicalPlan ;
48
51
import org .apache .doris .nereids .trees .plans .logical .LogicalProject ;
49
52
import org .apache .doris .nereids .trees .plans .logical .LogicalRepeat ;
50
53
import org .apache .doris .nereids .trees .plans .visitor .ExpressionLineageReplacer ;
@@ -113,7 +116,7 @@ protected Plan rewriteQueryByView(MatchMode matchMode,
113
116
boolean queryContainsGroupSets = queryAggregate .getSourceRepeat ().isPresent ();
114
117
// If group by expression between query and view is equals, try to rewrite expression directly
115
118
if (!queryContainsGroupSets && isGroupByEquals (queryTopPlanAndAggPair , viewTopPlanAndAggPair ,
116
- viewToQuerySlotMapping , queryStructInfo , viewStructInfo , materializationContext ,
119
+ viewToQuerySlotMapping , queryStructInfo , viewStructInfo , tempRewritedPlan , materializationContext ,
117
120
cascadesContext )) {
118
121
List <Expression > rewrittenQueryExpressions = rewriteExpression (queryTopPlan .getOutput (),
119
122
queryTopPlan ,
@@ -324,18 +327,21 @@ private boolean isGroupByEquals(Pair<Plan, LogicalAggregate<Plan>> queryTopPlanA
324
327
SlotMapping viewToQuerySlotMapping ,
325
328
StructInfo queryStructInfo ,
326
329
StructInfo viewStructInfo ,
330
+ Plan tempRewrittenPlan ,
327
331
MaterializationContext materializationContext ,
328
332
CascadesContext cascadesContext ) {
333
+
334
+ if (materializationContext instanceof SyncMaterializationContext ) {
335
+ // For data correctness, should always add aggregate node if rewritten by sync materialized view
336
+ return false ;
337
+ }
329
338
Plan queryTopPlan = queryTopPlanAndAggPair .key ();
330
339
Plan viewTopPlan = viewTopPlanAndAggPair .key ();
331
340
LogicalAggregate <Plan > queryAggregate = queryTopPlanAndAggPair .value ();
332
341
LogicalAggregate <Plan > viewAggregate = viewTopPlanAndAggPair .value ();
333
342
334
- Set <Expression > queryGroupShuttledExpression = new HashSet <>();
335
- for (Expression queryExpression : ExpressionUtils .shuttleExpressionWithLineage (
336
- queryAggregate .getGroupByExpressions (), queryTopPlan , queryStructInfo .getTableBitSet ())) {
337
- queryGroupShuttledExpression .add (queryExpression );
338
- }
343
+ Set <Expression > queryGroupShuttledExpression = new HashSet <>(ExpressionUtils .shuttleExpressionWithLineage (
344
+ queryAggregate .getGroupByExpressions (), queryTopPlan , queryStructInfo .getTableBitSet ()));
339
345
340
346
// try to eliminate group by dimension by function dependency if group by expression is not in query
341
347
Map <Expression , Expression > viewShuttledExpressionQueryBasedToGroupByExpressionMap = new HashMap <>();
@@ -355,22 +361,112 @@ private boolean isGroupByEquals(Pair<Plan, LogicalAggregate<Plan>> queryTopPlanA
355
361
viewGroupExpressionQueryBased
356
362
);
357
363
}
358
- if (queryGroupShuttledExpression .equals (viewShuttledExpressionQueryBasedToGroupByExpressionMap .values ())) {
364
+ if (queryGroupShuttledExpression .equals (viewShuttledExpressionQueryBasedToGroupByExpressionMap .keySet ())) {
359
365
// return true, if equals directly
360
366
return true ;
361
367
}
368
+
369
+ boolean isGroupByEquals = false ;
370
+ // check is equals by group by eliminate
371
+ isGroupByEquals |= isGroupByEqualsAfterGroupByEliminate (queryGroupShuttledExpression ,
372
+ viewShuttledExpressionQueryBasedToGroupByExpressionMap ,
373
+ groupByExpressionToViewShuttledExpressionQueryBasedMap ,
374
+ viewAggregate ,
375
+ cascadesContext );
376
+ // check is equals by equal filter eliminate
377
+ Optional <LogicalFilter <Plan >> filterOptional = tempRewrittenPlan .collectFirst (LogicalFilter .class ::isInstance );
378
+ if (!filterOptional .isPresent ()) {
379
+ return isGroupByEquals ;
380
+ }
381
+ isGroupByEquals |= isGroupByEqualsAfterEqualFilterEliminate (
382
+ (LogicalPlan ) tempRewrittenPlan ,
383
+ queryGroupShuttledExpression ,
384
+ viewShuttledExpressionQueryBasedToGroupByExpressionMap ,
385
+ materializationContext );
386
+ return isGroupByEquals ;
387
+ }
388
+
389
+ /**
390
+ * Check group by is equals by equal filter eliminate
391
+ * For example query is select a, b, c from t1 where a = 1 and d = 'xx' group by a, b, c;
392
+ * mv is select a, b, c, d from t1 group by a, b, c, d;
393
+ * the group by expression between query and view is equals after equal filter eliminate
394
+ * should not aggregate roll up
395
+ * */
396
+ private static boolean isGroupByEqualsAfterEqualFilterEliminate (
397
+ LogicalPlan tempRewrittenPlan ,
398
+ Set <Expression > queryGroupShuttledExpression ,
399
+ Map <Expression , Expression > viewShuttledExprQueryBasedToViewGroupByExprMap ,
400
+ MaterializationContext materializationContext ) {
401
+
402
+ Map <Expression , Expression > viewShuttledExprToScanExprMapping =
403
+ materializationContext .getShuttledExprToScanExprMapping ().flattenMap ().get (0 );
404
+ Set <Expression > viewShuttledExprQueryBasedSet = viewShuttledExprQueryBasedToViewGroupByExprMap .keySet ();
405
+ // view group by expr can not cover query group by expr
406
+ if (!viewShuttledExprQueryBasedSet .containsAll (queryGroupShuttledExpression )) {
407
+ return false ;
408
+ }
409
+ Set <Expression > viewShouldUniformExpressionSet = new HashSet <>();
410
+ // calc the group by expr which is needed to roll up and should be uniform
411
+ for (Map .Entry <Expression , Expression > expressionEntry :
412
+ viewShuttledExprQueryBasedToViewGroupByExprMap .entrySet ()) {
413
+ if (queryGroupShuttledExpression .contains (expressionEntry .getKey ())) {
414
+ // the group expr which query has, do not require uniform
415
+ continue ;
416
+ }
417
+ viewShouldUniformExpressionSet .add (expressionEntry .getValue ());
418
+ }
419
+
420
+ DataTrait dataTrait = tempRewrittenPlan .computeDataTrait ();
421
+ for (Expression shouldUniformExpr : viewShouldUniformExpressionSet ) {
422
+ Expression viewScanExpression = viewShuttledExprToScanExprMapping .get (shouldUniformExpr );
423
+ if (viewScanExpression == null ) {
424
+ return false ;
425
+ }
426
+ if (!(viewScanExpression instanceof Slot )) {
427
+ return false ;
428
+ }
429
+ if (!dataTrait .isUniform ((Slot ) viewScanExpression )) {
430
+ return false ;
431
+ }
432
+ }
433
+ return true ;
434
+ }
435
+
436
+ /**
437
+ * Check group by is equal or not after group by eliminate by functional dependency
438
+ * Such as query group by expression is (l_orderdate#1, l_supperkey#2)
439
+ * materialized view is group by expression is (l_orderdate#4, l_supperkey#5, l_partkey#6)
440
+ * materialized view expression mapping is
441
+ * {l_orderdate#4:l_orderdate#10, l_supperkey#5:l_supperkey#11, l_partkey#6:l_partkey#12}
442
+ * 1. viewShuttledExpressionQueryBasedToGroupByExpressionMap
443
+ * is {l_orderdate#1:l_orderdate#10, l_supperkey#2:l_supperkey#11}
444
+ * groupByExpressionToViewShuttledExpressionQueryBasedMap
445
+ * is {l_orderdate#10:l_orderdate#1, l_supperkey#11:l_supperkey#2:}
446
+ * 2. construct projects query used by view group expressions
447
+ * projects (l_orderdate#10, l_supperkey#11)
448
+ * 3. try to eliminate materialized view group expression
449
+ * projects (l_orderdate#10, l_supperkey#11)
450
+ * viewAggregate
451
+ * 4. check the viewAggregate group by expression is equals queryAggregate expression or not
452
+ */
453
+ private static boolean isGroupByEqualsAfterGroupByEliminate (Set <Expression > queryGroupShuttledExpression ,
454
+ Map <Expression , Expression > viewShuttledExpressionQueryBasedToGroupByExpressionMap ,
455
+ Map <Expression , Expression > groupByExpressionToViewShuttledExpressionQueryBasedMap ,
456
+ LogicalAggregate <Plan > viewAggregate ,
457
+ CascadesContext cascadesContext ) {
362
458
List <NamedExpression > projects = new ArrayList <>();
459
+ // construct projects query used by view group expressions
363
460
for (Expression expression : queryGroupShuttledExpression ) {
364
- if (! viewShuttledExpressionQueryBasedToGroupByExpressionMap .containsKey (expression )) {
365
- // query group expression is not in view group by expression
461
+ Expression chosenExpression = viewShuttledExpressionQueryBasedToGroupByExpressionMap .get (expression );
462
+ if ( chosenExpression == null ) {
366
463
return false ;
367
464
}
368
- Expression chosenExpression = viewShuttledExpressionQueryBasedToGroupByExpressionMap .get (expression );
369
465
projects .add (chosenExpression instanceof NamedExpression
370
466
? (NamedExpression ) chosenExpression : new Alias (chosenExpression ));
371
467
}
372
468
LogicalProject <LogicalAggregate <Plan >> project = new LogicalProject <>(projects , viewAggregate );
373
- // try to eliminate group by expression which is not in query group by expression
469
+ // try to eliminate view group by expression which is not in query group by expression
374
470
Plan rewrittenPlan = MaterializedViewUtils .rewriteByRules (cascadesContext ,
375
471
childContext -> {
376
472
Rewriter .getCteChildrenRewriter (childContext ,
@@ -383,20 +479,21 @@ private boolean isGroupByEquals(Pair<Plan, LogicalAggregate<Plan>> queryTopPlanA
383
479
if (!aggreagateOptional .isPresent ()) {
384
480
return false ;
385
481
}
482
+ // check result after view group by eliminate by functional dependency
386
483
List <Expression > viewEliminatedGroupByExpressions = aggreagateOptional .get ().getGroupByExpressions ();
387
484
if (viewEliminatedGroupByExpressions .size () != queryGroupShuttledExpression .size ()) {
388
485
return false ;
389
486
}
390
487
Set <Expression > viewGroupShuttledExpressionQueryBased = new HashSet <>();
391
488
for (Expression viewExpression : aggreagateOptional .get ().getGroupByExpressions ()) {
392
- if (!groupByExpressionToViewShuttledExpressionQueryBasedMap .containsKey (viewExpression )) {
489
+ Expression viewExpressionQueryBased =
490
+ groupByExpressionToViewShuttledExpressionQueryBasedMap .get (viewExpression );
491
+ if (viewExpressionQueryBased == null ) {
393
492
return false ;
394
493
}
395
- viewGroupShuttledExpressionQueryBased .add (
396
- groupByExpressionToViewShuttledExpressionQueryBasedMap .get (viewExpression ));
494
+ viewGroupShuttledExpressionQueryBased .add (viewExpressionQueryBased );
397
495
}
398
- return materializationContext instanceof SyncMaterializationContext ? false
399
- : queryGroupShuttledExpression .equals (viewGroupShuttledExpressionQueryBased );
496
+ return queryGroupShuttledExpression .equals (viewGroupShuttledExpressionQueryBased );
400
497
}
401
498
402
499
/**
0 commit comments