This is an automated email from the ASF dual-hosted git repository.
morrysnow 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 da40e1c7670 [feature](nereids) Matiarilzed view query rewrite util
implementation (#27568)
da40e1c7670 is described below
commit da40e1c7670492c747c284d7af7af2f874d01038
Author: seawinde <[email protected]>
AuthorDate: Tue Dec 5 11:48:04 2023 +0800
[feature](nereids) Matiarilzed view query rewrite util implementation
(#27568)
The basic util implementatation which is used by materialized view rewrite
---
.../mv/AbstractMaterializedViewJoinRule.java | 1 +
.../mv/AbstractMaterializedViewRule.java | 9 +-
.../nereids/rules/exploration/mv/Predicates.java | 56 ++--
.../rules/exploration/mv/PredicatesSplitter.java | 112 ++++++++
.../rules/exploration/mv/RelationMapping.java | 63 -----
.../nereids/rules/exploration/mv/SlotMapping.java | 49 ----
.../nereids/rules/exploration/mv/StructInfo.java | 2 +-
.../mv/mapping/ExpressionIndexMapping.java | 48 ++++
.../exploration/mv/{ => mapping}/Mapping.java | 61 ++--
.../exploration/mv/mapping/RelationMapping.java | 113 ++++++++
.../rules/exploration/mv/mapping/SlotMapping.java | 84 ++++++
.../expressions/visitor/ExpressionVisitors.java | 49 ----
.../plans/visitor/ExpressionLineageReplacer.java | 160 +++++++++++
.../apache/doris/nereids/util/ExpressionUtils.java | 30 +-
.../nereids/rules/exploration/mv/MappingTest.java | 309 +++++++++++++++++++++
.../expression/ExpressionRewriteTestHelper.java | 2 +-
.../rules/expression/PredicatesSplitterTest.java | 109 ++++++++
.../doris/nereids/util/ExpressionUtilsTest.java | 106 ++++++-
18 files changed, 1117 insertions(+), 246 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewJoinRule.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewJoinRule.java
index 17df9ef88ed..899ba216e99 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewJoinRule.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewJoinRule.java
@@ -17,6 +17,7 @@
package org.apache.doris.nereids.rules.exploration.mv;
+import org.apache.doris.nereids.rules.exploration.mv.mapping.RelationMapping;
import org.apache.doris.nereids.trees.expressions.NamedExpression;
import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java
index c52acdb3fda..2dc64ccfeab 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java
@@ -20,8 +20,10 @@ package org.apache.doris.nereids.rules.exploration.mv;
import org.apache.doris.catalog.TableIf;
import org.apache.doris.nereids.CascadesContext;
import org.apache.doris.nereids.memo.Group;
-import
org.apache.doris.nereids.rules.exploration.mv.Mapping.ExpressionIndexMapping;
import org.apache.doris.nereids.rules.exploration.mv.Predicates.SplitPredicate;
+import
org.apache.doris.nereids.rules.exploration.mv.mapping.ExpressionIndexMapping;
+import org.apache.doris.nereids.rules.exploration.mv.mapping.RelationMapping;
+import org.apache.doris.nereids.rules.exploration.mv.mapping.SlotMapping;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.NamedExpression;
import org.apache.doris.nereids.trees.plans.Plan;
@@ -141,8 +143,9 @@ public abstract class AbstractMaterializedViewRule {
targetTopExpressions, targetStructInfo.getOriginalPlan(),
Sets.newHashSet(), Sets.newHashSet());
SlotMapping sourceToTargetSlotMapping =
SlotMapping.generate(sourceToTargetMapping);
// mv sql plan expressions transform to query based
- List<? extends Expression> queryBasedExpressions =
ExpressionUtils.permute(shuttledTargetExpressions,
- sourceToTargetSlotMapping.inverse());
+ List<? extends Expression> queryBasedExpressions =
ExpressionUtils.replace(
+
shuttledTargetExpressions.stream().map(Expression.class::cast).collect(Collectors.toList()),
+ sourceToTargetSlotMapping.inverse().getSlotMap());
// mv sql query based expression and index mapping
ExpressionIndexMapping.generate(queryBasedExpressions);
// TODO visit source expression and replace the expression with
expressionIndexMapping
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/Predicates.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/Predicates.java
index 40b91994be7..43aab2389ca 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/Predicates.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/Predicates.java
@@ -19,7 +19,6 @@ package org.apache.doris.nereids.rules.exploration.mv;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral;
-import
org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitors.PredicatesSpliter;
import org.apache.doris.nereids.util.ExpressionUtils;
import com.google.common.collect.ImmutableList;
@@ -55,8 +54,7 @@ public class Predicates {
* Split the expression to equal, range and residual predicate.
* */
public static SplitPredicate splitPredicates(Expression expression) {
- PredicatesSpliter predicatesSplit = new PredicatesSpliter(expression);
- expression.accept(predicatesSplit, null);
+ PredicatesSplitter predicatesSplit = new
PredicatesSplitter(expression);
return predicatesSplit.getSplitPredicate();
}
@@ -64,26 +62,26 @@ public class Predicates {
* The split different representation for predicate expression, such as
equal, range and residual predicate.
* */
public static final class SplitPredicate {
- private final Expression equalPredicates;
- private final Expression rangePredicates;
- private final Expression residualPredicates;
-
- public SplitPredicate(Expression equalPredicates, Expression
rangePredicates, Expression residualPredicates) {
- this.equalPredicates = equalPredicates;
- this.rangePredicates = rangePredicates;
- this.residualPredicates = residualPredicates;
+ private final Expression equalPredicate;
+ private final Expression rangePredicate;
+ private final Expression residualPredicate;
+
+ public SplitPredicate(Expression equalPredicate, Expression
rangePredicate, Expression residualPredicate) {
+ this.equalPredicate = equalPredicate;
+ this.rangePredicate = rangePredicate;
+ this.residualPredicate = residualPredicate;
}
- public Expression getEqualPredicates() {
- return equalPredicates;
+ public Expression getEqualPredicate() {
+ return equalPredicate;
}
- public Expression getRangePredicates() {
- return rangePredicates;
+ public Expression getRangePredicate() {
+ return rangePredicate;
}
- public Expression getResidualPredicates() {
- return residualPredicates;
+ public Expression getResidualPredicate() {
+ return residualPredicate;
}
public static SplitPredicate empty() {
@@ -103,29 +101,25 @@ public class Predicates {
* isEmpty
* */
public boolean isEmpty() {
- return equalPredicates == null
- && rangePredicates == null
- && residualPredicates == null;
- }
-
- public Expression composedExpression() {
- return ExpressionUtils.and(equalPredicates, rangePredicates,
residualPredicates);
+ return equalPredicate == null
+ && rangePredicate == null
+ && residualPredicate == null;
}
public List<Expression> toList() {
- return ImmutableList.of(equalPredicates, rangePredicates,
residualPredicates);
+ return ImmutableList.of(equalPredicate, rangePredicate,
residualPredicate);
}
/**
* Check the predicates in SplitPredicate is whether all true or not
*/
public boolean isAlwaysTrue() {
- return equalPredicates instanceof BooleanLiteral
- && rangePredicates instanceof BooleanLiteral
- && residualPredicates instanceof BooleanLiteral
- && ((BooleanLiteral) equalPredicates).getValue()
- && ((BooleanLiteral) rangePredicates).getValue()
- && ((BooleanLiteral) residualPredicates).getValue();
+ return equalPredicate instanceof BooleanLiteral
+ && rangePredicate instanceof BooleanLiteral
+ && residualPredicate instanceof BooleanLiteral
+ && ((BooleanLiteral) equalPredicate).getValue()
+ && ((BooleanLiteral) rangePredicate).getValue()
+ && ((BooleanLiteral) residualPredicate).getValue();
}
}
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/PredicatesSplitter.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/PredicatesSplitter.java
new file mode 100644
index 00000000000..e90d6583eac
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/PredicatesSplitter.java
@@ -0,0 +1,112 @@
+// 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.exploration.mv;
+
+import org.apache.doris.nereids.trees.expressions.Alias;
+import org.apache.doris.nereids.trees.expressions.Cast;
+import org.apache.doris.nereids.trees.expressions.ComparisonPredicate;
+import org.apache.doris.nereids.trees.expressions.CompoundPredicate;
+import org.apache.doris.nereids.trees.expressions.EqualPredicate;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.Or;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.literal.Literal;
+import
org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionVisitor;
+import org.apache.doris.nereids.util.ExpressionUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Split the expression to equal, range and residual predicate.
+ * Should instance when used.
+ * TODO support complex predicate split
+ */
+public class PredicatesSplitter {
+
+ private final List<Expression> equalPredicates = new ArrayList<>();
+ private final List<Expression> rangePredicates = new ArrayList<>();
+ private final List<Expression> residualPredicates = new ArrayList<>();
+ private final List<Expression> conjunctExpressions;
+
+ private final PredicateExtract instance = new PredicateExtract();
+
+ public PredicatesSplitter(Expression target) {
+ this.conjunctExpressions = ExpressionUtils.extractConjunction(target);
+ for (Expression expression : conjunctExpressions) {
+ expression.accept(instance, expression);
+ }
+ }
+
+ /**
+ * PredicateExtract
+ */
+ public class PredicateExtract extends DefaultExpressionVisitor<Void,
Expression> {
+
+ @Override
+ public Void visitComparisonPredicate(ComparisonPredicate
comparisonPredicate, Expression sourceExpression) {
+ Expression leftArg = comparisonPredicate.getArgument(0);
+ Expression rightArg = comparisonPredicate.getArgument(1);
+ boolean leftArgOnlyContainsColumnRef =
containOnlyColumnRef(leftArg, true);
+ boolean rightArgOnlyContainsColumnRef =
containOnlyColumnRef(rightArg, true);
+ if (comparisonPredicate instanceof EqualPredicate) {
+ if (leftArgOnlyContainsColumnRef &&
rightArgOnlyContainsColumnRef) {
+ equalPredicates.add(comparisonPredicate);
+ return null;
+ } else {
+ residualPredicates.add(comparisonPredicate);
+ }
+ } else if ((leftArgOnlyContainsColumnRef && rightArg instanceof
Literal)
+ || (rightArgOnlyContainsColumnRef && leftArg instanceof
Literal)) {
+ rangePredicates.add(comparisonPredicate);
+ } else {
+ residualPredicates.add(comparisonPredicate);
+ }
+ return null;
+ }
+
+ @Override
+ public Void visitCompoundPredicate(CompoundPredicate
compoundPredicate, Expression context) {
+ if (compoundPredicate instanceof Or) {
+ residualPredicates.add(compoundPredicate);
+ return null;
+ }
+ return super.visit(compoundPredicate, context);
+ }
+ }
+
+ public Predicates.SplitPredicate getSplitPredicate() {
+ return Predicates.SplitPredicate.of(
+ equalPredicates.isEmpty() ? null :
ExpressionUtils.and(equalPredicates),
+ rangePredicates.isEmpty() ? null :
ExpressionUtils.and(rangePredicates),
+ residualPredicates.isEmpty() ? null :
ExpressionUtils.and(residualPredicates));
+ }
+
+ private static boolean containOnlyColumnRef(Expression expression, boolean
allowCast) {
+ if (expression instanceof SlotReference && ((SlotReference)
expression).isColumnFromTable()) {
+ return true;
+ }
+ if (allowCast && expression instanceof Cast) {
+ return containOnlyColumnRef(((Cast) expression).child(), true);
+ }
+ if (expression instanceof Alias) {
+ return containOnlyColumnRef(((Alias) expression).child(), true);
+ }
+ return false;
+ }
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/RelationMapping.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/RelationMapping.java
deleted file mode 100644
index ea91104a246..00000000000
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/RelationMapping.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// 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.exploration.mv;
-
-import org.apache.doris.catalog.TableIf;
-import org.apache.doris.nereids.trees.plans.algebra.CatalogRelation;
-
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Multimap;
-
-import java.util.List;
-
-/**
- * Relation mapping
- * such as query pattern is a1 left join a2 left join b
- * view pattern is a1 left join a2 left join b. the mapping will be
- * [{a1:a1, a2:a2, b:b}, {a1:a2, a2:a1, b:b}]
- */
-public class RelationMapping extends Mapping {
-
- private final BiMap<MappedRelation, MappedRelation> mappedRelationMap;
-
- public RelationMapping(BiMap<MappedRelation, MappedRelation>
mappedRelationMap) {
- this.mappedRelationMap = mappedRelationMap;
- }
-
- public BiMap<MappedRelation, MappedRelation> getMappedRelationMap() {
- return mappedRelationMap;
- }
-
- /**
- * Generate mapping according to source and target relation
- */
- public static List<RelationMapping> generate(List<CatalogRelation> source,
List<CatalogRelation> target) {
- Multimap<TableIf, CatalogRelation> queryTableRelationIdMap =
ArrayListMultimap.create();
- for (CatalogRelation relation : source) {
- queryTableRelationIdMap.put(relation.getTable(), relation);
- }
- Multimap<TableIf, CatalogRelation> viewTableRelationIdMap =
ArrayListMultimap.create();
- for (CatalogRelation relation : target) {
- viewTableRelationIdMap.put(relation.getTable(), relation);
- }
- // todo generate relation map
- return ImmutableList.of();
- }
-}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/SlotMapping.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/SlotMapping.java
deleted file mode 100644
index 7c50d79c6a2..00000000000
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/SlotMapping.java
+++ /dev/null
@@ -1,49 +0,0 @@
-// 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.exploration.mv;
-
-import com.google.common.collect.BiMap;
-
-/**
- * SlotMapping, this is open generated from relationMapping
- */
-public class SlotMapping extends Mapping {
-
- private final BiMap<MappedSlot, MappedSlot> relationSlotMap;
-
- public SlotMapping(BiMap<MappedSlot, MappedSlot> relationSlotMap) {
- this.relationSlotMap = relationSlotMap;
- }
-
- public BiMap<MappedSlot, MappedSlot> getRelationSlotMap() {
- return relationSlotMap;
- }
-
- public SlotMapping inverse() {
- return SlotMapping.of(relationSlotMap.inverse());
- }
-
- public static SlotMapping of(BiMap<MappedSlot, MappedSlot>
relationSlotMap) {
- return new SlotMapping(relationSlotMap);
- }
-
- public static SlotMapping generate(RelationMapping relationMapping) {
- // TODO implement
- return SlotMapping.of(null);
- }
-}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/StructInfo.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/StructInfo.java
index 141b8a98bcc..ff9dee3ca60 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/StructInfo.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/StructInfo.java
@@ -52,7 +52,7 @@ public class StructInfo {
// construct equivalenceClass according to equals predicates
this.equivalenceClass = new EquivalenceClass();
SplitPredicate splitPredicate =
Predicates.splitPredicates(predicates.composedExpression());
- for (Expression expression :
ExpressionUtils.extractConjunction(splitPredicate.getEqualPredicates())) {
+ for (Expression expression :
ExpressionUtils.extractConjunction(splitPredicate.getEqualPredicate())) {
EqualTo equalTo = (EqualTo) expression;
equivalenceClass.addEquivalenceClass(
(SlotReference) equalTo.getArguments().get(0),
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/ExpressionIndexMapping.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/ExpressionIndexMapping.java
new file mode 100644
index 00000000000..f63017633a8
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/ExpressionIndexMapping.java
@@ -0,0 +1,48 @@
+// 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.exploration.mv.mapping;
+
+import org.apache.doris.nereids.trees.expressions.Expression;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+
+import java.util.List;
+
+/**
+ * Expression and it's index mapping
+ */
+public class ExpressionIndexMapping extends Mapping {
+ private final Multimap<Expression, Integer> expressionIndexMapping;
+
+ public ExpressionIndexMapping(Multimap<Expression, Integer>
expressionIndexMapping) {
+ this.expressionIndexMapping = expressionIndexMapping;
+ }
+
+ public Multimap<Expression, Integer> getExpressionIndexMapping() {
+ return expressionIndexMapping;
+ }
+
+ public static ExpressionIndexMapping generate(List<? extends Expression>
expressions) {
+ Multimap<Expression, Integer> expressionIndexMapping =
ArrayListMultimap.create();
+ for (int i = 0; i < expressions.size(); i++) {
+ expressionIndexMapping.put(expressions.get(i), i);
+ }
+ return new ExpressionIndexMapping(expressionIndexMapping);
+ }
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/Mapping.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/Mapping.java
similarity index 69%
rename from
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/Mapping.java
rename to
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/Mapping.java
index 487fc92ce50..17a412dab10 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/Mapping.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/Mapping.java
@@ -15,18 +15,15 @@
// specific language governing permissions and limitations
// under the License.
-package org.apache.doris.nereids.rules.exploration.mv;
+package org.apache.doris.nereids.rules.exploration.mv.mapping;
import org.apache.doris.nereids.trees.expressions.ExprId;
-import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.Slot;
import org.apache.doris.nereids.trees.plans.RelationId;
import org.apache.doris.nereids.trees.plans.algebra.CatalogRelation;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Multimap;
-
-import java.util.List;
import java.util.Objects;
+import javax.annotation.Nullable;
/**
* Mapping slot from query to view or inversely,
@@ -38,6 +35,7 @@ public abstract class Mapping {
* The relation for mapping
*/
public static final class MappedRelation {
+
public final RelationId relationId;
public final CatalogRelation belongedRelation;
@@ -46,7 +44,7 @@ public abstract class Mapping {
this.belongedRelation = belongedRelation;
}
- public MappedRelation of(RelationId relationId, CatalogRelation
belongedRelation) {
+ public static MappedRelation of(RelationId relationId, CatalogRelation
belongedRelation) {
return new MappedRelation(relationId, belongedRelation);
}
@@ -82,15 +80,31 @@ public abstract class Mapping {
public static final class MappedSlot {
public final ExprId exprId;
+ public final Slot slot;
+ @Nullable
public final CatalogRelation belongedRelation;
- public MappedSlot(ExprId exprId, CatalogRelation belongedRelation) {
+ public MappedSlot(ExprId exprId,
+ Slot slot,
+ CatalogRelation belongedRelation) {
this.exprId = exprId;
+ this.slot = slot;
this.belongedRelation = belongedRelation;
}
- public MappedSlot of(ExprId exprId, CatalogRelation belongedRelation) {
- return new MappedSlot(exprId, belongedRelation);
+ public static MappedSlot of(ExprId exprId,
+ Slot slot,
+ CatalogRelation belongedRelation) {
+ return new MappedSlot(exprId, slot, belongedRelation);
+ }
+
+ public static MappedSlot of(Slot slot,
+ CatalogRelation belongedRelation) {
+ return new MappedSlot(slot.getExprId(), slot, belongedRelation);
+ }
+
+ public static MappedSlot of(Slot slot) {
+ return new MappedSlot(slot.getExprId(), slot, null);
}
public ExprId getExprId() {
@@ -101,6 +115,10 @@ public abstract class Mapping {
return belongedRelation;
}
+ public Slot getSlot() {
+ return slot;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -118,27 +136,4 @@ public abstract class Mapping {
return Objects.hash(exprId);
}
}
-
- /**
- * Expression and it's index mapping
- */
- public static class ExpressionIndexMapping extends Mapping {
- private final Multimap<Expression, Integer> expressionIndexMapping;
-
- public ExpressionIndexMapping(Multimap<Expression, Integer>
expressionIndexMapping) {
- this.expressionIndexMapping = expressionIndexMapping;
- }
-
- public Multimap<Expression, Integer> getExpressionIndexMapping() {
- return expressionIndexMapping;
- }
-
- public static ExpressionIndexMapping generate(List<? extends
Expression> expressions) {
- Multimap<Expression, Integer> expressionIndexMapping =
ArrayListMultimap.create();
- for (int i = 0; i < expressions.size(); i++) {
- expressionIndexMapping.put(expressions.get(i), i);
- }
- return new ExpressionIndexMapping(expressionIndexMapping);
- }
- }
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/RelationMapping.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/RelationMapping.java
new file mode 100644
index 00000000000..fdf47d13433
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/RelationMapping.java
@@ -0,0 +1,113 @@
+// 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.exploration.mv.mapping;
+
+import org.apache.doris.catalog.TableIf;
+import org.apache.doris.common.Pair;
+import org.apache.doris.nereids.trees.plans.algebra.CatalogRelation;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableBiMap.Builder;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Relation mapping
+ * such as query pattern is a1 left join a2 left join b
+ * view pattern is a1 left join a2 left join b. the mapping will be
+ * [{a1:a1, a2:a2, b:b}, {a1:a2, a2:a1, b:b}]
+ */
+public class RelationMapping extends Mapping {
+
+ private final ImmutableBiMap<MappedRelation, MappedRelation>
mappedRelationMap;
+
+ public RelationMapping(ImmutableBiMap<MappedRelation, MappedRelation>
mappedRelationMap) {
+ this.mappedRelationMap = mappedRelationMap;
+ }
+
+ public BiMap<MappedRelation, MappedRelation> getMappedRelationMap() {
+ return mappedRelationMap;
+ }
+
+ public static RelationMapping of(ImmutableBiMap<MappedRelation,
MappedRelation> mappedRelationMap) {
+ return new RelationMapping(mappedRelationMap);
+ }
+
+ /**
+ * Generate mapping according to source and target relation
+ */
+ public static List<RelationMapping> generate(List<CatalogRelation>
sources, List<CatalogRelation> targets) {
+ // Construct tmp map, key is the table qualifier, value is the
corresponding catalog relations
+ LinkedListMultimap<Long, MappedRelation> sourceTableRelationIdMap =
LinkedListMultimap.create();
+ for (CatalogRelation relation : sources) {
+
sourceTableRelationIdMap.put(getTableQualifier(relation.getTable()),
+ MappedRelation.of(relation.getRelationId(), relation));
+ }
+ LinkedListMultimap<Long, MappedRelation> targetTableRelationIdMap =
LinkedListMultimap.create();
+ for (CatalogRelation relation : targets) {
+
targetTableRelationIdMap.put(getTableQualifier(relation.getTable()),
+ MappedRelation.of(relation.getRelationId(), relation));
+ }
+ Set<Long> sourceTableKeySet = sourceTableRelationIdMap.keySet();
+ List<List<Pair<MappedRelation, MappedRelation>>> mappedRelations = new
ArrayList<>();
+
+ for (Long sourceTableQualifier : sourceTableKeySet) {
+ List<MappedRelation> sourceMappedRelations =
sourceTableRelationIdMap.get(sourceTableQualifier);
+ List<MappedRelation> targetMappedRelations =
targetTableRelationIdMap.get(sourceTableQualifier);
+ if (targetMappedRelations.isEmpty()) {
+ continue;
+ }
+ // if source and target relation appear once, just map them
+ if (targetMappedRelations.size() == 1 &&
sourceMappedRelations.size() == 1) {
+
mappedRelations.add(ImmutableList.of(Pair.of(sourceMappedRelations.get(0),
+ targetMappedRelations.get(0))));
+ continue;
+ }
+ // relation appear more than once, should cartesian them
+ ImmutableList<Pair<MappedRelation, MappedRelation>>
relationMapping = Lists.cartesianProduct(
+
sourceTableRelationIdMap.get(sourceTableQualifier), targetMappedRelations)
+ .stream()
+ .map(listPair -> Pair.of(listPair.get(0), listPair.get(1)))
+ .collect(ImmutableList.toImmutableList());
+ mappedRelations.add(relationMapping);
+ }
+
+ int mappedRelationCount = mappedRelations.size();
+
+ return Lists.cartesianProduct(mappedRelations).stream()
+ .map(mappedRelationList -> {
+ Builder<MappedRelation, MappedRelation> mapBuilder =
ImmutableBiMap.builder();
+ for (int relationIndex = 0; relationIndex <
mappedRelationCount; relationIndex++) {
+
mapBuilder.put(mappedRelationList.get(relationIndex).key(),
+ mappedRelationList.get(relationIndex).value());
+ }
+ return RelationMapping.of(mapBuilder.build());
+ })
+ .collect(ImmutableList.toImmutableList());
+ }
+
+ private static Long getTableQualifier(TableIf tableIf) {
+ return tableIf.getId();
+ }
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/SlotMapping.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/SlotMapping.java
new file mode 100644
index 00000000000..520d6be9cdd
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/SlotMapping.java
@@ -0,0 +1,84 @@
+// 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.exploration.mv.mapping;
+
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+
+import java.util.Map;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+
+/**
+ * SlotMapping, this is open generated from relationMapping
+ */
+public class SlotMapping extends Mapping {
+
+ private final BiMap<MappedSlot, MappedSlot> slotMapping;
+
+ public SlotMapping(BiMap<MappedSlot, MappedSlot> slotMapping) {
+ this.slotMapping = slotMapping;
+ }
+
+ public BiMap<MappedSlot, MappedSlot> getSlotBiMap() {
+ return slotMapping;
+ }
+
+ public SlotMapping inverse() {
+ return slotMapping == null
+ ? SlotMapping.of(HashBiMap.create()) :
SlotMapping.of(slotMapping.inverse());
+ }
+
+ public static SlotMapping of(BiMap<MappedSlot, MappedSlot>
relationSlotMap) {
+ return new SlotMapping(relationSlotMap);
+ }
+
+ /**
+ * SlotMapping, this is open generated from relationMapping
+ */
+ @Nullable
+ public static SlotMapping generate(RelationMapping relationMapping) {
+ BiMap<MappedSlot, MappedSlot> relationSlotMap = HashBiMap.create();
+ BiMap<MappedRelation, MappedRelation> mappedRelationMap =
relationMapping.getMappedRelationMap();
+ for (Map.Entry<MappedRelation, MappedRelation> mappedRelationEntry :
mappedRelationMap.entrySet()) {
+ Map<String, Slot> targetNameSlotMap =
+
mappedRelationEntry.getValue().getBelongedRelation().getOutput().stream()
+ .collect(Collectors.toMap(Slot::getName, slot ->
slot));
+ for (Slot sourceSlot :
mappedRelationEntry.getKey().getBelongedRelation().getOutput()) {
+ Slot targetSlot = targetNameSlotMap.get(sourceSlot.getName());
+ // source slot can not map from target, bail out
+ if (targetSlot == null) {
+ return null;
+ }
+ relationSlotMap.put(MappedSlot.of(sourceSlot,
mappedRelationEntry.getKey().getBelongedRelation()),
+ MappedSlot.of(targetSlot,
mappedRelationEntry.getValue().getBelongedRelation()));
+ }
+ }
+ return SlotMapping.of(relationSlotMap);
+ }
+
+ /**
+ * SlotMapping, getSlotMap
+ */
+ public Map<? extends Expression, ? extends Expression> getSlotMap() {
+ return (Map) this.getSlotBiMap();
+ }
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitors.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitors.java
index 86949f63e16..513da0e93d9 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitors.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitors.java
@@ -17,16 +17,9 @@
package org.apache.doris.nereids.trees.expressions.visitor;
-import org.apache.doris.nereids.rules.exploration.mv.Predicates;
-import org.apache.doris.nereids.trees.expressions.ComparisonPredicate;
-import org.apache.doris.nereids.trees.expressions.EqualTo;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.WindowExpression;
import
org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
-import org.apache.doris.nereids.util.ExpressionUtils;
-
-import java.util.ArrayList;
-import java.util.List;
/**
* This is the factory for all ExpressionVisitor instance.
@@ -61,46 +54,4 @@ public class ExpressionVisitors {
return true;
}
}
-
- /**
- * Split the expression to equal, range and residual predicate.
- * Should instance when used.
- */
- public static class PredicatesSpliter extends
DefaultExpressionVisitor<Void, Void> {
-
- private List<Expression> equalPredicates = new ArrayList<>();
- private List<Expression> rangePredicates = new ArrayList<>();
- private List<Expression> residualPredicates = new ArrayList<>();
- private final Expression target;
-
- public PredicatesSpliter(Expression target) {
- this.target = target;
- }
-
- @Override
- public Void visitComparisonPredicate(ComparisonPredicate
comparisonPredicate, Void context) {
- // TODO Smallest implement, complete later
- if (comparisonPredicate instanceof EqualTo) {
- Expression leftArgument = comparisonPredicate.getArgument(0);
- Expression rightArgument = comparisonPredicate.getArgument(1);
- if (leftArgument.isSlot() && rightArgument.isSlot()) {
- equalPredicates.add(comparisonPredicate);
- } else {
- rangePredicates.add(comparisonPredicate);
- }
- }
- return super.visit(comparisonPredicate, context);
- }
-
- public Expression getTarget() {
- return target;
- }
-
- public Predicates.SplitPredicate getSplitPredicate() {
- return Predicates.SplitPredicate.of(
- equalPredicates.isEmpty() ? null :
ExpressionUtils.and(equalPredicates),
- rangePredicates.isEmpty() ? null :
ExpressionUtils.and(rangePredicates),
- residualPredicates.isEmpty() ? null :
ExpressionUtils.and(residualPredicates));
- }
- }
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/ExpressionLineageReplacer.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/ExpressionLineageReplacer.java
new file mode 100644
index 00000000000..888b538d049
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/ExpressionLineageReplacer.java
@@ -0,0 +1,160 @@
+// 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.plans.visitor;
+
+import org.apache.doris.catalog.TableIf.TableType;
+import org.apache.doris.nereids.trees.expressions.Alias;
+import
org.apache.doris.nereids.trees.expressions.ArrayItemReference.ArrayItemSlot;
+import org.apache.doris.nereids.trees.expressions.ExprId;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import
org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionRewriter;
+import
org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionVisitor;
+import org.apache.doris.nereids.trees.plans.Plan;
+import
org.apache.doris.nereids.trees.plans.visitor.ExpressionLineageReplacer.ExpressionReplaceContext;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * ExpressionLineageReplacer
+ * Get from rewrite plan and can also get from plan struct info, if from plan
struct info it depends on
+ * the nodes from graph.
+ */
+public class ExpressionLineageReplacer extends DefaultPlanVisitor<Expression,
ExpressionReplaceContext> {
+
+ public static final ExpressionLineageReplacer INSTANCE = new
ExpressionLineageReplacer();
+
+ @Override
+ public Expression visit(Plan plan, ExpressionReplaceContext context) {
+ List<? extends Expression> expressions = plan.getExpressions();
+ Map<ExprId, Expression> targetExpressionMap =
context.getExprIdExpressionMap();
+ // Filter the namedExpression used by target and collect the
namedExpression
+ expressions.stream()
+ .filter(expression -> expression instanceof NamedExpression
+ && targetExpressionMap.containsKey(((NamedExpression)
expression).getExprId()))
+ .forEach(expression ->
expression.accept(NamedExpressionCollector.INSTANCE, context));
+ return super.visit(plan, context);
+ }
+
+ /**
+ * Replace the expression with lineage according the exprIdExpressionMap
+ */
+ public static class ExpressionReplacer extends
DefaultExpressionRewriter<Map<ExprId, Expression>> {
+
+ public static final ExpressionReplacer INSTANCE = new
ExpressionReplacer();
+
+ @Override
+ public Expression visitNamedExpression(NamedExpression namedExpression,
+ Map<ExprId, Expression> exprIdExpressionMap) {
+ if (exprIdExpressionMap.containsKey(namedExpression.getExprId())) {
+ return
super.visit(exprIdExpressionMap.get(namedExpression.getExprId()),
exprIdExpressionMap);
+ }
+ return super.visitNamedExpression(namedExpression,
exprIdExpressionMap);
+ }
+ }
+
+ /**
+ * The Collector for target named expressions
+ * TODO Collect named expression by targetTypes, tableIdentifiers
+ */
+ public static class NamedExpressionCollector
+ extends DefaultExpressionVisitor<Void, ExpressionReplaceContext> {
+
+ public static final NamedExpressionCollector INSTANCE = new
NamedExpressionCollector();
+
+ @Override
+ public Void visitSlotReference(SlotReference slotReference,
ExpressionReplaceContext context) {
+ context.getExprIdExpressionMap().put(slotReference.getExprId(),
slotReference);
+ return super.visitSlotReference(slotReference, context);
+ }
+
+ @Override
+ public Void visitArrayItemSlot(ArrayItemSlot arrayItemSlot,
ExpressionReplaceContext context) {
+ context.getExprIdExpressionMap().put(arrayItemSlot.getExprId(),
arrayItemSlot);
+ return super.visitArrayItemSlot(arrayItemSlot, context);
+ }
+
+ @Override
+ public Void visitAlias(Alias alias, ExpressionReplaceContext context) {
+ // remove the alias
+ if
(context.getExprIdExpressionMap().containsKey(alias.getExprId())) {
+ context.getExprIdExpressionMap().put(alias.getExprId(),
alias.child());
+ }
+ return super.visitAlias(alias, context);
+ }
+ }
+
+ /**
+ * The context for replacing the expression with lineage
+ */
+ public static class ExpressionReplaceContext {
+ private final List<Expression> targetExpressions;
+ private final Set<TableType> targetTypes;
+ private final Set<String> tableIdentifiers;
+ private Map<ExprId, Expression> exprIdExpressionMap;
+ private List<Expression> replacedExpressions;
+
+ /**ExpressionReplaceContext*/
+ public ExpressionReplaceContext(List<Expression> targetExpressions,
+ Set<TableType> targetTypes,
+ Set<String> tableIdentifiers) {
+ this.targetExpressions = targetExpressions;
+ this.targetTypes = targetTypes;
+ this.tableIdentifiers = tableIdentifiers;
+ // collect only named expressions and replace them with linage
identifier later
+ this.exprIdExpressionMap = targetExpressions.stream()
+ .map(each ->
each.collectToList(NamedExpression.class::isInstance))
+ .flatMap(Collection::stream)
+ .map(NamedExpression.class::cast)
+ .collect(Collectors.toMap(NamedExpression::getExprId, expr
-> expr));
+ }
+
+ public List<Expression> getTargetExpressions() {
+ return targetExpressions;
+ }
+
+ public Set<TableType> getTargetTypes() {
+ return targetTypes;
+ }
+
+ public Set<String> getTableIdentifiers() {
+ return tableIdentifiers;
+ }
+
+ public Map<ExprId, Expression> getExprIdExpressionMap() {
+ return exprIdExpressionMap;
+ }
+
+ /**
+ * getReplacedExpressions
+ */
+ public List<Expression> getReplacedExpressions() {
+ if (this.replacedExpressions == null) {
+ this.replacedExpressions = targetExpressions.stream()
+ .map(original ->
original.accept(ExpressionReplacer.INSTANCE, getExprIdExpressionMap()))
+ .collect(Collectors.toList());
+ }
+ return this.replacedExpressions;
+ }
+ }
+}
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
index 3149d140fe4..c44e888fbc8 100644
---
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
@@ -19,7 +19,6 @@ package org.apache.doris.nereids.util;
import org.apache.doris.catalog.TableIf.TableType;
import org.apache.doris.nereids.CascadesContext;
-import org.apache.doris.nereids.rules.exploration.mv.SlotMapping;
import org.apache.doris.nereids.rules.expression.ExpressionRewriteContext;
import org.apache.doris.nereids.rules.expression.rules.FoldConstantRule;
import org.apache.doris.nereids.trees.TreeNode;
@@ -48,6 +47,7 @@ import
org.apache.doris.nereids.trees.expressions.literal.Literal;
import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
import
org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionRewriter;
import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.visitor.ExpressionLineageReplacer;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
@@ -206,28 +206,28 @@ public class ExpressionUtils {
}
/**
- * Replace the slot in expression with the lineage identifier from
specified
- * baseTable sets or target table types.
- * <p>
- * For example as following:
+ * Replace the slot in expressions with the lineage identifier from
specifiedbaseTable sets or target table types
+ * example as following:
* select a + 10 as a1, d from (
* select b - 5 as a, d from table
* );
- * after shuttle a1, d in select will be b - 5 + 10, d
+ * op expression before is: a + 10 as a1, d. after is: b - 5 + 10, d
+ * todo to get from plan struct info
*/
- public static List<? extends Expression>
shuttleExpressionWithLineage(List<? extends Expression> expression,
+ public static List<? extends Expression>
shuttleExpressionWithLineage(List<? extends Expression> expressions,
Plan plan,
Set<TableType> targetTypes,
Set<String> tableIdentifiers) {
- return ImmutableList.of();
- }
- /**
- * Replace the slot in expressions according to the slotMapping
- * if any slot cannot be mapped then return null
- */
- public static List<? extends Expression> permute(List<? extends
Expression> expressions, SlotMapping slotMapping) {
- return ImmutableList.of();
+ ExpressionLineageReplacer.ExpressionReplaceContext replaceContext =
+ new ExpressionLineageReplacer.ExpressionReplaceContext(
+
expressions.stream().map(NamedExpression.class::cast).collect(Collectors.toList()),
+ targetTypes,
+ tableIdentifiers);
+
+ plan.accept(ExpressionLineageReplacer.INSTANCE, replaceContext);
+ // Replace expressions by expression map
+ return replaceContext.getReplacedExpressions();
}
/**
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/MappingTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/MappingTest.java
new file mode 100644
index 00000000000..39f6033b4a7
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/MappingTest.java
@@ -0,0 +1,309 @@
+// 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.exploration.mv;
+
+import org.apache.doris.nereids.rules.exploration.mv.mapping.RelationMapping;
+import org.apache.doris.nereids.rules.exploration.mv.mapping.SlotMapping;
+import org.apache.doris.nereids.trees.expressions.ExprId;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.RelationId;
+import org.apache.doris.nereids.trees.plans.algebra.CatalogRelation;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanVisitor;
+import org.apache.doris.nereids.util.PlanChecker;
+import org.apache.doris.utframe.TestWithFeService;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**MappingTest*/
+public class MappingTest extends TestWithFeService {
+
+ @Override
+ protected void runBeforeAll() throws Exception {
+ createDatabase("mapping_test");
+ useDatabase("mapping_test");
+
+ createTable("CREATE TABLE IF NOT EXISTS lineitem (\n"
+ + " L_ORDERKEY INTEGER NOT NULL,\n"
+ + " L_PARTKEY INTEGER NOT NULL\n"
+ + ")\n"
+ + "DUPLICATE KEY(L_ORDERKEY, L_PARTKEY)\n"
+ + "DISTRIBUTED BY HASH(L_ORDERKEY) BUCKETS 3\n"
+ + "PROPERTIES (\n"
+ + " \"replication_num\" = \"1\"\n"
+ + ")");
+ createTable("CREATE TABLE IF NOT EXISTS orders (\n"
+ + " O_ORDERKEY INTEGER NOT NULL,\n"
+ + " O_CUSTKEY INTEGER NOT NULL,\n"
+ + " O_ORDERSTATUS CHAR(1) NOT NULL\n"
+ + ")\n"
+ + "DUPLICATE KEY(O_ORDERKEY, O_CUSTKEY)\n"
+ + "DISTRIBUTED BY HASH(O_ORDERKEY) BUCKETS 3\n"
+ + "PROPERTIES (\n"
+ + " \"replication_num\" = \"1\"\n"
+ + ")");
+ createTable("CREATE TABLE IF NOT EXISTS customer (\n"
+ + " C_CUSTKEY INTEGER NOT NULL,\n"
+ + " C_NAME VARCHAR(25) NOT NULL,\n"
+ + ")\n"
+ + "DUPLICATE KEY(C_CUSTKEY, C_NAME)\n"
+ + "DISTRIBUTED BY HASH(C_CUSTKEY) BUCKETS 3\n"
+ + "PROPERTIES (\n"
+ + " \"replication_num\" = \"1\"\n"
+ + ")");
+ }
+
+ // test the num of source and target table is same
+ @Test
+ public void testGenerateMapping1() {
+ Plan sourcePlan = PlanChecker.from(connectContext)
+ .analyze("SELECT * "
+ + "FROM\n"
+ + " orders,\n"
+ + " lineitem,\n"
+ + " customer\n"
+ + "WHERE\n"
+ + " c_custkey = o_custkey\n"
+ + " AND l_orderkey = o_orderkey")
+ .getPlan();
+
+ Plan targetPlan = PlanChecker.from(connectContext)
+ .analyze("SELECT * "
+ + "FROM\n"
+ + " customer,\n"
+ + " orders,\n"
+ + " lineitem\n"
+ + "WHERE\n"
+ + " c_custkey = o_custkey\n"
+ + " AND l_orderkey = o_orderkey")
+ .getPlan();
+ List<CatalogRelation> sourceRelations = new ArrayList<>();
+ sourcePlan.accept(RelationCollector.INSTANCE, sourceRelations);
+
+ List<CatalogRelation> targetRelations = new ArrayList<>();
+ targetPlan.accept(RelationCollector.INSTANCE, targetRelations);
+
+ List<RelationMapping> generateRelationMapping =
RelationMapping.generate(sourceRelations, targetRelations);
+ Assertions.assertNotNull(generateRelationMapping);
+ Assertions.assertEquals(1, generateRelationMapping.size());
+
+ // expected slot mapping
+ BiMap<ExprId, ExprId> expectedSlotMapping = HashBiMap.create();
+ expectedSlotMapping.put(new ExprId(0), new ExprId(2));
+ expectedSlotMapping.put(new ExprId(1), new ExprId(3));
+ expectedSlotMapping.put(new ExprId(2), new ExprId(4));
+ expectedSlotMapping.put(new ExprId(3), new ExprId(5));
+ expectedSlotMapping.put(new ExprId(4), new ExprId(6));
+ expectedSlotMapping.put(new ExprId(5), new ExprId(0));
+ expectedSlotMapping.put(new ExprId(6), new ExprId(1));
+ // expected relation mapping
+ BiMap<RelationId, RelationId> expectedRelationMapping =
HashBiMap.create();
+ expectedRelationMapping.put(new RelationId(0), new RelationId(1));
+ expectedRelationMapping.put(new RelationId(1), new RelationId(2));
+ expectedRelationMapping.put(new RelationId(2), new RelationId(0));
+ assertRelationMapping(generateRelationMapping.get(0),
expectedRelationMapping, expectedSlotMapping);
+ }
+
+ // test the num of source table is less than target table
+ @Test
+ public void testGenerateMapping2() {
+ Plan sourcePlan = PlanChecker.from(connectContext)
+ .analyze("SELECT * "
+ + "FROM\n"
+ + " orders,\n"
+ + " customer\n"
+ + "WHERE\n"
+ + " c_custkey = o_custkey")
+ .getPlan();
+
+ Plan targetPlan = PlanChecker.from(connectContext)
+ .analyze("SELECT * "
+ + "FROM\n"
+ + " customer,\n"
+ + " orders,\n"
+ + " lineitem\n"
+ + "WHERE\n"
+ + " c_custkey = o_custkey\n"
+ + " AND l_orderkey = o_orderkey")
+ .getPlan();
+ List<CatalogRelation> sourceRelations = new ArrayList<>();
+ sourcePlan.accept(RelationCollector.INSTANCE, sourceRelations);
+
+ List<CatalogRelation> targetRelations = new ArrayList<>();
+ targetPlan.accept(RelationCollector.INSTANCE, targetRelations);
+
+ List<RelationMapping> generateRelationMapping =
RelationMapping.generate(sourceRelations, targetRelations);
+ Assertions.assertNotNull(generateRelationMapping);
+ Assertions.assertEquals(1, generateRelationMapping.size());
+
+ // expected slot mapping
+ BiMap<ExprId, ExprId> expectedSlotMapping = HashBiMap.create();
+ expectedSlotMapping.put(new ExprId(0), new ExprId(2));
+ expectedSlotMapping.put(new ExprId(1), new ExprId(3));
+ expectedSlotMapping.put(new ExprId(2), new ExprId(4));
+ expectedSlotMapping.put(new ExprId(3), new ExprId(0));
+ expectedSlotMapping.put(new ExprId(4), new ExprId(1));
+ // expected relation mapping
+ BiMap<RelationId, RelationId> expectedRelationMapping =
HashBiMap.create();
+ expectedRelationMapping.put(new RelationId(0), new RelationId(1));
+ expectedRelationMapping.put(new RelationId(1), new RelationId(0));
+ assertRelationMapping(generateRelationMapping.get(0),
expectedRelationMapping, expectedSlotMapping);
+ }
+
+ // test the num of source table is more than target table
+ @Test
+ public void testGenerateMapping3() {
+ Plan sourcePlan = PlanChecker.from(connectContext)
+ .analyze("SELECT * "
+ + "FROM\n"
+ + " orders,\n"
+ + " lineitem,\n"
+ + " customer\n"
+ + "WHERE\n"
+ + " c_custkey = o_custkey\n"
+ + " AND l_orderkey = o_orderkey")
+ .getPlan();
+
+ Plan targetPlan = PlanChecker.from(connectContext)
+ .analyze("SELECT * "
+ + "FROM\n"
+ + " customer,\n"
+ + " orders\n"
+ + "WHERE\n"
+ + " c_custkey = o_custkey\n")
+ .getPlan();
+ List<CatalogRelation> sourceRelations = new ArrayList<>();
+ sourcePlan.accept(RelationCollector.INSTANCE, sourceRelations);
+
+ List<CatalogRelation> targetRelations = new ArrayList<>();
+ targetPlan.accept(RelationCollector.INSTANCE, targetRelations);
+
+ List<RelationMapping> generateRelationMapping =
RelationMapping.generate(sourceRelations, targetRelations);
+ Assertions.assertNotNull(generateRelationMapping);
+ Assertions.assertEquals(1, generateRelationMapping.size());
+
+ // expected slot mapping
+ BiMap<ExprId, ExprId> expectedSlotMapping = HashBiMap.create();
+ expectedSlotMapping.put(new ExprId(0), new ExprId(2));
+ expectedSlotMapping.put(new ExprId(1), new ExprId(3));
+ expectedSlotMapping.put(new ExprId(2), new ExprId(4));
+ expectedSlotMapping.put(new ExprId(5), new ExprId(0));
+ expectedSlotMapping.put(new ExprId(6), new ExprId(1));
+ // expected relation mapping
+ BiMap<RelationId, RelationId> expectedRelationMapping =
HashBiMap.create();
+ expectedRelationMapping.put(new RelationId(0), new RelationId(1));
+ expectedRelationMapping.put(new RelationId(2), new RelationId(0));
+ assertRelationMapping(generateRelationMapping.get(0),
expectedRelationMapping, expectedSlotMapping);
+ }
+
+ // test table of source query is repeated
+ @Test
+ public void testGenerateMapping4() {
+ Plan sourcePlan = PlanChecker.from(connectContext)
+ .analyze("SELECT orders.*, l1.* "
+ + "FROM\n"
+ + " orders,\n"
+ + " lineitem l1,\n"
+ + " lineitem l2\n"
+ + "WHERE\n"
+ + " l1.l_orderkey = l2.l_orderkey\n"
+ + " AND l1.l_orderkey = o_orderkey")
+ .getPlan();
+
+ Plan targetPlan = PlanChecker.from(connectContext)
+ .analyze("SELECT * "
+ + "FROM\n"
+ + " lineitem,\n"
+ + " orders\n"
+ + "WHERE\n"
+ + " l_orderkey = o_orderkey")
+ .getPlan();
+ List<CatalogRelation> sourceRelations = new ArrayList<>();
+ sourcePlan.accept(RelationCollector.INSTANCE, sourceRelations);
+
+ List<CatalogRelation> targetRelations = new ArrayList<>();
+ targetPlan.accept(RelationCollector.INSTANCE, targetRelations);
+
+ List<RelationMapping> generateRelationMapping =
RelationMapping.generate(sourceRelations, targetRelations);
+ Assertions.assertNotNull(generateRelationMapping);
+ Assertions.assertEquals(2, generateRelationMapping.size());
+
+ // expected slot mapping
+ BiMap<ExprId, ExprId> expectedSlotMapping = HashBiMap.create();
+ expectedSlotMapping.put(new ExprId(0), new ExprId(2));
+ expectedSlotMapping.put(new ExprId(1), new ExprId(3));
+ expectedSlotMapping.put(new ExprId(2), new ExprId(4));
+ expectedSlotMapping.put(new ExprId(3), new ExprId(0));
+ expectedSlotMapping.put(new ExprId(4), new ExprId(1));
+ // expected relation mapping
+ BiMap<RelationId, RelationId> expectedRelationMapping =
HashBiMap.create();
+ expectedRelationMapping.put(new RelationId(0), new RelationId(1));
+ expectedRelationMapping.put(new RelationId(1), new RelationId(0));
+ assertRelationMapping(generateRelationMapping.get(0),
expectedRelationMapping, expectedSlotMapping);
+
+ // expected slot mapping
+ expectedSlotMapping = HashBiMap.create();
+ expectedSlotMapping.put(new ExprId(0), new ExprId(2));
+ expectedSlotMapping.put(new ExprId(1), new ExprId(3));
+ expectedSlotMapping.put(new ExprId(2), new ExprId(4));
+ expectedSlotMapping.put(new ExprId(5), new ExprId(0));
+ expectedSlotMapping.put(new ExprId(6), new ExprId(1));
+ // expected relation mapping
+ expectedRelationMapping = HashBiMap.create();
+ expectedRelationMapping.put(new RelationId(0), new RelationId(1));
+ expectedRelationMapping.put(new RelationId(2), new RelationId(0));
+ assertRelationMapping(generateRelationMapping.get(1),
expectedRelationMapping, expectedSlotMapping);
+ }
+
+ private void assertRelationMapping(RelationMapping relationMapping,
+ BiMap<RelationId, RelationId> expectRelationMapping,
+ BiMap<ExprId, ExprId> expectSlotMapping) {
+ // check relation mapping
+ BiMap<RelationId, RelationId> generatedRelationMapping =
HashBiMap.create();
+ relationMapping.getMappedRelationMap().forEach((key, value) ->
+ generatedRelationMapping.put(key.getRelationId(),
value.getRelationId()));
+ Assertions.assertEquals(generatedRelationMapping,
expectRelationMapping);
+
+ // Generate slot mapping from relationMapping and check
+ SlotMapping slotMapping = SlotMapping.generate(relationMapping);
+ Assertions.assertNotNull(slotMapping);
+ BiMap<ExprId, ExprId> generatedSlotMapping = HashBiMap.create();
+ slotMapping.getSlotBiMap().forEach((key, value) ->
+ generatedSlotMapping.put(key.getExprId(), value.getExprId())
+ );
+ Assertions.assertEquals(generatedSlotMapping, expectSlotMapping);
+ }
+
+ protected static class RelationCollector extends DefaultPlanVisitor<Void,
List<CatalogRelation>> {
+
+ public static final RelationCollector INSTANCE = new
RelationCollector();
+
+ @Override
+ public Void visit(Plan plan, List<CatalogRelation> catalogRelations) {
+ if (plan instanceof CatalogRelation) {
+ catalogRelations.add((CatalogRelation) plan);
+ }
+ return super.visit(plan, catalogRelations);
+ }
+ }
+}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/ExpressionRewriteTestHelper.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/ExpressionRewriteTestHelper.java
index e8cc4dd266a..ffd3c29a18d 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/ExpressionRewriteTestHelper.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/ExpressionRewriteTestHelper.java
@@ -108,7 +108,7 @@ public abstract class ExpressionRewriteTestHelper {
return FunctionBinder.INSTANCE.rewrite(expression, null);
}
- private DataType getType(char t) {
+ protected DataType getType(char t) {
switch (t) {
case 'T':
return TinyIntType.INSTANCE;
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/PredicatesSplitterTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/PredicatesSplitterTest.java
new file mode 100644
index 00000000000..4a8f2e981ef
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/PredicatesSplitterTest.java
@@ -0,0 +1,109 @@
+// 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.expression;
+
+import org.apache.doris.catalog.Column;
+import org.apache.doris.nereids.analyzer.UnboundSlot;
+import org.apache.doris.nereids.rules.exploration.mv.Predicates;
+import org.apache.doris.nereids.rules.exploration.mv.Predicates.SplitPredicate;
+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 com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * PredicatesSplitterTest
+ */
+public class PredicatesSplitterTest extends ExpressionRewriteTestHelper {
+
+ @Test
+ public void testSplitPredicates() {
+ assetEquals("a = b and (c = d or a = 10) and a > 7 and 10 > d",
+ "a = b",
+ "a > 7 and 10 > d",
+ "c = d or a = 10");
+ assetEquals("a = b and c + d = e and a > 7 and 10 > d",
+ "a = b",
+ "a > 7 and 10 > d",
+ "c + d = e");
+ assetEquals("a = b and c + d = e or a > 7 and 10 > d",
+ "",
+ "",
+ "a = b and c + d = e or a > 7 and 10 > d");
+ }
+
+ private void assetEquals(String expression,
+ String expectedEqualExpr,
+ String expectedRangeExpr,
+ String expectedResidualExpr) {
+
+ Map<String, Slot> mem = Maps.newHashMap();
+ Expression targetExpr =
replaceUnboundSlot(PARSER.parseExpression(expression), mem);
+ SplitPredicate splitPredicate = Predicates.splitPredicates(targetExpr);
+
+ if (!StringUtils.isEmpty(expectedEqualExpr)) {
+ Expression equalExpression =
replaceUnboundSlot(PARSER.parseExpression(expectedEqualExpr), mem);
+ Assertions.assertEquals(equalExpression,
splitPredicate.getEqualPredicate());
+ } else {
+ Assertions.assertNull(splitPredicate.getEqualPredicate());
+ }
+
+ if (!StringUtils.isEmpty(expectedRangeExpr)) {
+ Expression rangeExpression =
replaceUnboundSlot(PARSER.parseExpression(expectedRangeExpr), mem);
+ Assertions.assertEquals(rangeExpression,
splitPredicate.getRangePredicate());
+ } else {
+ Assertions.assertNull(splitPredicate.getRangePredicate());
+ }
+
+ if (!StringUtils.isEmpty(expectedResidualExpr)) {
+ Expression residualExpression =
replaceUnboundSlot(PARSER.parseExpression(expectedResidualExpr), mem);
+ Assertions.assertEquals(residualExpression,
splitPredicate.getResidualPredicate());
+ } else {
+ Assertions.assertNull(splitPredicate.getResidualPredicate());
+ }
+ }
+
+ @Override
+ public Expression replaceUnboundSlot(Expression expression, Map<String,
Slot> mem) {
+ List<Expression> children = Lists.newArrayList();
+ boolean hasNewChildren = false;
+ for (Expression child : expression.children()) {
+ Expression newChild = replaceUnboundSlot(child, mem);
+ if (newChild != child) {
+ hasNewChildren = true;
+ }
+ children.add(newChild);
+ }
+ if (expression instanceof UnboundSlot) {
+ String name = ((UnboundSlot) expression).getName();
+ mem.putIfAbsent(name, SlotReference.fromColumn(
+ new Column(name,
getType(name.charAt(0)).toCatalogDataType()),
+ Lists.newArrayList("table")));
+ return mem.get(name);
+ }
+ return hasNewChildren ? expression.withChildren(children) : expression;
+ }
+}
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
index 55f78faa20b..7ab3e8083d1 100644
---
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
@@ -19,7 +19,10 @@ package org.apache.doris.nereids.util;
import org.apache.doris.nereids.parser.NereidsParser;
import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.utframe.TestWithFeService;
+import com.google.common.collect.Sets;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -28,10 +31,70 @@ import java.util.List;
/**
* ExpressionUtils ut.
*/
-public class ExpressionUtilsTest {
+public class ExpressionUtilsTest extends TestWithFeService {
private static final NereidsParser PARSER = new NereidsParser();
+ @Override
+ protected void runBeforeAll() throws Exception {
+ createDatabase("expression_test");
+ useDatabase("expression_test");
+
+ createTable("CREATE TABLE IF NOT EXISTS lineitem (\n"
+ + " L_ORDERKEY INTEGER NOT NULL,\n"
+ + " L_PARTKEY INTEGER NOT NULL,\n"
+ + " L_SUPPKEY INTEGER NOT NULL,\n"
+ + " L_LINENUMBER INTEGER NOT NULL,\n"
+ + " L_QUANTITY DECIMALV3(15,2) NOT NULL,\n"
+ + " L_EXTENDEDPRICE DECIMALV3(15,2) NOT NULL,\n"
+ + " L_DISCOUNT DECIMALV3(15,2) NOT NULL,\n"
+ + " L_TAX DECIMALV3(15,2) NOT NULL,\n"
+ + " L_RETURNFLAG CHAR(1) NOT NULL,\n"
+ + " L_LINESTATUS CHAR(1) NOT NULL,\n"
+ + " L_SHIPDATE DATE NOT NULL,\n"
+ + " L_COMMITDATE DATE NOT NULL,\n"
+ + " L_RECEIPTDATE DATE NOT NULL,\n"
+ + " L_SHIPINSTRUCT CHAR(25) NOT NULL,\n"
+ + " L_SHIPMODE CHAR(10) NOT NULL,\n"
+ + " L_COMMENT VARCHAR(44) NOT NULL\n"
+ + ")\n"
+ + "DUPLICATE KEY(L_ORDERKEY, L_PARTKEY, L_SUPPKEY,
L_LINENUMBER)\n"
+ + "DISTRIBUTED BY HASH(L_ORDERKEY) BUCKETS 3\n"
+ + "PROPERTIES (\n"
+ + " \"replication_num\" = \"1\"\n"
+ + ")");
+ createTable("CREATE TABLE IF NOT EXISTS orders (\n"
+ + " O_ORDERKEY INTEGER NOT NULL,\n"
+ + " O_CUSTKEY INTEGER NOT NULL,\n"
+ + " O_ORDERSTATUS CHAR(1) NOT NULL,\n"
+ + " O_TOTALPRICE DECIMALV3(15,2) NOT NULL,\n"
+ + " O_ORDERDATE DATE NOT NULL,\n"
+ + " O_ORDERPRIORITY CHAR(15) NOT NULL, \n"
+ + " O_CLERK CHAR(15) NOT NULL, \n"
+ + " O_SHIPPRIORITY INTEGER NOT NULL,\n"
+ + " O_COMMENT VARCHAR(79) NOT NULL\n"
+ + ")\n"
+ + "DUPLICATE KEY(O_ORDERKEY, O_CUSTKEY)\n"
+ + "DISTRIBUTED BY HASH(O_ORDERKEY) BUCKETS 3\n"
+ + "PROPERTIES (\n"
+ + " \"replication_num\" = \"1\"\n"
+ + ")");
+ createTable("CREATE TABLE IF NOT EXISTS partsupp (\n"
+ + " PS_PARTKEY INTEGER NOT NULL,\n"
+ + " PS_SUPPKEY INTEGER NOT NULL,\n"
+ + " PS_AVAILQTY INTEGER NOT NULL,\n"
+ + " PS_SUPPLYCOST DECIMALV3(15,2) NOT NULL,\n"
+ + " PS_COMMENT VARCHAR(199) NOT NULL \n"
+ + ")\n"
+ + "DUPLICATE KEY(PS_PARTKEY, PS_SUPPKEY)\n"
+ + "DISTRIBUTED BY HASH(PS_PARTKEY) BUCKETS 3\n"
+ + "PROPERTIES (\n"
+ + " \"replication_num\" = \"1\"\n"
+ + ")");
+ connectContext.getSessionVariable().enableNereidsTimeout = false;
+
+ }
+
@Test
public void extractConjunctionTest() {
List<Expression> expressions;
@@ -93,4 +156,45 @@ public class ExpressionUtilsTest {
Assertions.assertEquals(c, expressions.get(1));
Assertions.assertEquals(eAndf, expressions.get(2));
}
+
+ @Test
+ public void testShuttleExpressionWithLineage1() {
+ PlanChecker.from(connectContext)
+ .checkExplain("SELECT (o.c1_abs + ps.c2_abs) as add_alias,
l.L_LINENUMBER, o.O_ORDERSTATUS "
+ + "FROM "
+ + "lineitem as l "
+ + "LEFT JOIN "
+ + "(SELECT abs(O_TOTALPRICE + 10) as c1_abs,
O_CUSTKEY, O_ORDERSTATUS, O_ORDERKEY "
+ + "FROM orders) as o "
+ + "ON l.L_ORDERKEY = o.O_ORDERKEY "
+ + "JOIN "
+ + "(SELECT abs(sqrt(PS_SUPPLYCOST)) as c2_abs,
PS_AVAILQTY, PS_PARTKEY, PS_SUPPKEY "
+ + "FROM partsupp) as ps "
+ + "ON l.L_PARTKEY = ps.PS_PARTKEY and
l.L_SUPPKEY = ps.PS_SUPPKEY",
+ nereidsPlanner -> {
+ Plan rewrittenPlan =
nereidsPlanner.getRewrittenPlan();
+ List<? extends Expression> originalExpressions =
rewrittenPlan.getExpressions();
+ List<? extends Expression> shuttledExpressions
+ =
ExpressionUtils.shuttleExpressionWithLineage(
+ originalExpressions,
+ rewrittenPlan,
+ Sets.newHashSet(),
+ Sets.newHashSet());
+ assertExpect(originalExpressions,
shuttledExpressions,
+ "(cast(abs((cast(O_TOTALPRICE as
DECIMALV3(16, 2)) + 10.00)) as "
+ + "DOUBLE) +
abs(sqrt(cast(PS_SUPPLYCOST as DOUBLE))))",
+ "L_LINENUMBER",
+ "O_ORDERSTATUS");
+ });
+ }
+
+ private void assertExpect(List<? extends Expression> originalExpressions,
+ List<? extends Expression> shuttledExpressions,
+ String... expectExpressions) {
+ Assertions.assertEquals(originalExpressions.size(),
shuttledExpressions.size());
+ Assertions.assertEquals(originalExpressions.size(),
expectExpressions.length);
+ for (int index = 0; index < shuttledExpressions.size(); index++) {
+ Assertions.assertEquals(shuttledExpressions.get(index).toSql(),
expectExpressions[index]);
+ }
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]