This is an automated email from the ASF dual-hosted git repository.
jakevin 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 051ab7a9c6 [refactor](Nereids): refactor Join-Dependent Predicate
Duplication. (#17653)
051ab7a9c6 is described below
commit 051ab7a9c665d3c05a3f3b6ef6e0d4921b2b4f43
Author: jakevin <[email protected]>
AuthorDate: Fri Mar 10 22:19:45 2023 +0800
[refactor](Nereids): refactor Join-Dependent Predicate Duplication. (#17653)
---
.../rules/rewrite/logical/AdjustNullable.java | 2 +-
...xtractSingleTableExpressionFromDisjunction.java | 149 +++++++++------------
.../nereids/trees/plans/logical/LogicalFilter.java | 55 +++-----
...ctSingleTableExpressionFromDisjunctionTest.java | 4 +-
4 files changed, 81 insertions(+), 129 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/AdjustNullable.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/AdjustNullable.java
index 42ebe1e2da..83f096586d 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/AdjustNullable.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/AdjustNullable.java
@@ -63,7 +63,7 @@ public class AdjustNullable implements RewriteRuleFactory {
RuleType.ADJUST_NULLABLE_ON_FILTER.build(logicalFilter().then(filter -> {
Map<ExprId, Slot> exprIdSlotMap =
collectChildrenOutputMap(filter);
Set<Expression> conjuncts =
updateExpressions(filter.getConjuncts(), exprIdSlotMap);
- return new LogicalFilter<>(conjuncts,
filter.isSingleTableExpressionExtracted(), filter.child());
+ return new LogicalFilter<>(conjuncts, filter.child());
})),
RuleType.ADJUST_NULLABLE_ON_GENERATE.build(logicalGenerate().then(generate -> {
Map<ExprId, Slot> exprIdSlotMap =
collectChildrenOutputMap(generate);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ExtractSingleTableExpressionFromDisjunction.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ExtractSingleTableExpressionFromDisjunction.java
index 5bb029e035..c72d7b00c5 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ExtractSingleTableExpressionFromDisjunction.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ExtractSingleTableExpressionFromDisjunction.java
@@ -22,7 +22,6 @@ import org.apache.doris.nereids.rules.RuleType;
import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.Slot;
-import org.apache.doris.nereids.trees.expressions.SlotReference;
import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
import org.apache.doris.nereids.util.ExpressionUtils;
@@ -35,109 +34,87 @@ import java.util.Set;
import java.util.stream.Collectors;
/**
+ * Paper: Quantifying TPC-H Choke Points and Their Optimizations
+ * 4.4 Join-Dependent Predicate Duplication
* Example:
- * (n1.n_name = 'FRANCE' and n2.n_name = 'GERMANY') or (n1.n_name = 'GERMANY'
and n2.n_name = 'FRANCE')
+ * Two queries, Q7 and Q19, include predicates that operate
+ * on multiple tables without being a join predicate. In Q17,
+ * (n1.name = ’NATION1’ AND n2.name = ’NATION2’) OR (n1.name = ’NATION2’ AND
n2.name = ’NATION1’)
* =>
* (n1.n_name = 'FRANCE' and n2.n_name = 'GERMANY') or (n1.n_name = 'GERMANY'
and n2.n_name = 'FRANCE')
* and (n1.n_name = 'FRANCE' or n1.n_name='GERMANY') and (n2.n_name='GERMANY'
or n2.n_name='FRANCE')
- *
- * (n1.n_name = 'FRANCE' or n1.n_name='GERMANY') is a logical redundant, but
it could be pushed down to scan(n1) to
- * reduce the number of scan output tuples.
- * For complete sql example, refer to tpch q7.
- *
==================================================================================================
- * <br/>
- * There are 2 cases, in which the redundant expressions are useless:
- * 1. filter(expr)-->XXX out join.
- * For example, for left join, the redundant expression for right side is not
useful, because we cannot push expression
- * down to right child. Refer to PushDownJoinOtherCondition Rule for push-down
cases.
- * But it is hard to detect this case, if the outer join is a descendant but
not child of the filter.
- * 2. filter(expr)
- * |-->upper-join
- * |-->bottom-join
- * +-->child
- * In current version, we do not extract redundant expression for bottom-join.
This redundancy is good for
- * upper-join (reduce the number of input tuple from bottom join), but it
becomes unuseful if we rotate the join tree.
- *
==================================================================================================
- *<br/>
+ * <p>
+ * new generated expr is redundant, but they could be pushed down to reduce
the cardinality of children output tuples.
+ * <p>
* Implementation note:
* 1. This rule should only be applied ONCE to avoid generate same redundant
expression.
- * 2. This version only generates redundant expressions, but not push them.
- * 3. A redundant expression only contains slots from a single table.
- * 4. This rule is applied after rules converting sub-query to join.
- * 5. Add a flag 'isRedundant' in Expression. It is true, if it is generated
by this rule.
- * 6. The useless redundant expression should be removed, if it cannot be
pushed down. We need a new rule
- * `RemoveRedundantExpression` to fulfill this purpose.
- * 7. In old optimizer, there is `InferFilterRule` generates redundancy
expressions. Its Nereid counterpart also need
+ * 2. A redundant expression only contains slots from a single table.
+ * 3. In old optimizer, there is `InferFilterRule` generates redundancy
expressions. Its Nereid counterpart also need
* `RemoveRedundantExpression`.
+ * <p>
+ * TODO: This rule just match filter, but it could be applied to inner/cross
join condition.
*/
public class ExtractSingleTableExpressionFromDisjunction extends
OneRewriteRuleFactory {
@Override
public Rule build() {
- return
logicalFilter().whenNot(LogicalFilter::isSingleTableExpressionExtracted).then(filter
-> {
- //filter = [(n1.n_name = 'FRANCE' and n2.n_name = 'GERMANY')
- // or (n1.n_name = 'GERMANY' and n2.n_name = 'FRANCE')]
- // and ...
- Set<Expression> conjuncts = filter.getConjuncts();
-
- List<Expression> redundants = Lists.newArrayList();
- for (Expression conjunct : conjuncts) {
- //conjunct=(n1.n_name = 'FRANCE' and n2.n_name = 'GERMANY')
- // or (n1.n_name = 'GERMANY' and n2.n_name = 'FRANCE')
- List<Expression> disjuncts =
ExpressionUtils.extractDisjunction(conjunct);
- //disjuncts={ (n1.n_name = 'FRANCE' and n2.n_name = 'GERMANY'),
- // (n1.n_name = 'GERMANY' and n2.n_name = 'FRANCE')}
- if (disjuncts.size() == 1) {
- continue;
- }
- //only check table in first disjunct.
- //In our example, qualifiers = { n1, n2 }
- Expression first = disjuncts.get(0);
- Set<String> qualifiers = first.getInputSlots()
- .stream()
- .map(SlotReference.class::cast)
- .map(this::getSlotQualifierAsString)
- .collect(Collectors.toSet());
- //try to extract
- for (String qualifier : qualifiers) {
- List<Expression> extractForAll = Lists.newArrayList();
- boolean success = true;
- for (Expression expr :
ExpressionUtils.extractDisjunction(conjunct)) {
- Optional<Expression> extracted =
extractSingleTableExpression(expr, qualifier);
- if (!extracted.isPresent()) {
- //extract failed
- success = false;
- break;
- } else {
- extractForAll.add(extracted.get());
- }
- }
- if (success) {
- redundants.add(ExpressionUtils.or(extractForAll));
- }
- }
+ return logicalFilter().then(filter -> {
+ List<Expression> dependentPredicates =
extractDependentConjuncts(filter.getConjuncts());
+ if (dependentPredicates.isEmpty()) {
+ return null;
}
- if (redundants.isEmpty()) {
- return new LogicalFilter<>(filter.getConjuncts(), true,
filter.child());
- } else {
- return new LogicalFilter<>(ImmutableSet.<Expression>builder()
- .addAll(filter.getConjuncts())
- .addAll(redundants).build(),
- true, filter.child());
+ Set<Expression> newPredicates = ImmutableSet.<Expression>builder()
+ .addAll(filter.getConjuncts())
+ .addAll(dependentPredicates).build();
+ if (newPredicates.size() == filter.getConjuncts().size()) {
+ return null;
}
+ return new LogicalFilter<>(newPredicates, filter.child());
}).toRule(RuleType.EXTRACT_SINGLE_TABLE_EXPRESSION_FROM_DISJUNCTION);
}
- private String getSlotQualifierAsString(SlotReference slotReference) {
- StringBuilder builder = new StringBuilder();
- for (String q : slotReference.getQualifier()) {
- builder.append(q).append('.');
+ private List<Expression> extractDependentConjuncts(Set<Expression>
conjuncts) {
+ List<Expression> dependentPredicates = Lists.newArrayList();
+ for (Expression conjunct : conjuncts) {
+ // conjunct=(n1.n_name = 'FRANCE' and n2.n_name = 'GERMANY')
+ // or (n1.n_name = 'GERMANY' and n2.n_name = 'FRANCE')
+ List<Expression> disjuncts =
ExpressionUtils.extractDisjunction(conjunct);
+ // disjuncts={ (n1.n_name = 'FRANCE' and n2.n_name = 'GERMANY'),
+ // (n1.n_name = 'GERMANY' and n2.n_name = 'FRANCE')}
+ if (disjuncts.size() == 1) {
+ continue;
+ }
+ // only check table in first disjunct.
+ // In our example, qualifiers = { n1, n2 }
+ Expression first = disjuncts.get(0);
+ Set<String> qualifiers = first.getInputSlots()
+ .stream()
+ .map(slot -> String.join(".", slot.getQualifier()))
+ .collect(Collectors.toSet());
+ // try to extract
+ for (String qualifier : qualifiers) {
+ List<Expression> extractForAll = Lists.newArrayList();
+ boolean success = true;
+ for (Expression expr :
ExpressionUtils.extractDisjunction(conjunct)) {
+ Optional<Expression> extracted =
extractSingleTableExpression(expr, qualifier);
+ if (!extracted.isPresent()) {
+ // extract failed
+ success = false;
+ break;
+ } else {
+ extractForAll.add(extracted.get());
+ }
+ }
+ if (success) {
+ dependentPredicates.add(ExpressionUtils.or(extractForAll));
+ }
+ }
}
- return builder.toString();
+ return dependentPredicates;
}
- //extract some conjucts from expr, all slots of the extracted conjunct
comes from the table referred by qualifier.
- //example: expr=(n1.n_name = 'FRANCE' and n2.n_name = 'GERMANY'),
qualifier="n1."
- //output: n1.n_name = 'FRANCE'
+ // extract some conjucts from expr, all slots of the extracted conjunct
comes from the table referred by qualifier.
+ // example: expr=(n1.n_name = 'FRANCE' and n2.n_name = 'GERMANY'),
qualifier="n1."
+ // output: n1.n_name = 'FRANCE'
private Optional<Expression> extractSingleTableExpression(Expression expr,
String qualifier) {
List<Expression> output = Lists.newArrayList();
List<Expression> conjuncts = ExpressionUtils.extractConjunction(expr);
@@ -156,7 +133,7 @@ public class ExtractSingleTableExpressionFromDisjunction
extends OneRewriteRuleF
private boolean isSingleTableExpression(Expression expr, String qualifier)
{
//TODO: cache getSlotQualifierAsString() result.
for (Slot slot : expr.getInputSlots()) {
- String slotQualifier = getSlotQualifierAsString((SlotReference)
slot);
+ String slotQualifier = String.join(".", slot.getQualifier());
if (!slotQualifier.equals(qualifier)) {
return false;
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFilter.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFilter.java
index 20a80ba614..cd533f63a9 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFilter.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFilter.java
@@ -47,24 +47,14 @@ public class LogicalFilter<CHILD_TYPE extends Plan> extends
LogicalUnary<CHILD_T
private final Set<Expression> conjuncts;
- private final boolean singleTableExpressionExtracted;
-
public LogicalFilter(Set<Expression> conjuncts, CHILD_TYPE child) {
- this(conjuncts, Optional.empty(), false, Optional.empty(), child);
- }
-
- public LogicalFilter(Set<Expression> conjuncts, boolean
singleTableExpressionExtracted,
- CHILD_TYPE child) {
- this(conjuncts, Optional.empty(), singleTableExpressionExtracted,
- Optional.empty(), child);
+ this(conjuncts, Optional.empty(), Optional.empty(), child);
}
- public LogicalFilter(Set<Expression> conjuncts, Optional<GroupExpression>
groupExpression,
- boolean singleTableExpressionExtracted,
+ private LogicalFilter(Set<Expression> conjuncts, Optional<GroupExpression>
groupExpression,
Optional<LogicalProperties> logicalProperties, CHILD_TYPE child) {
super(PlanType.LOGICAL_FILTER, groupExpression, logicalProperties,
child);
this.conjuncts = ImmutableSet.copyOf(Objects.requireNonNull(conjuncts,
"conjuncts can not be null"));
- this.singleTableExpressionExtracted = singleTableExpressionExtracted;
}
@Override
@@ -78,18 +68,13 @@ public class LogicalFilter<CHILD_TYPE extends Plan> extends
LogicalUnary<CHILD_T
@Override
public List<Plan> extraPlans() {
- if (conjuncts != null) {
- return conjuncts.stream().map(Expression::children)
- .flatMap(Collection::stream)
- .flatMap(m -> {
- if (m instanceof SubqueryExpr) {
- return Stream.of(new
LogicalSubQueryAlias<>(m.toSql(), ((SubqueryExpr) m).getQueryPlan()));
- } else {
- return new LogicalFilter<Plan>(ImmutableSet.of(m),
child()).extraPlans().stream();
- }
- }).collect(Collectors.toList());
- }
- return ImmutableList.of();
+ return
conjuncts.stream().map(Expression::children).flatMap(Collection::stream).flatMap(m
-> {
+ if (m instanceof SubqueryExpr) {
+ return Stream.of(new LogicalSubQueryAlias<>(m.toSql(),
((SubqueryExpr) m).getQueryPlan()));
+ } else {
+ return new LogicalFilter<Plan>(ImmutableSet.of(m),
child()).extraPlans().stream();
+ }
+ }).collect(Collectors.toList());
}
@Override
@@ -99,9 +84,7 @@ public class LogicalFilter<CHILD_TYPE extends Plan> extends
LogicalUnary<CHILD_T
@Override
public String toString() {
- return Utils.toSqlString("LogicalFilter",
- "predicates", getPredicate()
- );
+ return Utils.toSqlString("LogicalFilter", "predicates",
getPredicate());
}
@Override
@@ -113,13 +96,12 @@ public class LogicalFilter<CHILD_TYPE extends Plan>
extends LogicalUnary<CHILD_T
return false;
}
LogicalFilter that = (LogicalFilter) o;
- return conjuncts.equals(that.conjuncts)
- && singleTableExpressionExtracted ==
that.singleTableExpressionExtracted;
+ return conjuncts.equals(that.conjuncts);
}
@Override
public int hashCode() {
- return Objects.hash(conjuncts, singleTableExpressionExtracted);
+ return Objects.hash(conjuncts);
}
@Override
@@ -130,23 +112,16 @@ public class LogicalFilter<CHILD_TYPE extends Plan>
extends LogicalUnary<CHILD_T
@Override
public LogicalUnary<Plan> withChildren(List<Plan> children) {
Preconditions.checkArgument(children.size() == 1);
- return new LogicalFilter<>(conjuncts, singleTableExpressionExtracted,
children.get(0));
+ return new LogicalFilter<>(conjuncts, children.get(0));
}
@Override
public Plan withGroupExpression(Optional<GroupExpression> groupExpression)
{
- return new LogicalFilter<>(conjuncts, groupExpression,
singleTableExpressionExtracted,
- Optional.of(getLogicalProperties()), child());
+ return new LogicalFilter<>(conjuncts, groupExpression,
Optional.of(getLogicalProperties()), child());
}
@Override
public Plan withLogicalProperties(Optional<LogicalProperties>
logicalProperties) {
- return new LogicalFilter<>(conjuncts, Optional.empty(),
- singleTableExpressionExtracted,
- logicalProperties, child());
- }
-
- public boolean isSingleTableExpressionExtracted() {
- return singleTableExpressionExtracted;
+ return new LogicalFilter<>(conjuncts, Optional.empty(),
logicalProperties, child());
}
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/ExtractSingleTableExpressionFromDisjunctionTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/ExtractSingleTableExpressionFromDisjunctionTest.java
index bfc7e1aaa2..6e03afaa33 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/ExtractSingleTableExpressionFromDisjunctionTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/ExtractSingleTableExpressionFromDisjunctionTest.java
@@ -83,14 +83,14 @@ public class
ExtractSingleTableExpressionFromDisjunctionTest implements MemoPatt
)
);
Plan join = new LogicalJoin<>(JoinType.CROSS_JOIN, student, course);
- LogicalFilter root = new LogicalFilter(ImmutableSet.of(expr), join);
+ LogicalFilter root = new LogicalFilter<>(ImmutableSet.of(expr), join);
PlanChecker.from(MemoTestUtils.createConnectContext(), root)
.applyTopDown(new
ExtractSingleTableExpressionFromDisjunction())
.matchesFromRoot(
logicalFilter()
.when(filter ->
verifySingleTableExpression1(filter.getConjuncts()))
);
- Assertions.assertTrue(studentGender != null);
+ Assertions.assertNotNull(studentGender);
}
private boolean verifySingleTableExpression1(Set<Expression> conjuncts) {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]