You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by li...@apache.org on 2022/07/01 07:39:07 UTC
[doris] branch master updated: [Enhancement](Nereids) push down predicate through join (#10462)
This is an automated email from the ASF dual-hosted git repository.
lingmiao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new f998c0b044 [Enhancement](Nereids) push down predicate through join (#10462)
f998c0b044 is described below
commit f998c0b044ea81efa5057f1b67a232ad7e40da85
Author: shee <13...@users.noreply.github.com>
AuthorDate: Fri Jul 1 15:39:01 2022 +0800
[Enhancement](Nereids) push down predicate through join (#10462)
Add filter operator to join children according to the predicate of filter and join, in order to achieving predicate push-down
Pattern:
```
filter
|
join
/ \
child child
```
Transform:
```
filter
|
join
/ \
filter filter
| |
child child
```
---
.../java/org/apache/doris/nereids/jobs/Job.java | 3 +-
.../nereids/jobs/rewrite/RewriteTopDownJob.java | 6 +-
.../java/org/apache/doris/nereids/memo/Memo.java | 3 +
.../doris/nereids/parser/LogicalPlanBuilder.java | 3 +-
.../apache/doris/nereids/properties/OrderKey.java | 5 +
.../org/apache/doris/nereids/rules/RuleType.java | 1 +
.../expression/rewrite/ExpressionRuleExecutor.java | 8 +-
.../rewrite/logical/PushPredicateThroughJoin.java | 165 +++++++++++++
.../doris/nereids/trees/expressions/Add.java | 3 +
.../trees/expressions/CompoundPredicate.java | 29 +++
.../trees/expressions/IterationVisitor.java | 161 +++++++++++++
.../doris/nereids/trees/expressions/Literal.java | 6 +
.../nereids/trees/expressions/SlotExtractor.java | 68 ++++++
.../apache/doris/nereids/util/ExpressionUtils.java | 140 +++++++++++
.../rewrite/logical/PushDownPredicateTest.java | 256 +++++++++++++++++++++
.../org/apache/doris/nereids/ssb/SSBUtils.java | 14 +-
.../doris/nereids/util/ExpressionUtilsTest.java | 101 ++++++++
17 files changed, 960 insertions(+), 12 deletions(-)
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/Job.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/Job.java
index 97c2a375e6..3f7ef0200c 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/Job.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/Job.java
@@ -25,6 +25,7 @@ import org.apache.doris.nereids.rules.RuleSet;
import org.apache.doris.nereids.trees.TreeNode;
import java.util.List;
+import java.util.Objects;
import java.util.stream.Collectors;
/**
@@ -57,7 +58,7 @@ public abstract class Job<NODE_TYPE extends TreeNode<NODE_TYPE>> {
public List<Rule<NODE_TYPE>> getValidRules(GroupExpression groupExpression,
List<Rule<NODE_TYPE>> candidateRules) {
return candidateRules.stream()
- .filter(rule -> rule.getPattern().matchOperator(groupExpression.getOperator())
+ .filter(rule -> Objects.nonNull(rule) && rule.getPattern().matchOperator(groupExpression.getOperator())
&& groupExpression.notApplied(rule)).collect(Collectors.toList());
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/rewrite/RewriteTopDownJob.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/rewrite/RewriteTopDownJob.java
index 89c7f70d6b..4504109d68 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/rewrite/RewriteTopDownJob.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/rewrite/RewriteTopDownJob.java
@@ -72,7 +72,9 @@ public class RewriteTopDownJob extends Job<Plan> {
Preconditions.checkArgument(afters.size() == 1);
Plan after = afters.get(0);
if (after != before) {
- context.getOptimizerContext().getMemo().copyIn(after, group, rule.isRewrite());
+ GroupExpression expression = context.getOptimizerContext().getMemo()
+ .copyIn(after, group, rule.isRewrite());
+ expression.setApplied(rule);
pushTask(new RewriteTopDownJob(group, rules, context));
return;
}
@@ -80,7 +82,7 @@ public class RewriteTopDownJob extends Job<Plan> {
logicalExpression.setApplied(rule);
}
- for (Group childGroup : logicalExpression.children()) {
+ for (Group childGroup : group.getLogicalExpression().children()) {
pushTask(new RewriteTopDownJob(childGroup, rules, context));
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java
index 761ac4c342..b125a85ec8 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java
@@ -98,6 +98,9 @@ public class Memo {
childrenNode.add(groupToTreeNode(child));
}
Plan result = logicalExpression.getOperator().toTreeNode(logicalExpression);
+ if (result.children().size() == 0) {
+ return result;
+ }
return result.withChildren(childrenNode);
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
index f19c8fb9a7..e63601dc65 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
@@ -39,6 +39,7 @@ import org.apache.doris.nereids.DorisParser.MultipartIdentifierContext;
import org.apache.doris.nereids.DorisParser.NamedExpressionContext;
import org.apache.doris.nereids.DorisParser.NamedExpressionSeqContext;
import org.apache.doris.nereids.DorisParser.NullLiteralContext;
+import org.apache.doris.nereids.DorisParser.ParenthesizedExpressionContext;
import org.apache.doris.nereids.DorisParser.PredicateContext;
import org.apache.doris.nereids.DorisParser.PredicatedContext;
import org.apache.doris.nereids.DorisParser.QualifiedNameContext;
@@ -402,7 +403,7 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
}
@Override
- public Expression visitParenthesizedExpression(DorisParser.ParenthesizedExpressionContext ctx) {
+ public Expression visitParenthesizedExpression(ParenthesizedExpressionContext ctx) {
return getExpression(ctx.expression());
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/OrderKey.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/OrderKey.java
index 9cbf800d6e..2bd91c6540 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/OrderKey.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/OrderKey.java
@@ -53,4 +53,9 @@ public class OrderKey {
public boolean isNullFirst() {
return nullFirst;
}
+
+ @Override
+ public String toString() {
+ return expr.sql();
+ }
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
index 2e961f27ee..9cd9b42ccb 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
@@ -39,6 +39,7 @@ public enum RuleType {
// rewrite rules
COLUMN_PRUNE_PROJECTION(RuleTypeClass.REWRITE),
+ PUSH_DOWN_PREDICATE_THROUGH_JOIN(RuleTypeClass.REWRITE),
// exploration rules
LOGICAL_JOIN_COMMUTATIVE(RuleTypeClass.EXPLORATION),
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRuleExecutor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRuleExecutor.java
index 547266cea5..55c2ef0060 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRuleExecutor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRuleExecutor.java
@@ -21,6 +21,7 @@ import org.apache.doris.nereids.rules.expression.rewrite.rules.NormalizeExpressi
import org.apache.doris.nereids.rules.expression.rewrite.rules.SimplifyNotExprRule;
import org.apache.doris.nereids.trees.expressions.Expression;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import java.util.List;
@@ -30,7 +31,7 @@ import java.util.List;
*/
public class ExpressionRuleExecutor {
- public static final List<ExpressionRewriteRule> REWRITE_RULES = Lists.newArrayList(
+ public static final List<ExpressionRewriteRule> REWRITE_RULES = ImmutableList.of(
new SimplifyNotExprRule(),
new NormalizeExpressionRule()
);
@@ -38,6 +39,11 @@ public class ExpressionRuleExecutor {
private final ExpressionRewriteContext ctx;
private final List<ExpressionRewriteRule> rules;
+ public ExpressionRuleExecutor() {
+ this.rules = REWRITE_RULES;
+ this.ctx = new ExpressionRewriteContext();
+ }
+
public ExpressionRuleExecutor(List<ExpressionRewriteRule> rules) {
this.rules = rules;
this.ctx = new ExpressionRewriteContext();
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushPredicateThroughJoin.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushPredicateThroughJoin.java
new file mode 100644
index 0000000000..429c495623
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushPredicateThroughJoin.java
@@ -0,0 +1,165 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.logical;
+
+import org.apache.doris.nereids.operators.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.operators.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.rules.Rule;
+import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.rules.expression.rewrite.ExpressionRuleExecutor;
+import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
+import org.apache.doris.nereids.trees.expressions.ComparisonPredicate;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.Literal;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotExtractor;
+import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalBinaryPlan;
+import org.apache.doris.nereids.util.ExpressionUtils;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * Push the predicate in the LogicalFilter or LogicalJoin to the join children.
+ * For example:
+ * select a.k1,b.k1 from a join b on a.k1 = b.k1 and a.k2 > 2 and b.k2 > 5 where a.k1 > 1 and b.k1 > 2
+ * Logical plan tree:
+ * project
+ * |
+ * filter (a.k1 > 1 and b.k1 > 2)
+ * |
+ * join (a.k1 = b.k1 and a.k2 > 2 and b.k2 > 5)
+ * / \
+ * scan scan
+ * transformed:
+ * project
+ * |
+ * join (a.k1 = b.k1)
+ * / \
+ * filter(a.k1 > 1 and a.k2 > 2 ) filter(b.k1 > 2 and b.k2 > 5)
+ * | |
+ * scan scan
+ * todo: Now, only support eq on condition for inner join, support other case later
+ */
+public class PushPredicateThroughJoin extends OneRewriteRuleFactory {
+
+ @Override
+ public Rule<Plan> build() {
+ return logicalFilter(innerLogicalJoin()).then(filter -> {
+
+ LogicalJoin joinOp = filter.child().operator;
+
+ Expression wherePredicates = filter.operator.getPredicates();
+ Expression onPredicates = Literal.TRUE_LITERAL;
+
+ List<Expression> otherConditions = Lists.newArrayList();
+ List<Expression> eqConditions = Lists.newArrayList();
+
+ if (joinOp.getCondition().isPresent()) {
+ onPredicates = joinOp.getCondition().get();
+ }
+
+ List<Slot> leftInput = filter.child().left().getOutput();
+ List<Slot> rightInput = filter.child().right().getOutput();
+
+ ExpressionUtils.extractConjunct(ExpressionUtils.add(onPredicates, wherePredicates)).forEach(predicate -> {
+ if (Objects.nonNull(getJoinCondition(predicate, leftInput, rightInput))) {
+ eqConditions.add(predicate);
+ } else {
+ otherConditions.add(predicate);
+ }
+ });
+
+ List<Expression> leftPredicates = Lists.newArrayList();
+ List<Expression> rightPredicates = Lists.newArrayList();
+
+ for (Expression p : otherConditions) {
+ Set<Slot> slots = SlotExtractor.extractSlot(p);
+ if (slots.isEmpty()) {
+ leftPredicates.add(p);
+ rightPredicates.add(p);
+ }
+ if (leftInput.containsAll(slots)) {
+ leftPredicates.add(p);
+ }
+ if (rightInput.containsAll(slots)) {
+ rightPredicates.add(p);
+ }
+ }
+
+ otherConditions.removeAll(leftPredicates);
+ otherConditions.removeAll(rightPredicates);
+ otherConditions.addAll(eqConditions);
+ Expression joinConditions = ExpressionUtils.add(otherConditions);
+
+ return pushDownPredicate(filter.child(), joinConditions, leftPredicates, rightPredicates);
+ }).toRule(RuleType.PUSH_DOWN_PREDICATE_THROUGH_JOIN);
+ }
+
+ private Plan pushDownPredicate(LogicalBinaryPlan<LogicalJoin, GroupPlan, GroupPlan> joinPlan,
+ Expression joinConditions, List<Expression> leftPredicates, List<Expression> rightPredicates) {
+
+ Expression left = ExpressionUtils.add(leftPredicates);
+ Expression right = ExpressionUtils.add(rightPredicates);
+ //todo expr should optimize again using expr rewrite
+ ExpressionRuleExecutor exprRewriter = new ExpressionRuleExecutor();
+ Plan leftPlan = joinPlan.left();
+ Plan rightPlan = joinPlan.right();
+ if (!left.equals(Literal.TRUE_LITERAL)) {
+ leftPlan = plan(new LogicalFilter(exprRewriter.rewrite(left)), leftPlan);
+ }
+
+ if (!right.equals(Literal.TRUE_LITERAL)) {
+ rightPlan = plan(new LogicalFilter(exprRewriter.rewrite(right)), rightPlan);
+ }
+
+ return plan(new LogicalJoin(joinPlan.operator.getJoinType(), Optional.of(joinConditions)), leftPlan, rightPlan);
+ }
+
+ private Expression getJoinCondition(Expression predicate, List<Slot> leftOutputs, List<Slot> rightOutputs) {
+ if (!(predicate instanceof ComparisonPredicate)) {
+ return null;
+ }
+
+ ComparisonPredicate comparison = (ComparisonPredicate) predicate;
+
+ Set<Slot> leftSlots = SlotExtractor.extractSlot(comparison.left());
+ Set<Slot> rightSlots = SlotExtractor.extractSlot(comparison.right());
+
+ if (!(leftSlots.size() >= 1 && rightSlots.size() >= 1)) {
+ return null;
+ }
+
+ Set<Slot> left = Sets.newLinkedHashSet(leftOutputs);
+ Set<Slot> right = Sets.newLinkedHashSet(rightOutputs);
+
+ if ((left.containsAll(leftSlots) && right.containsAll(rightSlots)) || (left.containsAll(rightSlots)
+ && right.containsAll(leftSlots))) {
+ return predicate;
+ }
+
+ return null;
+ }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Add.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Add.java
index 27bf07a430..dac9d919e2 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Add.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Add.java
@@ -48,4 +48,7 @@ public class Add<LEFT_CHILD_TYPE extends Expression, RIGHT_CHILD_TYPE extends Ex
}
+ public String toString() {
+ return sql();
+ }
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/CompoundPredicate.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/CompoundPredicate.java
index f09e1ae4f6..39dcc2f4f4 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/CompoundPredicate.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/CompoundPredicate.java
@@ -19,6 +19,9 @@ package org.apache.doris.nereids.trees.expressions;
import org.apache.doris.nereids.trees.NodeType;
+import java.util.List;
+import java.util.Objects;
+
/**
* Compound predicate expression.
* Such as &&,||,AND,OR.
@@ -53,4 +56,30 @@ public class CompoundPredicate<LEFT_CHILD_TYPE extends Expression, RIGHT_CHILD_T
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitCompoundPredicate(this, context);
}
+
+ public NodeType flip() {
+ if (getType() == NodeType.AND) {
+ return NodeType.OR;
+ }
+ return NodeType.AND;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ CompoundPredicate other = (CompoundPredicate) o;
+ return (type == other.getType()) && Objects.equals(left(), other.left())
+ && Objects.equals(right(), other.right());
+ }
+
+ @Override
+ public Expression withChildren(List<Expression> children) {
+ return new CompoundPredicate<>(getType(), children.get(0), children.get(1));
+ }
}
+
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/IterationVisitor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/IterationVisitor.java
new file mode 100644
index 0000000000..34c3baac12
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/IterationVisitor.java
@@ -0,0 +1,161 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.expressions;
+
+import org.apache.doris.nereids.trees.expressions.functions.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.BoundFunction;
+
+/**
+ * Iterative traversal of an expression.
+ */
+public abstract class IterationVisitor<C> extends DefaultExpressionVisitor<Void, C> {
+
+ @Override
+ public Void visit(Expression expr, C context) {
+ return expr.accept(this, context);
+ }
+
+ @Override
+ public Void visitNot(Not expr, C context) {
+ visit(expr.child(), context);
+ return null;
+ }
+
+ @Override
+ public Void visitCompoundPredicate(CompoundPredicate expr, C context) {
+ visit(expr.left(), context);
+ visit(expr.right(), context);
+ return null;
+ }
+
+ @Override
+ public Void visitLiteral(Literal literal, C context) {
+ return null;
+ }
+
+ @Override
+ public Void visitArithmetic(Arithmetic arithmetic, C context) {
+ visit(arithmetic.child(0), context);
+ if (arithmetic.getArithmeticOperator().isBinary()) {
+ visit(arithmetic.child(1), context);
+ }
+ return null;
+ }
+
+ @Override
+ public Void visitBetween(Between betweenPredicate, C context) {
+ visit(betweenPredicate.getCompareExpr(), context);
+ visit(betweenPredicate.getLowerBound(), context);
+ visit(betweenPredicate.getUpperBound(), context);
+ return null;
+ }
+
+ @Override
+ public Void visitAlias(Alias alias, C context) {
+ return visitNamedExpression(alias, context);
+ }
+
+ @Override
+ public Void visitComparisonPredicate(ComparisonPredicate cp, C context) {
+ visit(cp.left(), context);
+ visit(cp.right(), context);
+ return null;
+ }
+
+ @Override
+ public Void visitEqualTo(EqualTo equalTo, C context) {
+ return visitComparisonPredicate(equalTo, context);
+ }
+
+ @Override
+ public Void visitGreaterThan(GreaterThan greaterThan, C context) {
+ return visitComparisonPredicate(greaterThan, context);
+ }
+
+ @Override
+ public Void visitGreaterThanEqual(GreaterThanEqual greaterThanEqual, C context) {
+ return visitComparisonPredicate(greaterThanEqual, context);
+ }
+
+ @Override
+ public Void visitLessThan(LessThan lessThan, C context) {
+ return visitComparisonPredicate(lessThan, context);
+ }
+
+ @Override
+ public Void visitLessThanEqual(LessThanEqual lessThanEqual, C context) {
+ return visitComparisonPredicate(lessThanEqual, context);
+ }
+
+ @Override
+ public Void visitNullSafeEqual(NullSafeEqual nullSafeEqual, C context) {
+ return visitComparisonPredicate(nullSafeEqual, context);
+ }
+
+ @Override
+ public Void visitSlot(Slot slot, C context) {
+ return null;
+ }
+
+ @Override
+ public Void visitNamedExpression(NamedExpression namedExpression, C context) {
+ for (Expression child : namedExpression.children()) {
+ visit(child, context);
+ }
+ return null;
+ }
+
+ @Override
+ public Void visitBoundFunction(BoundFunction boundFunction, C context) {
+ for (Expression argument : boundFunction.getArguments()) {
+ visit(argument, context);
+ }
+ return null;
+ }
+
+ @Override
+ public Void visitAggregateFunction(AggregateFunction aggregateFunction, C context) {
+ return visitBoundFunction(aggregateFunction, context);
+ }
+
+ @Override
+ public Void visitAdd(Add add, C context) {
+ return visitArithmetic(add, context);
+ }
+
+ @Override
+ public Void visitSubtract(Subtract subtract, C context) {
+ return visitArithmetic(subtract, context);
+ }
+
+ @Override
+ public Void visitMultiply(Multiply multiply, C context) {
+ return visitArithmetic(multiply, context);
+ }
+
+ @Override
+ public Void visitDivide(Divide divide, C context) {
+ return visitArithmetic(divide, context);
+ }
+
+ @Override
+ public Void visitMod(Mod mod, C context) {
+ return visitArithmetic(mod, context);
+ }
+
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Literal.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Literal.java
index 912b72705e..0d958f38c2 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Literal.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Literal.java
@@ -34,6 +34,8 @@ import java.util.Objects;
* TODO: Increase the implementation of sub expression. such as Integer.
*/
public class Literal extends Expression implements LeafExpression {
+ public static final Literal TRUE_LITERAL = new Literal(true);
+ public static final Literal FALSE_LITERAL = new Literal(false);
private final DataType dataType;
private final Object value;
@@ -97,6 +99,10 @@ public class Literal extends Expression implements LeafExpression {
return value == null;
}
+ public static Literal of(Object value) {
+ return new Literal(value);
+ }
+
@Override
public boolean isConstant() {
return true;
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotExtractor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotExtractor.java
new file mode 100644
index 0000000000..68f0b8d2de
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotExtractor.java
@@ -0,0 +1,68 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.expressions;
+
+import com.clearspring.analytics.util.Lists;
+import com.google.common.collect.Sets;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Extracts the SlotReference contained in the expression.
+ */
+public class SlotExtractor extends IterationVisitor<List<Slot>> {
+
+ /**
+ * extract slot reference.
+ */
+ public static Set<Slot> extractSlot(Collection<Expression> expressions) {
+
+ Set<Slot> slots = Sets.newLinkedHashSet();
+ for (Expression expression : expressions) {
+ slots.addAll(extractSlot(expression));
+ }
+ return slots;
+ }
+
+ /**
+ * extract slot reference.
+ */
+ public static Set<Slot> extractSlot(Expression... expressions) {
+
+ Set<Slot> slots = Sets.newLinkedHashSet();
+ for (Expression expression : expressions) {
+ slots.addAll(extractSlot(expression));
+ }
+ return slots;
+ }
+
+ private static List<Slot> extractSlot(Expression expression) {
+ List<Slot> slots = Lists.newArrayList();
+ new SlotExtractor().visit(expression, slots);
+ return slots;
+ }
+
+
+ @Override
+ public Void visitSlotReference(SlotReference slotReference, List<Slot> context) {
+ context.add(slotReference);
+ return null;
+ }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java
new file mode 100644
index 0000000000..b513038b35
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java
@@ -0,0 +1,140 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.util;
+
+import org.apache.doris.nereids.trees.NodeType;
+import org.apache.doris.nereids.trees.expressions.CompoundPredicate;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.Literal;
+
+import com.google.common.collect.Lists;
+
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * Expression rewrite helper class.
+ */
+public class ExpressionUtils {
+
+ public static boolean isConstant(Expression expr) {
+ return expr.isConstant();
+ }
+
+ public static List<Expression> extractConjunct(Expression expr) {
+ return extract(NodeType.AND, expr);
+ }
+
+
+ public static List<Expression> extractDisjunct(Expression expr) {
+ return extract(NodeType.OR, expr);
+ }
+
+ public static List<Expression> extract(CompoundPredicate expr) {
+ return extract(expr.getType(), expr);
+ }
+
+ private static List<Expression> extract(NodeType op, Expression expr) {
+ List<Expression> result = Lists.newArrayList();
+ extract(op, expr, result);
+ return result;
+ }
+
+ private static void extract(NodeType op, Expression expr, List<Expression> result) {
+ if (expr instanceof CompoundPredicate && expr.getType() == op) {
+ CompoundPredicate predicate = (CompoundPredicate) expr;
+ extract(op, predicate.left(), result);
+ extract(op, predicate.right(), result);
+ } else {
+ result.add(expr);
+ }
+ }
+
+
+ public static Expression add(List<Expression> expressions) {
+ return combine(NodeType.AND, expressions);
+ }
+
+ public static Expression add(Expression... expressions) {
+ return combine(NodeType.AND, Lists.newArrayList(expressions));
+ }
+
+ public static Expression or(Expression... expressions) {
+ return combine(NodeType.OR, Lists.newArrayList(expressions));
+ }
+
+ public static Expression or(List<Expression> expressions) {
+ return combine(NodeType.OR, expressions);
+ }
+
+ /**
+ * Use AND/OR to combine expressions together.
+ */
+ public static Expression combine(NodeType op, List<Expression> expressions) {
+
+ Objects.requireNonNull(expressions, "expressions is null");
+
+ if (expressions.size() == 0) {
+ if (op == NodeType.AND) {
+ return new Literal(true);
+ }
+ if (op == NodeType.OR) {
+ return new Literal(false);
+ }
+ }
+
+ if (expressions.size() == 1) {
+ return expressions.get(0);
+ }
+
+ List<Expression> distinctExpressions = Lists.newArrayList(new LinkedHashSet<>(expressions));
+ if (op == NodeType.AND) {
+ if (distinctExpressions.contains(Literal.FALSE_LITERAL)) {
+ return Literal.FALSE_LITERAL;
+ }
+ distinctExpressions = distinctExpressions.stream().filter(p -> !p.equals(Literal.TRUE_LITERAL))
+ .collect(Collectors.toList());
+ }
+
+ if (op == NodeType.OR) {
+ if (distinctExpressions.contains(Literal.TRUE_LITERAL)) {
+ return Literal.TRUE_LITERAL;
+ }
+ distinctExpressions = distinctExpressions.stream().filter(p -> !p.equals(Literal.FALSE_LITERAL))
+ .collect(Collectors.toList());
+ }
+
+ List<List<Expression>> partitions = Lists.partition(distinctExpressions, 2);
+ List<Expression> result = new LinkedList<>();
+
+ for (List<Expression> partition : partitions) {
+ if (partition.size() == 2) {
+ result.add(new CompoundPredicate(op, partition.get(0), partition.get(1)));
+ }
+ if (partition.size() == 1) {
+ result.add(partition.get(0));
+ }
+ }
+
+ return combine(op, result);
+ }
+}
+
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushDownPredicateTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushDownPredicateTest.java
new file mode 100644
index 0000000000..ee07b99194
--- /dev/null
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushDownPredicateTest.java
@@ -0,0 +1,256 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite.logical;
+
+import org.apache.doris.catalog.AggregateType;
+import org.apache.doris.catalog.Column;
+import org.apache.doris.catalog.Table;
+import org.apache.doris.catalog.Type;
+import org.apache.doris.nereids.OptimizerContext;
+import org.apache.doris.nereids.PlannerContext;
+import org.apache.doris.nereids.jobs.rewrite.RewriteTopDownJob;
+import org.apache.doris.nereids.memo.Group;
+import org.apache.doris.nereids.memo.Memo;
+import org.apache.doris.nereids.operators.Operator;
+import org.apache.doris.nereids.operators.plans.JoinType;
+import org.apache.doris.nereids.operators.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.operators.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.operators.plans.logical.LogicalOlapScan;
+import org.apache.doris.nereids.operators.plans.logical.LogicalProject;
+import org.apache.doris.nereids.properties.PhysicalProperties;
+import org.apache.doris.nereids.rules.Rule;
+import org.apache.doris.nereids.trees.expressions.Add;
+import org.apache.doris.nereids.trees.expressions.Between;
+import org.apache.doris.nereids.trees.expressions.EqualTo;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.GreaterThan;
+import org.apache.doris.nereids.trees.expressions.Literal;
+import org.apache.doris.nereids.trees.expressions.Subtract;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.Plans;
+import org.apache.doris.nereids.util.ExpressionUtils;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * plan rewrite ut.
+ */
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class PushDownPredicateTest implements Plans {
+
+ private Table student;
+ private Table score;
+ private Table course;
+
+ private Plan rStudent;
+ private Plan rScore;
+ private Plan rCourse;
+
+ /**
+ * ut before.
+ */
+ @BeforeAll
+ public final void beforeAll() {
+ student = new Table(0L, "student", Table.TableType.OLAP,
+ ImmutableList.<Column>of(new Column("id", Type.INT, true, AggregateType.NONE, "0", ""),
+ new Column("name", Type.STRING, true, AggregateType.NONE, "", ""),
+ new Column("age", Type.INT, true, AggregateType.NONE, "", "")));
+
+ score = new Table(0L, "score", Table.TableType.OLAP,
+ ImmutableList.<Column>of(new Column("sid", Type.INT, true, AggregateType.NONE, "0", ""),
+ new Column("cid", Type.INT, true, AggregateType.NONE, "", ""),
+ new Column("grade", Type.DOUBLE, true, AggregateType.NONE, "", "")));
+
+ course = new Table(0L, "course", Table.TableType.OLAP,
+ ImmutableList.<Column>of(new Column("cid", Type.INT, true, AggregateType.NONE, "0", ""),
+ new Column("name", Type.STRING, true, AggregateType.NONE, "", ""),
+ new Column("teacher", Type.STRING, true, AggregateType.NONE, "", "")));
+
+ rStudent = plan(new LogicalOlapScan(student, ImmutableList.of("student")));
+
+ rScore = plan(new LogicalOlapScan(score, ImmutableList.of("score")));
+
+ rCourse = plan(new LogicalOlapScan(course, ImmutableList.of("course")));
+ }
+
+ @Test
+ public void pushDownPredicateIntoScanTest1() {
+ // select id,name,grade from student join score on student.id = score.sid and student.id > 1
+ // and score.cid > 2 where student.age > 18 and score.grade > 60
+ Expression onCondition1 = new EqualTo<>(rStudent.getOutput().get(0), rScore.getOutput().get(0));
+ Expression onCondition2 = new GreaterThan<>(rStudent.getOutput().get(0), Literal.of(1));
+ Expression onCondition3 = new GreaterThan<>(rScore.getOutput().get(0), Literal.of(2));
+ Expression onCondition = ExpressionUtils.add(onCondition1, onCondition2, onCondition3);
+
+ Expression whereCondition1 = new GreaterThan<>(rStudent.getOutput().get(1), Literal.of(18));
+ Expression whereCondition2 = new GreaterThan<>(rScore.getOutput().get(2), Literal.of(60));
+ Expression whereCondition = ExpressionUtils.add(whereCondition1, whereCondition2);
+
+
+ Plan join = plan(new LogicalJoin(JoinType.INNER_JOIN, Optional.of(onCondition)), rStudent, rScore);
+ Plan filter = plan(new LogicalFilter(whereCondition), join);
+
+ Plan root = plan(new LogicalProject(
+ Lists.newArrayList(rStudent.getOutput().get(1), rCourse.getOutput().get(1), rScore.getOutput().get(2))),
+ filter);
+
+ Memo memo = new Memo();
+ memo.initialize(root);
+ System.out.println(memo.copyOut().treeString());
+
+ OptimizerContext optimizerContext = new OptimizerContext(memo);
+ PlannerContext plannerContext = new PlannerContext(optimizerContext, null, new PhysicalProperties());
+ RewriteTopDownJob rewriteTopDownJob = new RewriteTopDownJob(memo.getRoot(),
+ ImmutableList.of(new PushPredicateThroughJoin().build()), plannerContext);
+ plannerContext.getOptimizerContext().pushJob(rewriteTopDownJob);
+ plannerContext.getOptimizerContext().getJobScheduler().executeJobPool(plannerContext);
+
+ Group rootGroup = memo.getRoot();
+ System.out.println(memo.copyOut().treeString());
+ System.out.println(11);
+
+ Operator op1 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().getOperator();
+ Operator op2 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().child(0).getLogicalExpression().getOperator();
+ Operator op3 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().child(1).getLogicalExpression().getOperator();
+
+ Assertions.assertTrue(op1 instanceof LogicalJoin);
+ Assertions.assertTrue(op2 instanceof LogicalFilter);
+ Assertions.assertTrue(op3 instanceof LogicalFilter);
+ LogicalJoin join1 = (LogicalJoin) op1;
+ LogicalFilter filter1 = (LogicalFilter) op2;
+ LogicalFilter filter2 = (LogicalFilter) op3;
+
+ Assertions.assertEquals(join1.getCondition().get(), onCondition1);
+ Assertions.assertEquals(filter1.getPredicates(), ExpressionUtils.add(onCondition2, whereCondition1));
+ Assertions.assertEquals(filter2.getPredicates(), ExpressionUtils.add(onCondition3, whereCondition2));
+ }
+
+ @Test
+ public void pushDownPredicateIntoScanTest3() {
+ //select id,name,grade from student left join score on student.id + 1 = score.sid - 2
+ //where student.age > 18 and score.grade > 60
+ Expression whereCondition1 = new EqualTo<>(new Add<>(rStudent.getOutput().get(0), Literal.of(1)),
+ new Subtract<>(rScore.getOutput().get(0), Literal.of(2)));
+ Expression whereCondition2 = new GreaterThan<>(rStudent.getOutput().get(1), Literal.of(18));
+ Expression whereCondition3 = new GreaterThan<>(rScore.getOutput().get(2), Literal.of(60));
+ Expression whereCondition = ExpressionUtils.add(whereCondition1, whereCondition2, whereCondition3);
+
+ Plan join = plan(new LogicalJoin(JoinType.INNER_JOIN, Optional.empty()), rStudent, rScore);
+ Plan filter = plan(new LogicalFilter(whereCondition), join);
+
+ Plan root = plan(new LogicalProject(
+ Lists.newArrayList(rStudent.getOutput().get(1), rCourse.getOutput().get(1), rScore.getOutput().get(2))),
+ filter);
+
+ Memo memo = new Memo();
+ memo.initialize(root);
+ System.out.println(memo.copyOut().treeString());
+
+ OptimizerContext optimizerContext = new OptimizerContext(memo);
+ PlannerContext plannerContext = new PlannerContext(optimizerContext, null, new PhysicalProperties());
+ RewriteTopDownJob rewriteTopDownJob = new RewriteTopDownJob(memo.getRoot(),
+ ImmutableList.of(new PushPredicateThroughJoin().build()), plannerContext);
+ plannerContext.getOptimizerContext().pushJob(rewriteTopDownJob);
+ plannerContext.getOptimizerContext().getJobScheduler().executeJobPool(plannerContext);
+
+ Group rootGroup = memo.getRoot();
+ System.out.println(memo.copyOut().treeString());
+
+ Operator op1 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().getOperator();
+ Operator op2 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().child(0).getLogicalExpression().getOperator();
+ Operator op3 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().child(1).getLogicalExpression().getOperator();
+
+ Assertions.assertTrue(op1 instanceof LogicalJoin);
+ Assertions.assertTrue(op2 instanceof LogicalFilter);
+ Assertions.assertTrue(op3 instanceof LogicalFilter);
+ LogicalJoin join1 = (LogicalJoin) op1;
+ LogicalFilter filter1 = (LogicalFilter) op2;
+ LogicalFilter filter2 = (LogicalFilter) op3;
+ Assertions.assertEquals(join1.getCondition().get(), whereCondition1);
+ Assertions.assertEquals(filter1.getPredicates(), whereCondition2);
+ Assertions.assertEquals(filter2.getPredicates(), whereCondition3);
+ }
+
+ @Test
+ public void pushDownPredicateIntoScanTest4() {
+ /*
+ select
+ student.name,
+ course.name,
+ score.grade
+ from student,score,course
+ where on student.id = score.sid and student.age between 18 and 20 and score.grade > 60 and student.id = score.sid
+ */
+
+ // student.id = score.sid
+ Expression whereCondition1 = new EqualTo<>(rStudent.getOutput().get(0), rScore.getOutput().get(0));
+ // score.cid = course.cid
+ Expression whereCondition2 = new EqualTo<>(rScore.getOutput().get(1), rCourse.getOutput().get(0));
+ // student.age between 18 and 20
+ Expression whereCondition3 = new Between<>(rStudent.getOutput().get(2), Literal.of(18), Literal.of(20));
+ // score.grade > 60
+ Expression whereCondition4 = new GreaterThan<>(rScore.getOutput().get(2), Literal.of(60));
+
+ Expression whereCondition = ExpressionUtils.add(whereCondition1, whereCondition2, whereCondition3, whereCondition4);
+
+ Plan join = plan(new LogicalJoin(JoinType.INNER_JOIN, Optional.empty()), rStudent, rScore);
+ Plan join1 = plan(new LogicalJoin(JoinType.INNER_JOIN, Optional.empty()), join, rCourse);
+ Plan filter = plan(new LogicalFilter(whereCondition), join1);
+
+ Plan root = plan(new LogicalProject(
+ Lists.newArrayList(rStudent.getOutput().get(1), rCourse.getOutput().get(1), rScore.getOutput().get(2))),
+ filter);
+
+
+ Memo memo = new Memo();
+ memo.initialize(root);
+ System.out.println(memo.copyOut().treeString());
+
+ OptimizerContext optimizerContext = new OptimizerContext(memo);
+ PlannerContext plannerContext = new PlannerContext(optimizerContext, null, new PhysicalProperties());
+ List<Rule<Plan>> fakeRules = Lists.newArrayList(new PushPredicateThroughJoin().build());
+ RewriteTopDownJob rewriteTopDownJob = new RewriteTopDownJob(memo.getRoot(), fakeRules, plannerContext);
+ plannerContext.getOptimizerContext().pushJob(rewriteTopDownJob);
+ plannerContext.getOptimizerContext().getJobScheduler().executeJobPool(plannerContext);
+
+ Group rootGroup = memo.getRoot();
+ System.out.println(memo.copyOut().treeString());
+ Operator join2 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().getOperator();
+ Operator join3 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().child(0).getLogicalExpression().getOperator();
+ Operator op1 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().child(0).getLogicalExpression().child(0).getLogicalExpression().getOperator();
+ Operator op2 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().child(0).getLogicalExpression().child(1).getLogicalExpression().getOperator();
+
+ Assertions.assertTrue(join2 instanceof LogicalJoin);
+ Assertions.assertTrue(join3 instanceof LogicalJoin);
+ Assertions.assertTrue(op1 instanceof LogicalFilter);
+ Assertions.assertTrue(op2 instanceof LogicalFilter);
+
+ Assertions.assertEquals(((LogicalJoin) join2).getCondition().get(), whereCondition2);
+ Assertions.assertEquals(((LogicalJoin) join3).getCondition().get(), whereCondition1);
+ Assertions.assertEquals(((LogicalFilter) op1).getPredicates(), whereCondition3);
+ Assertions.assertEquals(((LogicalFilter) op2).getPredicates(), whereCondition4);
+ }
+}
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/ssb/SSBUtils.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/ssb/SSBUtils.java
index cf43c1246b..074e970b31 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/ssb/SSBUtils.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/ssb/SSBUtils.java
@@ -84,7 +84,7 @@ public class SSBUtils {
+ " s_nation,\n"
+ " d_year,\n"
+ " SUM(lo_revenue) AS REVENUE\n"
- + "FROM customer, lineorder, supplier, dates\n"
+ + "FROM lineorder, customer, supplier, dates\n"
+ "WHERE\n"
+ " lo_custkey = c_custkey\n"
+ " AND lo_suppkey = s_suppkey\n"
@@ -101,7 +101,7 @@ public class SSBUtils {
+ " s_city,\n"
+ " d_year,\n"
+ " SUM(lo_revenue) AS REVENUE\n"
- + "FROM customer, lineorder, supplier, dates\n"
+ + "FROM lineorder, customer , supplier, dates\n"
+ "WHERE\n"
+ " lo_custkey = c_custkey\n"
+ " AND lo_suppkey = s_suppkey\n"
@@ -118,7 +118,7 @@ public class SSBUtils {
+ " s_city,\n"
+ " d_year,\n"
+ " SUM(lo_revenue) AS REVENUE\n"
- + "FROM customer, lineorder, supplier, dates\n"
+ + "FROM lineorder, customer, supplier, dates\n"
+ "WHERE\n"
+ " lo_custkey = c_custkey\n"
+ " AND lo_suppkey = s_suppkey\n"
@@ -141,7 +141,7 @@ public class SSBUtils {
+ " s_city,\n"
+ " d_year,\n"
+ " SUM(lo_revenue) AS REVENUE\n"
- + "FROM customer, lineorder, supplier, dates\n"
+ + "FROM lineorder, customer, supplier, dates\n"
+ "WHERE\n"
+ " lo_custkey = c_custkey\n"
+ " AND lo_suppkey = s_suppkey\n"
@@ -162,7 +162,7 @@ public class SSBUtils {
+ " d_year,\n"
+ " c_nation,\n"
+ " SUM(lo_revenue - lo_supplycost) AS PROFIT\n"
- + "FROM dates, customer, supplier, part, lineorder\n"
+ + "FROM lineorder, dates, customer, supplier, part\n"
+ "WHERE\n"
+ " lo_custkey = c_custkey\n"
+ " AND lo_suppkey = s_suppkey\n"
@@ -182,7 +182,7 @@ public class SSBUtils {
+ " s_nation,\n"
+ " p_category,\n"
+ " SUM(lo_revenue - lo_supplycost) AS PROFIT\n"
- + "FROM dates, customer, supplier, part, lineorder\n"
+ + "FROM lineorder, dates, customer, supplier, part\n"
+ "WHERE\n"
+ " lo_custkey = c_custkey\n"
+ " AND lo_suppkey = s_suppkey\n"
@@ -206,7 +206,7 @@ public class SSBUtils {
+ " s_city,\n"
+ " p_brand,\n"
+ " SUM(lo_revenue - lo_supplycost) AS PROFIT\n"
- + "FROM dates, customer, supplier, part, lineorder\n"
+ + "FROM lineorder, dates, customer, supplier, part\n"
+ "WHERE\n"
+ " lo_custkey = c_custkey\n"
+ " AND lo_suppkey = s_suppkey\n"
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/ExpressionUtilsTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/ExpressionUtilsTest.java
new file mode 100644
index 0000000000..e60b322804
--- /dev/null
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/ExpressionUtilsTest.java
@@ -0,0 +1,101 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.util;
+
+import org.apache.doris.nereids.parser.NereidsParser;
+import org.apache.doris.nereids.trees.expressions.Expression;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+/**
+ * ExpressionUtils ut.
+ */
+public class ExpressionUtilsTest {
+
+ private static final NereidsParser PARSER = new NereidsParser();
+
+ @Test
+ public void extractConjunctsTest() {
+ List<Expression> expressions;
+ Expression expr;
+
+ expr = PARSER.createExpression("a");
+ expressions = ExpressionUtils.extractConjunct(expr);
+ Assertions.assertEquals(expressions.size(), 1);
+ Assertions.assertEquals(expressions.get(0), expr);
+
+
+ expr = PARSER.createExpression("a and b and c");
+ Expression a = PARSER.createExpression("a");
+ Expression b = PARSER.createExpression("b");
+ Expression c = PARSER.createExpression("c");
+
+ expressions = ExpressionUtils.extractConjunct(expr);
+ Assertions.assertEquals(expressions.size(), 3);
+ Assertions.assertEquals(expressions.get(0), a);
+ Assertions.assertEquals(expressions.get(1), b);
+ Assertions.assertEquals(expressions.get(2), c);
+
+
+ expr = PARSER.createExpression("(a or b) and c and (e or f)");
+ expressions = ExpressionUtils.extractConjunct(expr);
+ Expression aOrb = PARSER.createExpression("a or b");
+ Expression eOrf = PARSER.createExpression("e or f");
+ Assertions.assertEquals(expressions.size(), 3);
+ Assertions.assertEquals(expressions.get(0), aOrb);
+ Assertions.assertEquals(expressions.get(1), c);
+ Assertions.assertEquals(expressions.get(2), eOrf);
+
+ }
+
+ @Test
+ public void extractDisjunctsTest() {
+ List<Expression> expressions;
+ Expression expr;
+
+ expr = PARSER.createExpression("a");
+ expressions = ExpressionUtils.extractDisjunct(expr);
+ Assertions.assertEquals(expressions.size(), 1);
+ Assertions.assertEquals(expressions.get(0), expr);
+
+
+ expr = PARSER.createExpression("a or b or c");
+ Expression a = PARSER.createExpression("a");
+ Expression b = PARSER.createExpression("b");
+ Expression c = PARSER.createExpression("c");
+
+ expressions = ExpressionUtils.extractDisjunct(expr);
+ Assertions.assertEquals(expressions.size(), 3);
+ Assertions.assertEquals(expressions.get(0), a);
+ Assertions.assertEquals(expressions.get(1), b);
+ Assertions.assertEquals(expressions.get(2), c);
+
+
+ expr = PARSER.createExpression("(a and b) or c or (e and f)");
+ expressions = ExpressionUtils.extractDisjunct(expr);
+ Expression aAndb = PARSER.createExpression("a and b");
+ Expression eAndf = PARSER.createExpression("e and f");
+ Assertions.assertEquals(expressions.size(), 3);
+ Assertions.assertEquals(expressions.get(0), aAndb);
+ Assertions.assertEquals(expressions.get(1), c);
+ Assertions.assertEquals(expressions.get(2), eAndf);
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org