Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 3.9k
[Feat](nereids) support pull up predicate from set operator#39450
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
5d41847da19001d15f9f512ad38a31b32c3bb3b364f5c84b991a93cc4ad57a74859fed497109e78e9b58638198cbf6c399221843539ee7419327850ed7b314b74a06a5a5e271a5b7f1b1bd24358c3e6df13b6815File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -29,7 +29,6 @@ | ||
| import org.apache.doris.nereids.rules.analysis.LogicalSubQueryAliasToLogicalProject; | ||
| import org.apache.doris.nereids.rules.analysis.NormalizeAggregate; | ||
| import org.apache.doris.nereids.rules.expression.CheckLegalityAfterRewrite; | ||
| import org.apache.doris.nereids.rules.expression.ExpressionNormalization; | ||
| import org.apache.doris.nereids.rules.expression.ExpressionNormalizationAndOptimization; | ||
| import org.apache.doris.nereids.rules.expression.ExpressionRewrite; | ||
| import org.apache.doris.nereids.rules.expression.QueryColumnCollector; | ||
| @@ -293,6 +292,21 @@ public class Rewriter extends AbstractBatchJobExecutor { | ||
| topDown(new ConvertInnerOrCrossJoin()), | ||
| topDown(new ProjectOtherJoinConditionForNestedLoopJoin()) | ||
| ), | ||
| topic("Set operation optimization", | ||
| // Do MergeSetOperation first because we hope to match pattern of Distinct SetOperator. | ||
| topDown(new PushProjectThroughUnion(), new MergeProjects()), | ||
| bottomUp(new MergeSetOperations(), new MergeSetOperationsExcept()), | ||
| bottomUp(new PushProjectIntoOneRowRelation()), | ||
| topDown(new MergeOneRowRelationIntoUnion()), | ||
| costBased(topDown(new InferSetOperatorDistinct())), | ||
| topDown(new BuildAggForUnion()), | ||
| bottomUp(new EliminateEmptyRelation()), | ||
morrySnow marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| // when union has empty relation child and constantExprsList is not empty, | ||
| // after EliminateEmptyRelation, project can be pushed into union | ||
| topDown(new PushProjectIntoUnion()) | ||
| ), | ||
| // putting the "Column pruning and infer predicate" topic behind the "Set operation optimization" | ||
| // is because that pulling up predicates from union needs EliminateEmptyRelation in union child | ||
| topic("Column pruning and infer predicate", | ||
| custom(RuleType.COLUMN_PRUNING, ColumnPruning::new), | ||
| custom(RuleType.INFER_PREDICATES, InferPredicates::new), | ||
| @@ -306,24 +320,11 @@ public class Rewriter extends AbstractBatchJobExecutor { | ||
| // after eliminate outer join, we can move some filters to join.otherJoinConjuncts, | ||
| // this can help to translate plan to backend | ||
| topDown(new PushFilterInsideJoin()), | ||
| topDown(new FindHashConditionForJoin()), | ||
| topDown(new ExpressionNormalization()) | ||
morrySnow marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| topDown(new FindHashConditionForJoin()) | ||
| ), | ||
| // this rule should invoke after ColumnPruning | ||
| custom(RuleType.ELIMINATE_UNNECESSARY_PROJECT, EliminateUnnecessaryProject::new), | ||
| topic("Set operation optimization", | ||
| // Do MergeSetOperation first because we hope to match pattern of Distinct SetOperator. | ||
| topDown(new PushProjectThroughUnion(), new MergeProjects()), | ||
| bottomUp(new MergeSetOperations(), new MergeSetOperationsExcept()), | ||
| bottomUp(new PushProjectIntoOneRowRelation()), | ||
| topDown(new MergeOneRowRelationIntoUnion()), | ||
| topDown(new PushProjectIntoUnion()), | ||
| costBased(topDown(new InferSetOperatorDistinct())), | ||
| topDown(new BuildAggForUnion()) | ||
| ), | ||
| topic("Eliminate GroupBy", | ||
| topDown(new EliminateGroupBy(), | ||
| new MergeAggregate(), | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -47,6 +47,7 @@ | ||
| import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewProjectFilterScanRule; | ||
| import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewProjectJoinRule; | ||
| import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewProjectScanRule; | ||
| import org.apache.doris.nereids.rules.expression.ExpressionNormalization; | ||
| import org.apache.doris.nereids.rules.expression.ExpressionOptimization; | ||
| import org.apache.doris.nereids.rules.implementation.AggregateStrategies; | ||
| import org.apache.doris.nereids.rules.implementation.LogicalAssertNumRowsToPhysicalAssertNumRows; | ||
| @@ -87,6 +88,7 @@ | ||
| import org.apache.doris.nereids.rules.implementation.LogicalWindowToPhysicalWindow; | ||
| import org.apache.doris.nereids.rules.rewrite.ConvertOuterJoinToAntiJoin; | ||
| import org.apache.doris.nereids.rules.rewrite.CreatePartitionTopNFromWindow; | ||
| import org.apache.doris.nereids.rules.rewrite.EliminateFilter; | ||
| import org.apache.doris.nereids.rules.rewrite.EliminateOuterJoin; | ||
| import org.apache.doris.nereids.rules.rewrite.MaxMinFilterPushDown; | ||
| import org.apache.doris.nereids.rules.rewrite.MergeFilters; | ||
| @@ -154,7 +156,12 @@ public class RuleSet { | ||
| new PushDownAliasThroughJoin(), | ||
| new PushDownFilterThroughWindow(), | ||
| new PushDownFilterThroughPartitionTopN(), | ||
| new ExpressionOptimization() | ||
| new ExpressionOptimization(), | ||
| // some useless predicates(e.g. 1=1) can be inferred by InferPredicates, | ||
| // the FoldConstantRule in ExpressionNormalization can fold 1=1 to true | ||
| // and EliminateFilter can eliminate the useless filter | ||
| new ExpressionNormalization(), | ||
| new EliminateFilter() | ||
morrySnow marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| ); | ||
| public static final List<Rule> IMPLEMENTATION_RULES = planRuleFactories() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -20,15 +20,22 @@ | ||
| import org.apache.doris.nereids.trees.expressions.Alias; | ||
| import org.apache.doris.nereids.trees.expressions.EqualTo; | ||
| import org.apache.doris.nereids.trees.expressions.Expression; | ||
| import org.apache.doris.nereids.trees.expressions.InPredicate; | ||
| import org.apache.doris.nereids.trees.expressions.NamedExpression; | ||
| import org.apache.doris.nereids.trees.expressions.Slot; | ||
| import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction; | ||
| import org.apache.doris.nereids.trees.expressions.literal.Literal; | ||
| import org.apache.doris.nereids.trees.expressions.literal.NullLiteral; | ||
| import org.apache.doris.nereids.trees.plans.JoinType; | ||
| import org.apache.doris.nereids.trees.plans.Plan; | ||
| import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate; | ||
| import org.apache.doris.nereids.trees.plans.logical.LogicalExcept; | ||
| import org.apache.doris.nereids.trees.plans.logical.LogicalFilter; | ||
| import org.apache.doris.nereids.trees.plans.logical.LogicalIntersect; | ||
| import org.apache.doris.nereids.trees.plans.logical.LogicalJoin; | ||
| import org.apache.doris.nereids.trees.plans.logical.LogicalOneRowRelation; | ||
| import org.apache.doris.nereids.trees.plans.logical.LogicalProject; | ||
| import org.apache.doris.nereids.trees.plans.logical.LogicalUnion; | ||
| import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor; | ||
| import org.apache.doris.nereids.util.ExpressionUtils; | ||
| @@ -38,6 +45,8 @@ | ||
| import com.google.common.collect.Maps; | ||
| import com.google.common.collect.Sets; | ||
| import java.util.HashMap; | ||
| import java.util.HashSet; | ||
| import java.util.IdentityHashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| @@ -60,6 +69,78 @@ public ImmutableSet<Expression> visit(Plan plan, Void context) { | ||
| return ImmutableSet.of(); | ||
| } | ||
| @Override | ||
| public ImmutableSet<Expression> visitLogicalOneRowRelation(LogicalOneRowRelation r, Void context) { | ||
| ImmutableSet.Builder<Expression> predicates = ImmutableSet.builder(); | ||
| for (NamedExpression expr : r.getProjects()) { | ||
| if (expr instanceof Alias && expr.child(0) instanceof Literal) { | ||
| predicates.add(new EqualTo(expr.toSlot(), expr.child(0))); | ||
| } | ||
| } | ||
| return predicates.build(); | ||
| } | ||
| @Override | ||
| public ImmutableSet<Expression> visitLogicalIntersect(LogicalIntersect intersect, Void context) { | ||
| return cacheOrElse(intersect, () -> { | ||
| ImmutableSet.Builder<Expression> builder = ImmutableSet.builder(); | ||
| for (int i = 0; i < intersect.children().size(); ++i) { | ||
| Plan child = intersect.child(i); | ||
| Set<Expression> childFilters = child.accept(this, context); | ||
| if (childFilters.isEmpty()) { | ||
| continue; | ||
| } | ||
| Map<Expression, Expression> replaceMap = new HashMap<>(); | ||
| for (int j = 0; j < intersect.getOutput().size(); ++j) { | ||
| NamedExpression output = intersect.getOutput().get(j); | ||
| replaceMap.put(intersect.getRegularChildOutput(i).get(j), output); | ||
| } | ||
| builder.addAll(ExpressionUtils.replace(childFilters, replaceMap)); | ||
| } | ||
| return getAvailableExpressions(builder.build(), intersect); | ||
| }); | ||
| } | ||
| @Override | ||
| public ImmutableSet<Expression> visitLogicalExcept(LogicalExcept except, Void context) { | ||
| return cacheOrElse(except, () -> { | ||
| if (except.arity() < 1) { | ||
| return ImmutableSet.of(); | ||
| } | ||
| Set<Expression> firstChildFilters = except.child(0).accept(this, context); | ||
| if (firstChildFilters.isEmpty()) { | ||
| return ImmutableSet.of(); | ||
| } | ||
| Map<Expression, Expression> replaceMap = new HashMap<>(); | ||
| for (int i = 0; i < except.getOutput().size(); ++i) { | ||
| NamedExpression output = except.getOutput().get(i); | ||
| replaceMap.put(except.getRegularChildOutput(0).get(i), output); | ||
| } | ||
| return ImmutableSet.copyOf(ExpressionUtils.replace(firstChildFilters, replaceMap)); | ||
| }); | ||
| } | ||
| @Override | ||
| public ImmutableSet<Expression> visitLogicalUnion(LogicalUnion union, Void context) { | ||
| return cacheOrElse(union, () -> { | ||
| if (!union.getConstantExprsList().isEmpty() && union.arity() == 0) { | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why could not process union with both constantExprs and normal children? how about CollaboratorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done | ||
| return getFiltersFromUnionConstExprs(union); | ||
| } else if (union.getConstantExprsList().isEmpty() && union.arity() != 0) { | ||
| return getFiltersFromUnionChild(union, context); | ||
| } else if (!union.getConstantExprsList().isEmpty() && union.arity() != 0) { | ||
| HashSet<Expression> fromChildFilters = new HashSet<>(getFiltersFromUnionChild(union, context)); | ||
| if (fromChildFilters.isEmpty()) { | ||
| return ImmutableSet.of(); | ||
| } | ||
| if (!ExpressionUtils.unionConstExprsSatisfyConjuncts(union, fromChildFilters)) { | ||
| return ImmutableSet.of(); | ||
| } | ||
| return ImmutableSet.copyOf(fromChildFilters); | ||
| } | ||
| return ImmutableSet.of(); | ||
| }); | ||
| } | ||
| @Override | ||
| public ImmutableSet<Expression> visitLogicalFilter(LogicalFilter<? extends Plan> filter, Void context) { | ||
| return cacheOrElse(filter, () -> { | ||
| @@ -77,6 +158,10 @@ public ImmutableSet<Expression> visitLogicalJoin(LogicalJoin<? extends Plan, ? e | ||
| ImmutableSet<Expression> rightPredicates = join.right().accept(this, context); | ||
| predicates.addAll(leftPredicates); | ||
| predicates.addAll(rightPredicates); | ||
| if (join.getJoinType() == JoinType.CROSS_JOIN || join.getJoinType() == JoinType.INNER_JOIN) { | ||
| predicates.addAll(join.getHashJoinConjuncts()); | ||
| predicates.addAll(join.getOtherJoinConjuncts()); | ||
| } | ||
| return getAvailableExpressions(predicates, join); | ||
| }); | ||
| } | ||
| @@ -138,6 +223,9 @@ private ImmutableSet<Expression> cacheOrElse(Plan plan, Supplier<ImmutableSet<Ex | ||
| } | ||
| private ImmutableSet<Expression> getAvailableExpressions(Set<Expression> predicates, Plan plan) { | ||
| if (predicates.isEmpty()) { | ||
| return ImmutableSet.of(); | ||
| } | ||
| Set<Expression> inferPredicates = PredicatePropagation.infer(predicates); | ||
| Builder<Expression> newPredicates = ImmutableSet.builderWithExpectedSize(predicates.size() + 10); | ||
| Set<Slot> outputSet = plan.getOutputSet(); | ||
| @@ -159,4 +247,55 @@ private ImmutableSet<Expression> getAvailableExpressions(Set<Expression> predica | ||
| private boolean hasAgg(Expression expression) { | ||
| return expression.anyMatch(AggregateFunction.class::isInstance); | ||
| } | ||
| private ImmutableSet<Expression> getFiltersFromUnionChild(LogicalUnion union, Void context) { | ||
| Set<Expression> filters = new HashSet<>(); | ||
| for (int i = 0; i < union.getArity(); ++i) { | ||
| Plan child = union.child(i); | ||
| Set<Expression> childFilters = child.accept(this, context); | ||
| if (childFilters.isEmpty()) { | ||
| return ImmutableSet.of(); | ||
| } | ||
| Map<Expression, Expression> replaceMap = new HashMap<>(); | ||
| for (int j = 0; j < union.getOutput().size(); ++j) { | ||
| NamedExpression output = union.getOutput().get(j); | ||
| replaceMap.put(union.getRegularChildOutput(i).get(j), output); | ||
| } | ||
| Set<Expression> unionFilters = ExpressionUtils.replace(childFilters, replaceMap); | ||
| if (0 == i) { | ||
| filters.addAll(unionFilters); | ||
| } else { | ||
| filters.retainAll(unionFilters); | ||
| } | ||
| if (filters.isEmpty()) { | ||
| return ImmutableSet.of(); | ||
| } | ||
| } | ||
| return ImmutableSet.copyOf(filters); | ||
| } | ||
| private ImmutableSet<Expression> getFiltersFromUnionConstExprs(LogicalUnion union) { | ||
| List<List<NamedExpression>> constExprs = union.getConstantExprsList(); | ||
| ImmutableSet.Builder<Expression> filtersFromConstExprs = ImmutableSet.builder(); | ||
| for (int col = 0; col < union.getOutput().size(); ++col) { | ||
| Expression compareExpr = union.getOutput().get(col); | ||
| Set<Expression> options = new HashSet<>(); | ||
| for (List<NamedExpression> constExpr : constExprs) { | ||
| if (constExpr.get(col) instanceof Alias | ||
| && ((Alias) constExpr.get(col)).child() instanceof Literal) { | ||
| options.add(((Alias) constExpr.get(col)).child()); | ||
| } else { | ||
| options.clear(); | ||
| break; | ||
| } | ||
| } | ||
| options.removeIf(option -> option instanceof NullLiteral); | ||
| if (options.size() > 1) { | ||
| filtersFromConstExprs.add(new InPredicate(compareExpr, options)); | ||
| } else if (options.size() == 1) { | ||
| filtersFromConstExprs.add(new EqualTo(compareExpr, options.iterator().next())); | ||
| } | ||
| } | ||
| return filtersFromConstExprs.build(); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add comments to explain why move this rewrite topic here