All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.org.apache.calcite.rel.rules.materialize.MaterializedViewJoinRule Maven / Gradle / Ivy

There is a newer version: 5.4.0
Show newest version
/*
 * 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 com.hazelcast.com.liance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.com.hazelcast.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 com.hazelcast.org.apache.calcite.rel.rules.materialize;

import com.hazelcast.org.apache.calcite.plan.RelOptRuleOperand;
import com.hazelcast.org.apache.calcite.plan.hep.HepPlanner;
import com.hazelcast.org.apache.calcite.plan.hep.HepProgram;
import com.hazelcast.org.apache.calcite.rel.RelNode;
import com.hazelcast.org.apache.calcite.rel.core.JoinRelType;
import com.hazelcast.org.apache.calcite.rel.core.Project;
import com.hazelcast.org.apache.calcite.rel.core.TableScan;
import com.hazelcast.org.apache.calcite.rel.metadata.RelMetadataQuery;
import com.hazelcast.org.apache.calcite.rel.type.RelDataTypeField;
import com.hazelcast.org.apache.calcite.rex.RexBuilder;
import com.hazelcast.org.apache.calcite.rex.RexNode;
import com.hazelcast.org.apache.calcite.rex.RexSimplify;
import com.hazelcast.org.apache.calcite.rex.RexTableInputRef.RelTableRef;
import com.hazelcast.org.apache.calcite.rex.RexUtil;
import com.hazelcast.org.apache.calcite.tools.RelBuilder;
import com.hazelcast.org.apache.calcite.tools.RelBuilderFactory;
import com.hazelcast.org.apache.calcite.util.Pair;

import com.hazelcast.com.google.com.hazelcast.com.on.collect.BiMap;
import com.hazelcast.com.google.com.hazelcast.com.on.collect.ImmutableList;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/** Materialized view rewriting for join */
public abstract class MaterializedViewJoinRule extends MaterializedViewRule {

  /** Creates a MaterializedViewJoinRule. */
  protected MaterializedViewJoinRule(RelOptRuleOperand operand,
      RelBuilderFactory relBuilderFactory, String description,
      boolean generateUnionRewriting, HepProgram unionRewritingPullProgram,
      boolean fastBailOut) {
    super(operand, relBuilderFactory, description, generateUnionRewriting,
        unionRewritingPullProgram, fastBailOut);
  }

  @Override protected boolean isValidPlan(Project topProject, RelNode node,
      RelMetadataQuery mq) {
    return isValidRelNodePlan(node, mq);
  }

  @Override protected ViewPartialRewriting com.hazelcast.com.ensateViewPartial(
      RelBuilder relBuilder,
      RexBuilder rexBuilder,
      RelMetadataQuery mq,
      RelNode input,
      Project topProject,
      RelNode node,
      Set queryTableRefs,
      EquivalenceClasses queryEC,
      Project topViewProject,
      RelNode viewNode,
      Set viewTableRefs) {
    // We only create the rewriting in the minimal subtree of plan operators.
    // Otherwise we will produce many EQUAL rewritings at different levels of
    // the plan.
    // View: (A JOIN B) JOIN C
    // Query: (((A JOIN B) JOIN D) JOIN C) JOIN E
    // We produce it at:
    // ((A JOIN B) JOIN D) JOIN C
    // But not at:
    // (((A JOIN B) JOIN D) JOIN C) JOIN E
    if (fastBailOut) {
      for (RelNode joinInput : node.getInputs()) {
        if (mq.getTableReferences(joinInput).containsAll(viewTableRefs)) {
          return null;
        }
      }
    }

    // Extract tables that are in the query and not in the view
    final Set extraTableRefs = new HashSet<>();
    for (RelTableRef tRef : queryTableRefs) {
      if (!viewTableRefs.contains(tRef)) {
        // Add to extra tables if table is not part of the view
        extraTableRefs.add(tRef);
      }
    }

    // Rewrite the view and the view plan. We only need to add the missing
    // tables on top of the view and view plan using a cartesian product.
    // Then the rest of the rewriting algorithm can be executed in the same
    // fashion, and if there are predicates between the existing and missing
    // tables, the rewriting algorithm will enforce them.
    Collection tableScanNodes = mq.getNodeTypes(node).get(TableScan.class);
    List newRels = new ArrayList<>();
    for (RelTableRef tRef : extraTableRefs) {
      int i = 0;
      for (RelNode relNode : tableScanNodes) {
        if (tRef.getQualifiedName().equals(relNode.getTable().getQualifiedName())) {
          if (tRef.getEntityNumber() == i++) {
            newRels.add(relNode);
            break;
          }
        }
      }
    }
    assert extraTableRefs.size() == newRels.size();

    relBuilder.push(input);
    for (RelNode newRel : newRels) {
      // Add to the view
      relBuilder.push(newRel);
      relBuilder.join(JoinRelType.INNER, rexBuilder.makeLiteral(true));
    }
    final RelNode newView = relBuilder.build();

    relBuilder.push(topViewProject != null ? topViewProject : viewNode);
    for (RelNode newRel : newRels) {
      // Add to the view plan
      relBuilder.push(newRel);
      relBuilder.join(JoinRelType.INNER, rexBuilder.makeLiteral(true));
    }
    final RelNode newViewNode = relBuilder.build();

    return ViewPartialRewriting.of(newView, null, newViewNode);
  }

