@@ -431,6 +431,49 @@ static bool ExpressionReferencesMinMaxAggregate(Expression &expr,
431431 return found;
432432}
433433
434+ static bool ExpressionReferencesUserSumAggregate (Expression &expr, const CreateMVPlanFacts &facts,
435+ const unordered_map<idx_t , LogicalAggregate *> &aggregates,
436+ idx_t depth = 0 ) {
437+ if (depth > 16 ) {
438+ return false ;
439+ }
440+ if (expr.expression_class == ExpressionClass::BOUND_COLUMN_REF ) {
441+ auto &column_ref = expr.Cast <BoundColumnRefExpression>();
442+ auto aggregate_it = aggregates.find (column_ref.binding .table_index );
443+ if (aggregate_it != aggregates.end ()) {
444+ auto &aggregate = *aggregate_it->second ;
445+ if (column_ref.binding .column_index >= aggregate.expressions .size ()) {
446+ return false ;
447+ }
448+ auto &aggregate_expr = aggregate.expressions [column_ref.binding .column_index ];
449+ if (aggregate_expr->expression_class != ExpressionClass::BOUND_AGGREGATE ) {
450+ return false ;
451+ }
452+ auto &bound_aggregate = aggregate_expr->Cast <BoundAggregateExpression>();
453+ return bound_aggregate.function .name == " sum" &&
454+ !IncrementalTableNames::IsInternalColumn (bound_aggregate.alias );
455+ }
456+ auto projection_it = facts.projections_by_index .find (column_ref.binding .table_index );
457+ if (projection_it == facts.projections_by_index .end ()) {
458+ return false ;
459+ }
460+ auto &projection = *projection_it->second ;
461+ if (column_ref.binding .column_index >= projection.expressions .size ()) {
462+ return false ;
463+ }
464+ return ExpressionReferencesUserSumAggregate (*projection.expressions [column_ref.binding .column_index ], facts,
465+ aggregates, depth + 1 );
466+ }
467+
468+ bool found = false ;
469+ ExpressionIterator::EnumerateChildren (expr, [&](Expression &child) {
470+ if (!found && ExpressionReferencesUserSumAggregate (child, facts, aggregates, depth + 1 )) {
471+ found = true ;
472+ }
473+ });
474+ return found;
475+ }
476+
434477static void FinalizeCreateMVPlanFacts (CreateMVPlanFacts &facts) {
435478 unordered_map<idx_t , LogicalAggregate *> aggregates;
436479 for (auto *aggregate : facts.aggregates ) {
@@ -441,6 +484,14 @@ static void FinalizeCreateMVPlanFacts(CreateMVPlanFacts &facts) {
441484 }
442485 for (auto *projection : facts.projections ) {
443486 for (auto &expr : projection->expressions ) {
487+ Expression *unwrapped = expr.get ();
488+ while (unwrapped->expression_class == ExpressionClass::BOUND_CAST ) {
489+ unwrapped = unwrapped->Cast <BoundCastExpression>().child .get ();
490+ }
491+ if (unwrapped->expression_class != ExpressionClass::BOUND_COLUMN_REF &&
492+ ExpressionReferencesUserSumAggregate (*unwrapped, facts, aggregates)) {
493+ facts.has_computed_sum_aggregate_projection = true ;
494+ }
444495 if (expr->expression_class == ExpressionClass::BOUND_COLUMN_REF && IsHiddenHavingColumn (expr->alias )) {
445496 auto &column_ref = expr->Cast <BoundColumnRefExpression>();
446497 if (IsMinMaxAggregateColumn (column_ref, aggregates)) {
@@ -901,10 +952,34 @@ static void AddJoinEdgesFromFacts(CreateMVPlanFacts &facts) {
901952 }
902953}
903954
955+ bool ProducesAtMostOneRow (LogicalOperator &node) {
956+ LogicalOperator *current = &node;
957+ while ((current->type == LogicalOperatorType::LOGICAL_PROJECTION ||
958+ current->type == LogicalOperatorType::LOGICAL_FILTER ) &&
959+ !current->children .empty ()) {
960+ current = current->children [0 ].get ();
961+ }
962+ if (current->type != LogicalOperatorType::LOGICAL_AGGREGATE_AND_GROUP_BY ) {
963+ return false ;
964+ }
965+ auto &aggregate = current->Cast <LogicalAggregate>();
966+ return aggregate.groups .empty () && aggregate.grouping_sets .size () <= 1 ;
967+ }
968+
904969CreateMVPlanFacts BuildCreateMVPlanFacts (LogicalOperator *plan, const string ¤t_catalog) {
905970 CreateMVPlanFacts facts;
906971 facts.root = plan;
907972 facts.analysis = AnalyzePlan (plan);
973+ LogicalOperator *top = plan;
974+ while (top && top->children .size () == 1 &&
975+ (top->type == LogicalOperatorType::LOGICAL_CREATE_TABLE ||
976+ top->type == LogicalOperatorType::LOGICAL_PROJECTION || top->type == LogicalOperatorType::LOGICAL_FILTER ||
977+ top->type == LogicalOperatorType::LOGICAL_ORDER_BY || top->type == LogicalOperatorType::LOGICAL_LIMIT ||
978+ top->type == LogicalOperatorType::LOGICAL_TOP_N )) {
979+ top = top->children [0 ].get ();
980+ }
981+ facts.has_top_level_redundant_scalar_distinct = top && top->type == LogicalOperatorType::LOGICAL_DISTINCT &&
982+ !top->children .empty () && ProducesAtMostOneRow (*top->children [0 ]);
908983 unordered_map<string, idx_t > next_occurrence;
909984 CollectCreateMVPlanFacts (plan, current_catalog, facts, next_occurrence, false , false );
910985 FinalizeCreateMVPlanFacts (facts);
0 commit comments