You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jc...@apache.org on 2019/03/25 17:52:29 UTC
[calcite] 02/04: [CALCITE-2943] Materialized view rewriting logic
calls getApplicableMaterializations each time the rule is triggered
This is an automated email from the ASF dual-hosted git repository.
jcamacho pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/calcite.git
commit ecc100ea23bdb5dd1a4c34633c3a376856b64970
Author: Jesus Camacho Rodriguez <jc...@apache.org>
AuthorDate: Thu Mar 21 15:21:35 2019 -0700
[CALCITE-2943] Materialized view rewriting logic calls getApplicableMaterializations each time the rule is triggered
Close apache/calcite#1125
---
.../calcite/plan/volcano/VolcanoPlanner.java | 2 +-
.../rel/rules/AbstractMaterializedViewRule.java | 583 +++++++++++----------
2 files changed, 295 insertions(+), 290 deletions(-)
diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
index cd4af85..6500110 100644
--- a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
+++ b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
@@ -331,7 +331,7 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
return latticeByName.get(table.getQualifiedName());
}
- private void registerMaterializations() {
+ protected void registerMaterializations() {
// Avoid using materializations while populating materializations!
final CalciteConnectionConfig config =
context.unwrap(CalciteConnectionConfig.class);
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
index 706dcca..01bb2e3 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
@@ -18,7 +18,6 @@ package org.apache.calcite.rel.rules;
import org.apache.calcite.avatica.util.TimeUnitRange;
import org.apache.calcite.plan.RelOptMaterialization;
-import org.apache.calcite.plan.RelOptMaterializations;
import org.apache.calcite.plan.RelOptPlanner;
import org.apache.calcite.plan.RelOptPredicateList;
import org.apache.calcite.plan.RelOptRule;
@@ -221,319 +220,325 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
return;
}
- // Obtain applicable (filtered) materializations
- // TODO: Filtering of relevant materializations needs to be
- // improved so we gather only materializations that might
- // actually generate a valid rewriting.
- final List<RelOptMaterialization> applicableMaterializations =
- RelOptMaterializations.getApplicableMaterializations(node, materializations);
+ // 2. Initialize all query related auxiliary data structures
+ // that will be used throughout query rewriting process
+ // Generate query table references
+ final Set<RelTableRef> queryTableRefs = mq.getTableReferences(node);
+ if (queryTableRefs == null) {
+ // Bail out
+ return;
+ }
- if (!applicableMaterializations.isEmpty()) {
- // 2. Initialize all query related auxiliary data structures
- // that will be used throughout query rewriting process
- // Generate query table references
- final Set<RelTableRef> queryTableRefs = mq.getTableReferences(node);
- if (queryTableRefs == null) {
- // Bail out
- return;
+ // Extract query predicates
+ final RelOptPredicateList queryPredicateList =
+ mq.getAllPredicates(node);
+ if (queryPredicateList == null) {
+ // Bail out
+ return;
+ }
+ final RexNode pred =
+ simplify.simplifyUnknownAsFalse(
+ RexUtil.composeConjunction(rexBuilder,
+ queryPredicateList.pulledUpPredicates));
+ final Triple<RexNode, RexNode, RexNode> queryPreds =
+ splitPredicates(rexBuilder, pred);
+
+ // Extract query equivalence classes. An equivalence class is a set
+ // of columns in the query output that are known to be equal.
+ final EquivalenceClasses qEC = new EquivalenceClasses();
+ for (RexNode conj : RelOptUtil.conjunctions(queryPreds.getLeft())) {
+ assert conj.isA(SqlKind.EQUALS);
+ RexCall equiCond = (RexCall) conj;
+ qEC.addEquivalenceClass(
+ (RexTableInputRef) equiCond.getOperands().get(0),
+ (RexTableInputRef) equiCond.getOperands().get(1));
+ }
+
+ // 3. We iterate through all applicable materializations trying to
+ // rewrite the given query
+ for (RelOptMaterialization materialization : materializations) {
+ RelNode view = materialization.tableRel;
+ Project topViewProject;
+ RelNode viewNode;
+ if (materialization.queryRel instanceof Project) {
+ topViewProject = (Project) materialization.queryRel;
+ viewNode = topViewProject.getInput();
+ } else {
+ topViewProject = null;
+ viewNode = materialization.queryRel;
}
- // Extract query predicates
- final RelOptPredicateList queryPredicateList =
- mq.getAllPredicates(node);
- if (queryPredicateList == null) {
- // Bail out
- return;
+ // Extract view table references
+ final Set<RelTableRef> viewTableRefs = mq.getTableReferences(viewNode);
+ if (viewTableRefs == null) {
+ // Skip it
+ continue;
}
- final RexNode pred =
- simplify.simplifyUnknownAsFalse(
- RexUtil.composeConjunction(rexBuilder,
- queryPredicateList.pulledUpPredicates));
- final Triple<RexNode, RexNode, RexNode> queryPreds =
- splitPredicates(rexBuilder, pred);
-
- // Extract query equivalence classes. An equivalence class is a set
- // of columns in the query output that are known to be equal.
- final EquivalenceClasses qEC = new EquivalenceClasses();
- for (RexNode conj : RelOptUtil.conjunctions(queryPreds.getLeft())) {
- assert conj.isA(SqlKind.EQUALS);
- RexCall equiCond = (RexCall) conj;
- qEC.addEquivalenceClass(
- (RexTableInputRef) equiCond.getOperands().get(0),
- (RexTableInputRef) equiCond.getOperands().get(1));
- }
-
- // 3. We iterate through all applicable materializations trying to
- // rewrite the given query
- for (RelOptMaterialization materialization : applicableMaterializations) {
- RelNode view = materialization.tableRel;
- Project topViewProject;
- RelNode viewNode;
- if (materialization.queryRel instanceof Project) {
- topViewProject = (Project) materialization.queryRel;
- viewNode = topViewProject.getInput();
- } else {
- topViewProject = null;
- viewNode = materialization.queryRel;
- }
- // 3.1. View checks before proceeding
- if (!isValidPlan(topViewProject, viewNode, mq)) {
- // Skip it
- continue;
+ // Filter relevant materializations. Currently, we only check whether
+ // the materialization contains any table that is used by the query
+ // TODO: Filtering of relevant materializations can be improved to be more fine-grained.
+ boolean applicable = false;
+ for (RelTableRef tableRef : viewTableRefs) {
+ if (queryTableRefs.contains(tableRef)) {
+ applicable = true;
+ break;
}
+ }
+ if (!applicable) {
+ // Skip it
+ continue;
+ }
- // 3.2. Initialize all query related auxiliary data structures
- // that will be used throughout query rewriting process
- // Extract view predicates
- final RelOptPredicateList viewPredicateList =
- mq.getAllPredicates(viewNode);
- if (viewPredicateList == null) {
- // Skip it
- continue;
- }
- final RexNode viewPred = simplify.simplifyUnknownAsFalse(
- RexUtil.composeConjunction(rexBuilder,
- viewPredicateList.pulledUpPredicates));
- final Triple<RexNode, RexNode, RexNode> viewPreds =
- splitPredicates(rexBuilder, viewPred);
+ // 3.1. View checks before proceeding
+ if (!isValidPlan(topViewProject, viewNode, mq)) {
+ // Skip it
+ continue;
+ }
- // Extract view table references
- final Set<RelTableRef> viewTableRefs = mq.getTableReferences(viewNode);
- if (viewTableRefs == null) {
+ // 3.2. Initialize all query related auxiliary data structures
+ // that will be used throughout query rewriting process
+ // Extract view predicates
+ final RelOptPredicateList viewPredicateList =
+ mq.getAllPredicates(viewNode);
+ if (viewPredicateList == null) {
+ // Skip it
+ continue;
+ }
+ final RexNode viewPred = simplify.simplifyUnknownAsFalse(
+ RexUtil.composeConjunction(rexBuilder,
+ viewPredicateList.pulledUpPredicates));
+ final Triple<RexNode, RexNode, RexNode> viewPreds =
+ splitPredicates(rexBuilder, viewPred);
+
+ // Extract view tables
+ MatchModality matchModality;
+ Multimap<RexTableInputRef, RexTableInputRef> compensationEquiColumns =
+ ArrayListMultimap.create();
+ if (!queryTableRefs.equals(viewTableRefs)) {
+ // We try to compensate, e.g., for join queries it might be
+ // possible to join missing tables with view to compute result.
+ // Two supported cases: query tables are subset of view tables (we need to
+ // check whether they are cardinality-preserving joins), or view tables are
+ // subset of query tables (add additional tables through joins if possible)
+ if (viewTableRefs.containsAll(queryTableRefs)) {
+ matchModality = MatchModality.QUERY_PARTIAL;
+ final EquivalenceClasses vEC = new EquivalenceClasses();
+ for (RexNode conj : RelOptUtil.conjunctions(viewPreds.getLeft())) {
+ assert conj.isA(SqlKind.EQUALS);
+ RexCall equiCond = (RexCall) conj;
+ vEC.addEquivalenceClass(
+ (RexTableInputRef) equiCond.getOperands().get(0),
+ (RexTableInputRef) equiCond.getOperands().get(1));
+ }
+ if (!compensatePartial(viewTableRefs, vEC, queryTableRefs,
+ compensationEquiColumns)) {
+ // Cannot rewrite, skip it
+ continue;
+ }
+ } else if (queryTableRefs.containsAll(viewTableRefs)) {
+ matchModality = MatchModality.VIEW_PARTIAL;
+ ViewPartialRewriting partialRewritingResult = compensateViewPartial(
+ call.builder(), rexBuilder, mq, view,
+ topProject, node, queryTableRefs, qEC,
+ topViewProject, viewNode, viewTableRefs);
+ if (partialRewritingResult == null) {
+ // Cannot rewrite, skip it
+ continue;
+ }
+ // Rewrite succeeded
+ view = partialRewritingResult.newView;
+ topViewProject = partialRewritingResult.newTopViewProject;
+ viewNode = partialRewritingResult.newViewNode;
+ } else {
// Skip it
continue;
}
+ } else {
+ matchModality = MatchModality.COMPLETE;
+ }
- // Extract view tables
- MatchModality matchModality;
- Multimap<RexTableInputRef, RexTableInputRef> compensationEquiColumns =
- ArrayListMultimap.create();
- if (!queryTableRefs.equals(viewTableRefs)) {
- // We try to compensate, e.g., for join queries it might be
- // possible to join missing tables with view to compute result.
- // Two supported cases: query tables are subset of view tables (we need to
- // check whether they are cardinality-preserving joins), or view tables are
- // subset of query tables (add additional tables through joins if possible)
- if (viewTableRefs.containsAll(queryTableRefs)) {
- matchModality = MatchModality.QUERY_PARTIAL;
- final EquivalenceClasses vEC = new EquivalenceClasses();
- for (RexNode conj : RelOptUtil.conjunctions(viewPreds.getLeft())) {
- assert conj.isA(SqlKind.EQUALS);
- RexCall equiCond = (RexCall) conj;
- vEC.addEquivalenceClass(
- (RexTableInputRef) equiCond.getOperands().get(0),
- (RexTableInputRef) equiCond.getOperands().get(1));
- }
- if (!compensatePartial(viewTableRefs, vEC, queryTableRefs,
- compensationEquiColumns)) {
- // Cannot rewrite, skip it
- continue;
- }
- } else if (queryTableRefs.containsAll(viewTableRefs)) {
- matchModality = MatchModality.VIEW_PARTIAL;
- ViewPartialRewriting partialRewritingResult = compensateViewPartial(
- call.builder(), rexBuilder, mq, view,
- topProject, node, queryTableRefs, qEC,
- topViewProject, viewNode, viewTableRefs);
- if (partialRewritingResult == null) {
- // Cannot rewrite, skip it
- continue;
- }
- // Rewrite succeeded
- view = partialRewritingResult.newView;
- topViewProject = partialRewritingResult.newTopViewProject;
- viewNode = partialRewritingResult.newViewNode;
- } else {
- // Skip it
- continue;
+ // 4. We map every table in the query to a table with the same qualified
+ // name (all query tables are contained in the view, thus this is equivalent
+ // to mapping every table in the query to a view table).
+ final Multimap<RelTableRef, RelTableRef> multiMapTables = ArrayListMultimap.create();
+ for (RelTableRef queryTableRef1 : queryTableRefs) {
+ for (RelTableRef queryTableRef2 : queryTableRefs) {
+ if (queryTableRef1.getQualifiedName().equals(
+ queryTableRef2.getQualifiedName())) {
+ multiMapTables.put(queryTableRef1, queryTableRef2);
}
- } else {
- matchModality = MatchModality.COMPLETE;
}
+ }
- // 4. We map every table in the query to a table with the same qualified
- // name (all query tables are contained in the view, thus this is equivalent
- // to mapping every table in the query to a view table).
- final Multimap<RelTableRef, RelTableRef> multiMapTables = ArrayListMultimap.create();
- for (RelTableRef queryTableRef1 : queryTableRefs) {
- for (RelTableRef queryTableRef2 : queryTableRefs) {
- if (queryTableRef1.getQualifiedName().equals(
- queryTableRef2.getQualifiedName())) {
- multiMapTables.put(queryTableRef1, queryTableRef2);
- }
+ // If a table is used multiple times, we will create multiple mappings,
+ // and we will try to rewrite the query using each of the mappings.
+ // Then, we will try to map every source table (query) to a target
+ // table (view), and if we are successful, we will try to create
+ // compensation predicates to filter the view results further
+ // (if needed).
+ final List<BiMap<RelTableRef, RelTableRef>> flatListMappings =
+ generateTableMappings(multiMapTables);
+ for (BiMap<RelTableRef, RelTableRef> queryToViewTableMapping : flatListMappings) {
+ // TableMapping : mapping query tables -> view tables
+ // 4.0. If compensation equivalence classes exist, we need to add
+ // the mapping to the query mapping
+ final EquivalenceClasses currQEC = EquivalenceClasses.copy(qEC);
+ if (matchModality == MatchModality.QUERY_PARTIAL) {
+ for (Entry<RexTableInputRef, RexTableInputRef> e
+ : compensationEquiColumns.entries()) {
+ // Copy origin
+ RelTableRef queryTableRef = queryToViewTableMapping.inverse().get(
+ e.getKey().getTableRef());
+ RexTableInputRef queryColumnRef = RexTableInputRef.of(queryTableRef,
+ e.getKey().getIndex(), e.getKey().getType());
+ // Add to query equivalence classes and table mapping
+ currQEC.addEquivalenceClass(queryColumnRef, e.getValue());
+ queryToViewTableMapping.put(e.getValue().getTableRef(),
+ e.getValue().getTableRef()); // identity
}
}
- // If a table is used multiple times, we will create multiple mappings,
- // and we will try to rewrite the query using each of the mappings.
- // Then, we will try to map every source table (query) to a target
- // table (view), and if we are successful, we will try to create
- // compensation predicates to filter the view results further
- // (if needed).
- final List<BiMap<RelTableRef, RelTableRef>> flatListMappings =
- generateTableMappings(multiMapTables);
- for (BiMap<RelTableRef, RelTableRef> queryToViewTableMapping : flatListMappings) {
- // TableMapping : mapping query tables -> view tables
- // 4.0. If compensation equivalence classes exist, we need to add
- // the mapping to the query mapping
- final EquivalenceClasses currQEC = EquivalenceClasses.copy(qEC);
- if (matchModality == MatchModality.QUERY_PARTIAL) {
- for (Entry<RexTableInputRef, RexTableInputRef> e
- : compensationEquiColumns.entries()) {
- // Copy origin
- RelTableRef queryTableRef = queryToViewTableMapping.inverse().get(
- e.getKey().getTableRef());
- RexTableInputRef queryColumnRef = RexTableInputRef.of(queryTableRef,
- e.getKey().getIndex(), e.getKey().getType());
- // Add to query equivalence classes and table mapping
- currQEC.addEquivalenceClass(queryColumnRef, e.getValue());
- queryToViewTableMapping.put(e.getValue().getTableRef(),
- e.getValue().getTableRef()); // identity
- }
+ // 4.1. Compute compensation predicates, i.e., predicates that need to be
+ // enforced over the view to retain query semantics. The resulting predicates
+ // are expressed using {@link RexTableInputRef} over the query.
+ // First, to establish relationship, we swap column references of the view
+ // predicates to point to query tables and compute equivalence classes.
+ final RexNode viewColumnsEquiPred = RexUtil.swapTableReferences(
+ rexBuilder, viewPreds.getLeft(), queryToViewTableMapping.inverse());
+ final EquivalenceClasses queryBasedVEC = new EquivalenceClasses();
+ for (RexNode conj : RelOptUtil.conjunctions(viewColumnsEquiPred)) {
+ assert conj.isA(SqlKind.EQUALS);
+ RexCall equiCond = (RexCall) conj;
+ queryBasedVEC.addEquivalenceClass(
+ (RexTableInputRef) equiCond.getOperands().get(0),
+ (RexTableInputRef) equiCond.getOperands().get(1));
+ }
+ Triple<RexNode, RexNode, RexNode> compensationPreds =
+ computeCompensationPredicates(rexBuilder, simplify,
+ currQEC, queryPreds, queryBasedVEC, viewPreds,
+ queryToViewTableMapping);
+ if (compensationPreds == null && generateUnionRewriting) {
+ // Attempt partial rewriting using union operator. This rewriting
+ // will read some data from the view and the rest of the data from
+ // the query computation. The resulting predicates are expressed
+ // using {@link RexTableInputRef} over the view.
+ compensationPreds = computeCompensationPredicates(rexBuilder, simplify,
+ queryBasedVEC, viewPreds, currQEC, queryPreds,
+ queryToViewTableMapping.inverse());
+ if (compensationPreds == null) {
+ // This was our last chance to use the view, skip it
+ continue;
}
-
- // 4.1. Compute compensation predicates, i.e., predicates that need to be
- // enforced over the view to retain query semantics. The resulting predicates
- // are expressed using {@link RexTableInputRef} over the query.
- // First, to establish relationship, we swap column references of the view
- // predicates to point to query tables and compute equivalence classes.
- final RexNode viewColumnsEquiPred = RexUtil.swapTableReferences(
- rexBuilder, viewPreds.getLeft(), queryToViewTableMapping.inverse());
- final EquivalenceClasses queryBasedVEC = new EquivalenceClasses();
- for (RexNode conj : RelOptUtil.conjunctions(viewColumnsEquiPred)) {
- assert conj.isA(SqlKind.EQUALS);
- RexCall equiCond = (RexCall) conj;
- queryBasedVEC.addEquivalenceClass(
- (RexTableInputRef) equiCond.getOperands().get(0),
- (RexTableInputRef) equiCond.getOperands().get(1));
+ RexNode compensationColumnsEquiPred = compensationPreds.getLeft();
+ RexNode otherCompensationPred =
+ RexUtil.composeConjunction(rexBuilder,
+ ImmutableList.of(compensationPreds.getMiddle(),
+ compensationPreds.getRight()));
+ assert !compensationColumnsEquiPred.isAlwaysTrue()
+ || !otherCompensationPred.isAlwaysTrue();
+
+ // b. Generate union branch (query).
+ final RelNode unionInputQuery = rewriteQuery(call.builder(), rexBuilder,
+ simplify, mq, compensationColumnsEquiPred, otherCompensationPred,
+ topProject, node, queryToViewTableMapping, queryBasedVEC, currQEC);
+ if (unionInputQuery == null) {
+ // Skip it
+ continue;
}
- Triple<RexNode, RexNode, RexNode> compensationPreds =
- computeCompensationPredicates(rexBuilder, simplify,
- currQEC, queryPreds, queryBasedVEC, viewPreds,
- queryToViewTableMapping);
- if (compensationPreds == null && generateUnionRewriting) {
- // Attempt partial rewriting using union operator. This rewriting
- // will read some data from the view and the rest of the data from
- // the query computation. The resulting predicates are expressed
- // using {@link RexTableInputRef} over the view.
- compensationPreds = computeCompensationPredicates(rexBuilder, simplify,
- queryBasedVEC, viewPreds, currQEC, queryPreds,
- queryToViewTableMapping.inverse());
- if (compensationPreds == null) {
- // This was our last chance to use the view, skip it
- continue;
- }
- RexNode compensationColumnsEquiPred = compensationPreds.getLeft();
- RexNode otherCompensationPred =
- RexUtil.composeConjunction(rexBuilder,
- ImmutableList.of(compensationPreds.getMiddle(),
- compensationPreds.getRight()));
- assert !compensationColumnsEquiPred.isAlwaysTrue()
- || !otherCompensationPred.isAlwaysTrue();
-
- // b. Generate union branch (query).
- final RelNode unionInputQuery = rewriteQuery(call.builder(), rexBuilder,
- simplify, mq, compensationColumnsEquiPred, otherCompensationPred,
- topProject, node, queryToViewTableMapping, queryBasedVEC, currQEC);
- if (unionInputQuery == null) {
- // Skip it
- continue;
- }
- // c. Generate union branch (view).
- // We trigger the unifying method. This method will either create a Project
- // or an Aggregate operator on top of the view. It will also compute the
- // output expressions for the query.
- final RelNode unionInputView = rewriteView(call.builder(), rexBuilder, simplify, mq,
- matchModality, true, view, topProject, node, topViewProject, viewNode,
- queryToViewTableMapping, currQEC);
- if (unionInputView == null) {
- // Skip it
- continue;
- }
+ // c. Generate union branch (view).
+ // We trigger the unifying method. This method will either create a Project
+ // or an Aggregate operator on top of the view. It will also compute the
+ // output expressions for the query.
+ final RelNode unionInputView = rewriteView(call.builder(), rexBuilder, simplify, mq,
+ matchModality, true, view, topProject, node, topViewProject, viewNode,
+ queryToViewTableMapping, currQEC);
+ if (unionInputView == null) {
+ // Skip it
+ continue;
+ }
- // d. Generate final rewriting (union).
- final RelNode result = createUnion(call.builder(), rexBuilder,
- topProject, unionInputQuery, unionInputView);
- if (result == null) {
- // Skip it
- continue;
- }
- call.transformTo(result);
- } else if (compensationPreds != null) {
- RexNode compensationColumnsEquiPred = compensationPreds.getLeft();
- RexNode otherCompensationPred =
- RexUtil.composeConjunction(rexBuilder,
- ImmutableList.of(compensationPreds.getMiddle(),
- compensationPreds.getRight()));
-
- // a. Compute final compensation predicate.
- if (!compensationColumnsEquiPred.isAlwaysTrue()
- || !otherCompensationPred.isAlwaysTrue()) {
- // All columns required by compensating predicates must be contained
- // in the view output (condition 2).
- List<RexNode> viewExprs = topViewProject == null
- ? extractReferences(rexBuilder, view)
- : topViewProject.getChildExps();
- // For compensationColumnsEquiPred, we use the view equivalence classes,
- // since we want to enforce the rest
- if (!compensationColumnsEquiPred.isAlwaysTrue()) {
- compensationColumnsEquiPred = rewriteExpression(rexBuilder, mq,
- view, viewNode, viewExprs, queryToViewTableMapping.inverse(), queryBasedVEC,
- false, compensationColumnsEquiPred);
- if (compensationColumnsEquiPred == null) {
- // Skip it
- continue;
- }
- }
- // For the rest, we use the query equivalence classes
- if (!otherCompensationPred.isAlwaysTrue()) {
- otherCompensationPred = rewriteExpression(rexBuilder, mq,
- view, viewNode, viewExprs, queryToViewTableMapping.inverse(), currQEC,
- true, otherCompensationPred);
- if (otherCompensationPred == null) {
- // Skip it
- continue;
- }
+ // d. Generate final rewriting (union).
+ final RelNode result = createUnion(call.builder(), rexBuilder,
+ topProject, unionInputQuery, unionInputView);
+ if (result == null) {
+ // Skip it
+ continue;
+ }
+ call.transformTo(result);
+ } else if (compensationPreds != null) {
+ RexNode compensationColumnsEquiPred = compensationPreds.getLeft();
+ RexNode otherCompensationPred =
+ RexUtil.composeConjunction(rexBuilder,
+ ImmutableList.of(compensationPreds.getMiddle(),
+ compensationPreds.getRight()));
+
+ // a. Compute final compensation predicate.
+ if (!compensationColumnsEquiPred.isAlwaysTrue()
+ || !otherCompensationPred.isAlwaysTrue()) {
+ // All columns required by compensating predicates must be contained
+ // in the view output (condition 2).
+ List<RexNode> viewExprs = topViewProject == null
+ ? extractReferences(rexBuilder, view)
+ : topViewProject.getChildExps();
+ // For compensationColumnsEquiPred, we use the view equivalence classes,
+ // since we want to enforce the rest
+ if (!compensationColumnsEquiPred.isAlwaysTrue()) {
+ compensationColumnsEquiPred = rewriteExpression(rexBuilder, mq,
+ view, viewNode, viewExprs, queryToViewTableMapping.inverse(), queryBasedVEC,
+ false, compensationColumnsEquiPred);
+ if (compensationColumnsEquiPred == null) {
+ // Skip it
+ continue;
}
}
- final RexNode viewCompensationPred =
- RexUtil.composeConjunction(rexBuilder,
- ImmutableList.of(compensationColumnsEquiPred,
- otherCompensationPred));
-
- // b. Generate final rewriting if possible.
- // First, we add the compensation predicate (if any) on top of the view.
- // Then, we trigger the unifying method. This method will either create a
- // Project or an Aggregate operator on top of the view. It will also compute
- // the output expressions for the query.
- RelBuilder builder = call.builder();
- RelNode viewWithFilter;
- if (!viewCompensationPred.isAlwaysTrue()) {
- RexNode newPred =
- simplify.simplifyUnknownAsFalse(viewCompensationPred);
- viewWithFilter = builder.push(view).filter(newPred).build();
- // We add (and push) the filter to the view plan before triggering the rewriting.
- // This is useful in case some of the columns can be folded to same value after
- // filter is added.
- Pair<RelNode, RelNode> pushedNodes =
- pushFilterToOriginalViewPlan(builder, topViewProject, viewNode, newPred);
- topViewProject = (Project) pushedNodes.left;
- viewNode = pushedNodes.right;
- } else {
- viewWithFilter = builder.push(view).build();
- }
- final RelNode result = rewriteView(builder, rexBuilder, simplify, mq, matchModality,
- false, viewWithFilter, topProject, node, topViewProject, viewNode,
- queryToViewTableMapping, currQEC);
- if (result == null) {
- // Skip it
- continue;
+ // For the rest, we use the query equivalence classes
+ if (!otherCompensationPred.isAlwaysTrue()) {
+ otherCompensationPred = rewriteExpression(rexBuilder, mq,
+ view, viewNode, viewExprs, queryToViewTableMapping.inverse(), currQEC,
+ true, otherCompensationPred);
+ if (otherCompensationPred == null) {
+ // Skip it
+ continue;
+ }
}
- call.transformTo(result);
- } // end else
- }
+ }
+ final RexNode viewCompensationPred =
+ RexUtil.composeConjunction(rexBuilder,
+ ImmutableList.of(compensationColumnsEquiPred,
+ otherCompensationPred));
+
+ // b. Generate final rewriting if possible.
+ // First, we add the compensation predicate (if any) on top of the view.
+ // Then, we trigger the unifying method. This method will either create a
+ // Project or an Aggregate operator on top of the view. It will also compute
+ // the output expressions for the query.
+ RelBuilder builder = call.builder();
+ RelNode viewWithFilter;
+ if (!viewCompensationPred.isAlwaysTrue()) {
+ RexNode newPred =
+ simplify.simplifyUnknownAsFalse(viewCompensationPred);
+ viewWithFilter = builder.push(view).filter(newPred).build();
+ // We add (and push) the filter to the view plan before triggering the rewriting.
+ // This is useful in case some of the columns can be folded to same value after
+ // filter is added.
+ Pair<RelNode, RelNode> pushedNodes =
+ pushFilterToOriginalViewPlan(builder, topViewProject, viewNode, newPred);
+ topViewProject = (Project) pushedNodes.left;
+ viewNode = pushedNodes.right;
+ } else {
+ viewWithFilter = builder.push(view).build();
+ }
+ final RelNode result = rewriteView(builder, rexBuilder, simplify, mq, matchModality,
+ false, viewWithFilter, topProject, node, topViewProject, viewNode,
+ queryToViewTableMapping, currQEC);
+ if (result == null) {
+ // Skip it
+ continue;
+ }
+ call.transformTo(result);
+ } // end else
}
}
}