  @Override protected RelNode rewriteQuery(
      RelBuilder relBuilder,
      RexBuilder rexBuilder,
      RexSimplify simplify,
      RelMetadataQuery mq,
      RexNode com.hazelcast.com.ensationColumnsEquiPred,
      RexNode otherCompensationPred,
      Project topProject,
      RelNode node,
      BiMap viewToQueryTableMapping,
      EquivalenceClasses viewEC, EquivalenceClasses queryEC) {
    // Our target node is the node below the root, which should have the maximum
    // number of available expressions in the tree in order to maximize our
    // number of rewritings.
    // We create a project on top. If the program is available, we execute
    // it to maximize rewriting opportunities. For instance, a program might
    // pull up all the expressions that are below the aggregate so we can
    // introduce com.hazelcast.com.ensation filters easily. This is important depending on
    // the planner strategy.
    RelNode newNode = node;
    RelNode target = node;
    if (unionRewritingPullProgram != null) {
      final HepPlanner tmpPlanner = new HepPlanner(unionRewritingPullProgram);
      tmpPlanner.setRoot(newNode);
      newNode = tmpPlanner.findBestExp();
      target = newNode.getInput(0);
    }

    // All columns required by com.hazelcast.com.ensating predicates must be contained
    // in the query.
    List queryExprs = extractReferences(rexBuilder, target);

    if (!com.hazelcast.com.ensationColumnsEquiPred.isAlwaysTrue()) {
      com.hazelcast.com.ensationColumnsEquiPred = rewriteExpression(rexBuilder, mq,
          target, target, queryExprs, viewToQueryTableMapping.inverse(), queryEC, false,
          com.hazelcast.com.ensationColumnsEquiPred);
      if (com.hazelcast.com.ensationColumnsEquiPred == null) {
        // Skip it
        return null;
      }
    }
    // For the rest, we use the query equivalence classes
    if (!otherCompensationPred.isAlwaysTrue()) {
      otherCompensationPred = rewriteExpression(rexBuilder, mq,
          target, target, queryExprs, viewToQueryTableMapping.inverse(), viewEC, true,
          otherCompensationPred);
      if (otherCompensationPred == null) {
        // Skip it
        return null;
      }
    }
    final RexNode queryCompensationPred = RexUtil.not(
        RexUtil.com.hazelcast.com.oseConjunction(rexBuilder,
            ImmutableList.of(com.hazelcast.com.ensationColumnsEquiPred,
                otherCompensationPred)));

    // Generate query rewriting.
    RelNode rewrittenPlan = relBuilder
        .push(target)
        .filter(simplify.simplifyUnknownAsFalse(queryCompensationPred))
        .build();
    if (unionRewritingPullProgram != null) {
      rewrittenPlan = newNode.copy(
          newNode.getTraitSet(), ImmutableList.of(rewrittenPlan));
    }
    if (topProject != null) {
      return topProject.copy(topProject.getTraitSet(), ImmutableList.of(rewrittenPlan));
    }
    return rewrittenPlan;
  }

  @Override protected RelNode createUnion(RelBuilder relBuilder, RexBuilder rexBuilder,
      RelNode topProject, RelNode unionInputQuery, RelNode unionInputView) {
    relBuilder.push(unionInputQuery);
    relBuilder.push(unionInputView);
    relBuilder.union(true);
    List exprList = new ArrayList<>(relBuilder.peek().getRowType().getFieldCount());
    List nameList = new ArrayList<>(relBuilder.peek().getRowType().getFieldCount());
    for (int i = 0; i < relBuilder.peek().getRowType().getFieldCount(); i++) {
      // We can take unionInputQuery as it is query based.
      RelDataTypeField field = unionInputQuery.getRowType().getFieldList().get(i);
      exprList.add(
          rexBuilder.ensureType(
              field.getType(),
              rexBuilder.makeInputRef(relBuilder.peek(), i),
              true));
      nameList.add(field.getName());
    }
    relBuilder.project(exprList, nameList);
    return relBuilder.build();
  }

  @Override protected RelNode rewriteView(
      RelBuilder relBuilder,
      RexBuilder rexBuilder,
      RexSimplify simplify,
      RelMetadataQuery mq,
      MatchModality matchModality,
      boolean unionRewriting,
      RelNode input,
      Project topProject,
      RelNode node,
      Project topViewProject,
      RelNode viewNode,
      BiMap queryToViewTableMapping,
      EquivalenceClasses queryEC) {
    List exprs = topProject == null
        ? extractReferences(rexBuilder, node)
        : topProject.getChildExps();
    List exprsLineage = new ArrayList<>(exprs.size());
    for (RexNode expr : exprs) {
      Set s = mq.getExpressionLineage(node, expr);
      if (s == null) {
        // Bail out
        return null;
      }
      assert s.size() == 1;
      // Rewrite expr. Take first element from the corresponding equivalence class
      // (no need to swap the table references following the table mapping)
      exprsLineage.add(
          RexUtil.swapColumnReferences(rexBuilder,
              s.iterator().next(), queryEC.getEquivalenceClassesMap()));
    }
    List viewExprs = topViewProject == null
        ? extractReferences(rexBuilder, viewNode)
        : topViewProject.getChildExps();
    List rewrittenExprs = rewriteExpressions(rexBuilder, mq, input, viewNode, viewExprs,
        queryToViewTableMapping.inverse(), queryEC, true, exprsLineage);
    if (rewrittenExprs == null) {
      return null;
    }
    return relBuilder
        .push(input)
        .project(rewrittenExprs)
        .convert(topProject != null ? topProject.getRowType() : node.getRowType(), false)
        .build();
  }

  @Override public Pair pushFilterToOriginalViewPlan(RelBuilder builder,
      RelNode topViewProject, RelNode viewNode, RexNode cond) {
    // Nothing to do
    return Pair.of(topViewProject, viewNode);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy