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

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

There is a newer version: 5.5.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 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 com.hazelcast.org.apache.calcite.rel.rules;

import com.hazelcast.org.apache.calcite.plan.RelOptCluster;
import com.hazelcast.org.apache.calcite.plan.RelOptPlanner;
import com.hazelcast.org.apache.calcite.plan.RelTraitSet;
import com.hazelcast.org.apache.calcite.rel.RelNode;
import com.hazelcast.org.apache.calcite.rel.core.Calc;
import com.hazelcast.org.apache.calcite.rel.logical.LogicalCalc;
import com.hazelcast.org.apache.calcite.rel.type.RelDataType;
import com.hazelcast.org.apache.calcite.rel.type.RelDataTypeFactory;
import com.hazelcast.org.apache.calcite.rex.RexCall;
import com.hazelcast.org.apache.calcite.rex.RexDynamicParam;
import com.hazelcast.org.apache.calcite.rex.RexFieldAccess;
import com.hazelcast.org.apache.calcite.rex.RexInputRef;
import com.hazelcast.org.apache.calcite.rex.RexLiteral;
import com.hazelcast.org.apache.calcite.rex.RexLocalRef;
import com.hazelcast.org.apache.calcite.rex.RexNode;
import com.hazelcast.org.apache.calcite.rex.RexProgram;
import com.hazelcast.org.apache.calcite.rex.RexShuttle;
import com.hazelcast.org.apache.calcite.rex.RexUtil;
import com.hazelcast.org.apache.calcite.rex.RexVisitorImpl;
import com.hazelcast.org.apache.calcite.tools.RelBuilder;
import com.hazelcast.org.apache.calcite.util.Litmus;
import com.hazelcast.org.apache.calcite.util.Util;
import com.hazelcast.org.apache.calcite.util.graph.DefaultDirectedGraph;
import com.hazelcast.org.apache.calcite.util.graph.DefaultEdge;
import com.hazelcast.org.apache.calcite.util.graph.DirectedGraph;
import com.hazelcast.org.apache.calcite.util.graph.TopologicalOrderIterator;

import com.hazelcast.com.google.common.base.Preconditions;
import com.hazelcast.com.google.common.primitives.Ints;

import com.hazelcast.org.checkerframework.checker.nullness.qual.Nullable;
import com.hazelcast.org.slf4j.Logger;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;

/**
 * CalcRelSplitter operates on a
 * {@link com.hazelcast.org.apache.calcite.rel.core.Calc} with multiple {@link RexCall}
 * sub-expressions that cannot all be implemented by a single concrete
 * {@link RelNode}.
 *
 * 

For example, the Java and Fennel calculator do not implement an identical * set of operators. The Calc can be used to split a single Calc with * mixed Java- and Fennel-only operators into a tree of Calc object that can * each be individually implemented by either Java or Fennel.and splits it into * several Calc instances. * *

Currently the splitter is only capable of handling two "rel types". That * is, it can deal with Java vs. Fennel Calcs, but not Java vs. Fennel vs. * some other type of Calc. * *

See {@link ProjectToWindowRule} * for an example of how this class is used. */ public abstract class CalcRelSplitter { //~ Static fields/initializers --------------------------------------------- private static final Logger RULE_LOGGER = RelOptPlanner.LOGGER; //~ Instance fields -------------------------------------------------------- protected final RexProgram program; private final RelDataTypeFactory typeFactory; private final RelType[] relTypes; private final RelOptCluster cluster; private final RelTraitSet traits; private final RelNode child; protected final RelBuilder relBuilder; //~ Constructors ----------------------------------------------------------- /** * Constructs a CalcRelSplitter. * * @param calc Calc to split * @param relTypes Array of rel types, e.g. {Java, Fennel}. Must be * distinct. */ CalcRelSplitter(Calc calc, RelBuilder relBuilder, RelType[] relTypes) { this.relBuilder = relBuilder; for (int i = 0; i < relTypes.length; i++) { assert relTypes[i] != null; for (int j = 0; j < i; j++) { assert relTypes[i] != relTypes[j] : "Rel types must be distinct"; } } this.program = calc.getProgram(); this.cluster = calc.getCluster(); this.traits = calc.getTraitSet(); this.typeFactory = calc.getCluster().getTypeFactory(); this.child = calc.getInput(); this.relTypes = relTypes; } //~ Methods ---------------------------------------------------------------- RelNode execute() { // Check that program is valid. In particular, this means that every // expression is trivial (either an atom, or a function applied to // references to atoms) and every expression depends only on // expressions to the left. assert program.isValid(Litmus.THROW, null); final List exprList = program.getExprList(); final RexNode[] exprs = exprList.toArray(new RexNode[0]); assert !RexUtil.containComplexExprs(exprList); // Figure out what level each expression belongs to. int[] exprLevels = new int[exprs.length]; // The type of a level is given by // relTypes[levelTypeOrdinals[level]]. int[] levelTypeOrdinals = new int[exprs.length]; int levelCount = chooseLevels(exprs, -1, exprLevels, levelTypeOrdinals); // For each expression, figure out which is the highest level where it // is used. int[] exprMaxUsingLevelOrdinals = new HighestUsageFinder(exprs, exprLevels) .getMaxUsingLevelOrdinals(); // If expressions are used as outputs, mark them as higher than that. final List projectRefList = program.getProjectList(); final RexLocalRef conditionRef = program.getCondition(); for (RexLocalRef projectRef : projectRefList) { exprMaxUsingLevelOrdinals[projectRef.getIndex()] = levelCount; } if (conditionRef != null) { exprMaxUsingLevelOrdinals[conditionRef.getIndex()] = levelCount; } // Print out what we've got. if (RULE_LOGGER.isTraceEnabled()) { traceLevelExpressions( exprs, exprLevels, levelTypeOrdinals, levelCount); } // Now build the calcs. RelNode rel = child; final int inputFieldCount = program.getInputRowType().getFieldCount(); int[] inputExprOrdinals = identityArray(inputFieldCount); boolean doneCondition = false; for (int level = 0; level < levelCount; level++) { final int[] projectExprOrdinals; final RelDataType outputRowType; if (level == (levelCount - 1)) { outputRowType = program.getOutputRowType(); projectExprOrdinals = new int[projectRefList.size()]; for (int i = 0; i < projectExprOrdinals.length; i++) { projectExprOrdinals[i] = projectRefList.get(i).getIndex(); } } else { outputRowType = null; // Project the expressions which are computed at this level or // before, and will be used at later levels. List projectExprOrdinalList = new ArrayList<>(); for (int i = 0; i < exprs.length; i++) { RexNode expr = exprs[i]; if (expr instanceof RexLiteral) { // Don't project literals. They are always created in // the level where they are used. exprLevels[i] = -1; continue; } if ((exprLevels[i] <= level) && (exprMaxUsingLevelOrdinals[i] > level)) { projectExprOrdinalList.add(i); } } projectExprOrdinals = Ints.toArray(projectExprOrdinalList); } final RelType relType = relTypes[levelTypeOrdinals[level]]; // Can we do the condition this level? int conditionExprOrdinal = -1; if ((conditionRef != null) && !doneCondition) { conditionExprOrdinal = conditionRef.getIndex(); if ((exprLevels[conditionExprOrdinal] > level) || !relType.supportsCondition()) { // stand down -- we're not ready to do the condition yet conditionExprOrdinal = -1; } else { doneCondition = true; } } RexProgram program1 = createProgramForLevel( level, levelCount, rel.getRowType(), exprs, exprLevels, inputExprOrdinals, projectExprOrdinals, conditionExprOrdinal, outputRowType); rel = relType.makeRel(cluster, traits, relBuilder, rel, program1); // Sometimes a level's program merely projects its inputs. We don't // want these. They cause an explosion in the search space. if (rel instanceof LogicalCalc && ((LogicalCalc) rel).getProgram().isTrivial()) { rel = rel.getInput(0); } rel = handle(rel); // The outputs of this level will be the inputs to the next level. inputExprOrdinals = projectExprOrdinals; } Preconditions.checkArgument(doneCondition || (conditionRef == null), "unhandled condition"); return rel; } /** * Opportunity to further refine the relational expression created for a * given level. The default implementation returns the relational expression * unchanged. */ protected RelNode handle(RelNode rel) { return rel; } /** * Figures out which expressions to calculate at which level. * * @param exprs Array of expressions * @param conditionOrdinal Ordinal of the condition expression, or -1 if no * condition * @param exprLevels Level ordinal for each expression (output) * @param levelTypeOrdinals The type of each level (output) * @return Number of levels required */ private int chooseLevels( final RexNode[] exprs, int conditionOrdinal, int[] exprLevels, int[] levelTypeOrdinals) { final int inputFieldCount = program.getInputRowType().getFieldCount(); int levelCount = 0; final MaxInputFinder maxInputFinder = new MaxInputFinder(exprLevels); boolean[] relTypesPossibleForTopLevel = new boolean[relTypes.length]; Arrays.fill(relTypesPossibleForTopLevel, true); // Compute the order in which to visit expressions. final List> cohorts = getCohorts(); final List permutation = computeTopologicalOrdering(exprs, cohorts); for (int i : permutation) { RexNode expr = exprs[i]; final boolean condition = i == conditionOrdinal; if (i < inputFieldCount) { assert expr instanceof RexInputRef; exprLevels[i] = -1; continue; } // Deduce the minimum level of the expression. An expression must // be at a level greater than or equal to all of its inputs. int level = maxInputFinder.maxInputFor(expr); // If the expression is in a cohort, it can occur no lower than the // levels of other expressions in the same cohort. Set cohort = findCohort(cohorts, i); if (cohort != null) { for (Integer exprOrdinal : cohort) { if (exprOrdinal == i) { // Already did this member of the cohort. It's a waste // of effort to repeat. continue; } final RexNode cohortExpr = exprs[exprOrdinal]; int cohortLevel = maxInputFinder.maxInputFor(cohortExpr); if (cohortLevel > level) { level = cohortLevel; } } } // Try to implement this expression at this level. // If that is not possible, try to implement it at higher levels. levelLoop: for (;; ++level) { if (level >= levelCount) { // This is a new level. We can use any type we like. for (int relTypeOrdinal = 0; relTypeOrdinal < relTypes.length; relTypeOrdinal++) { if (!relTypesPossibleForTopLevel[relTypeOrdinal]) { continue; } if (relTypes[relTypeOrdinal].canImplement( expr, condition)) { // Success. We have found a type where we can // implement this expression. exprLevels[i] = level; levelTypeOrdinals[level] = relTypeOrdinal; assert (level == 0) || (levelTypeOrdinals[level - 1] != levelTypeOrdinals[level]) : "successive levels of same type"; // Figure out which of the other reltypes are // still possible for this level. // Previous reltypes are not possible. for (int j = 0; j < relTypeOrdinal; ++j) { relTypesPossibleForTopLevel[j] = false; } // Successive reltypes may be possible. for (int j = relTypeOrdinal + 1; j < relTypes.length; ++j) { if (relTypesPossibleForTopLevel[j]) { relTypesPossibleForTopLevel[j] = relTypes[j].canImplement( expr, condition); } } // Move to next level. levelTypeOrdinals[levelCount] = firstSet(relTypesPossibleForTopLevel); ++levelCount; Arrays.fill(relTypesPossibleForTopLevel, true); break levelLoop; } } // None of the reltypes still active for this level could // implement expr. But maybe we could succeed with a new // level, with all options open? if (count(relTypesPossibleForTopLevel) >= relTypes.length) { // Cannot implement for any type. throw new AssertionError("cannot implement " + expr); } levelTypeOrdinals[levelCount] = firstSet(relTypesPossibleForTopLevel); ++levelCount; Arrays.fill(relTypesPossibleForTopLevel, true); } else { final int levelTypeOrdinal = levelTypeOrdinals[level]; if (!relTypes[levelTypeOrdinal].canImplement( expr, condition)) { // Cannot implement this expression in this type; // continue to next level. continue; } exprLevels[i] = level; break; } } } if (levelCount > 0) { // The latest level should be CalcRelType otherwise literals cannot be // implemented. assert "CalcRelType".equals(relTypes[0].name) : "The first RelType should be CalcRelType for proper RexLiteral" + " implementation at the last level, got " + relTypes[0].name; if (levelTypeOrdinals[levelCount - 1] != 0) { levelCount++; } } return levelCount; } /** * Computes the order in which to visit expressions, so that we decide the * level of an expression only after the levels of lower expressions have * been decided. * *

First, we need to ensure that an expression is visited after all of * its inputs. * *

Further, if the expression is a member of a cohort, we need to visit * it after the inputs of all other expressions in that cohort. With this * condition, expressions in the same cohort will very likely end up in the * same level. * *

Note that if there are no cohorts, the expressions from the * {@link RexProgram} are already in a suitable order. We perform the * topological sort just to ensure that the code path is well-trodden. * * @param exprs Expressions * @param cohorts List of cohorts, each of which is a set of expr ordinals * @return Expression ordinals in topological order */ private static List computeTopologicalOrdering( RexNode[] exprs, List> cohorts) { final DirectedGraph graph = DefaultDirectedGraph.create(); for (int i = 0; i < exprs.length; i++) { graph.addVertex(i); } for (int i = 0; i < exprs.length; i++) { final RexNode expr = exprs[i]; final Set cohort = findCohort(cohorts, i); final Set targets; if (cohort == null) { targets = Collections.singleton(i); } else { targets = cohort; } expr.accept( new RexVisitorImpl(true) { @Override public Void visitLocalRef(RexLocalRef localRef) { for (Integer target : targets) { graph.addEdge(localRef.getIndex(), target); } return null; } }); } TopologicalOrderIterator iter = new TopologicalOrderIterator<>(graph); final List permutation = new ArrayList<>(); while (iter.hasNext()) { permutation.add(iter.next()); } return permutation; } /** * Finds the cohort that contains the given integer, or returns null. * * @param cohorts List of cohorts, each a set of integers * @param ordinal Integer to search for * @return Cohort that contains the integer, or null if not found */ private static @Nullable Set findCohort( List> cohorts, int ordinal) { for (Set cohort : cohorts) { if (cohort.contains(ordinal)) { return cohort; } } return null; } private static int[] identityArray(int length) { final int[] ints = new int[length]; for (int i = 0; i < ints.length; i++) { ints[i] = i; } return ints; } /** * Creates a program containing the expressions for a given level. * *

The expression list of the program will consist of all entries in the * expression list allExprs[i] for which the corresponding * level ordinal exprLevels[i] is equal to level. * Expressions are mapped according to inputExprOrdinals. * * @param level Level ordinal * @param levelCount Number of levels * @param inputRowType Input row type * @param allExprs Array of all expressions * @param exprLevels Array of the level ordinal of each expression * @param inputExprOrdinals Ordinals in the expression list of input * expressions. Input expression i * will be found at position * inputExprOrdinals[i]. * @param projectExprOrdinals Ordinals of the expressions to be output this * level. * @param conditionExprOrdinal Ordinal of the expression to form the * condition for this level, or -1 if there is no * condition. * @param outputRowType Output row type * @return Relational expression */ private RexProgram createProgramForLevel( int level, int levelCount, RelDataType inputRowType, RexNode[] allExprs, int[] exprLevels, int[] inputExprOrdinals, final int[] projectExprOrdinals, int conditionExprOrdinal, @Nullable RelDataType outputRowType) { // Build a list of expressions to form the calc. List exprs = new ArrayList<>(); // exprInverseOrdinals describes where an expression in allExprs comes // from -- from an input, from a calculated expression, or -1 if not // available at this level. int[] exprInverseOrdinals = new int[allExprs.length]; Arrays.fill(exprInverseOrdinals, -1); int j = 0; // First populate the inputs. They were computed at some previous level // and are used here. for (int i = 0; i < inputExprOrdinals.length; i++) { final int inputExprOrdinal = inputExprOrdinals[i]; exprs.add( new RexInputRef( i, allExprs[inputExprOrdinal].getType())); exprInverseOrdinals[inputExprOrdinal] = j; ++j; } // Next populate the computed expressions. final RexShuttle shuttle = new InputToCommonExprConverter( exprInverseOrdinals, exprLevels, level, inputExprOrdinals, allExprs); for (int i = 0; i < allExprs.length; i++) { if (exprLevels[i] == level || exprLevels[i] == -1 && level == (levelCount - 1) && allExprs[i] instanceof RexLiteral) { RexNode expr = allExprs[i]; final RexNode translatedExpr = expr.accept(shuttle); exprs.add(translatedExpr); assert exprInverseOrdinals[i] == -1; exprInverseOrdinals[i] = j; ++j; } } // Form the projection and condition list. Project and condition // ordinals are offsets into allExprs, so we need to map them into // exprs. final List projectRefs = new ArrayList<>(projectExprOrdinals.length); final List fieldNames = new ArrayList<>(projectExprOrdinals.length); for (int i = 0; i < projectExprOrdinals.length; i++) { final int projectExprOrdinal = projectExprOrdinals[i]; final int index = exprInverseOrdinals[projectExprOrdinal]; assert index >= 0; RexNode expr = allExprs[projectExprOrdinal]; projectRefs.add(new RexLocalRef(index, expr.getType())); // Inherit meaningful field name if possible. fieldNames.add(deriveFieldName(expr, i)); } RexLocalRef conditionRef; if (conditionExprOrdinal >= 0) { final int index = exprInverseOrdinals[conditionExprOrdinal]; conditionRef = new RexLocalRef( index, allExprs[conditionExprOrdinal].getType()); } else { conditionRef = null; } if (outputRowType == null) { outputRowType = RexUtil.createStructType(typeFactory, projectRefs, fieldNames, null); } final RexProgram program = new RexProgram( inputRowType, exprs, projectRefs, conditionRef, outputRowType); // Program is NOT normalized here (e.g. can contain literals in // call operands), since literals should be inlined. return program; } private String deriveFieldName(RexNode expr, int ordinal) { if (expr instanceof RexInputRef) { int inputIndex = ((RexInputRef) expr).getIndex(); String fieldName = child.getRowType().getFieldList().get(inputIndex).getName(); // Don't inherit field names like '$3' from child: that's // confusing. if (!fieldName.startsWith("$") || fieldName.startsWith("$EXPR")) { return fieldName; } } return "$" + ordinal; } /** * Traces the given array of level expression lists at the finer level. * * @param exprs Array expressions * @param exprLevels For each expression, the ordinal of its level * @param levelTypeOrdinals For each level, the ordinal of its type in * the {@link #relTypes} array * @param levelCount The number of levels */ private void traceLevelExpressions( RexNode[] exprs, int[] exprLevels, int[] levelTypeOrdinals, int levelCount) { StringWriter traceMsg = new StringWriter(); PrintWriter traceWriter = new PrintWriter(traceMsg); traceWriter.println("FarragoAutoCalcRule result expressions for: "); traceWriter.println(program.toString()); for (int level = 0; level < levelCount; level++) { traceWriter.println("Rel Level " + level + ", type " + relTypes[levelTypeOrdinals[level]]); for (int i = 0; i < exprs.length; i++) { RexNode expr = exprs[i]; assert (exprLevels[i] >= -1) && (exprLevels[i] < levelCount) : "expression's level is out of range"; if (exprLevels[i] == level) { traceWriter.println("\t" + i + ": " + expr); } } traceWriter.println(); } String msg = traceMsg.toString(); RULE_LOGGER.trace(msg); } /** * Returns the number of bits set in an array. */ private static int count(boolean[] booleans) { int count = 0; for (boolean b : booleans) { if (b) { ++count; } } return count; } /** * Returns the index of the first set bit in an array. */ private static int firstSet(boolean[] booleans) { for (int i = 0; i < booleans.length; i++) { if (booleans[i]) { return i; } } return -1; } /** * Searches for a value in a map, and returns the position where it was * found, or -1. * * @param value Value to search for * @param map Map to search in * @return Ordinal of value in map, or -1 if not found */ private static int indexOf(int value, int[] map) { for (int i = 0; i < map.length; i++) { if (value == map[i]) { return i; } } return -1; } /** * Returns whether a relational expression can be implemented solely in a * given {@link RelType}. * * @param rel Calculation relational expression * @param relTypeName Name of a {@link RelType} * @return Whether relational expression can be implemented */ protected boolean canImplement(LogicalCalc rel, String relTypeName) { for (RelType relType : relTypes) { if (relType.name.equals(relTypeName)) { return relType.canImplement(rel.getProgram()); } } throw new AssertionError("unknown type " + relTypeName); } /** * Returns a list of sets of expressions that should be on the same level. * *

For example, if this method returns { {3, 5}, {4, 7} }, it means that * expressions 3 and 5, should be on the same level, and expressions 4 and 7 * should be on the same level. The two cohorts do not need to be on the * same level. * *

The list is best effort. If it is not possible to arrange that the * expressions in a cohort are on the same level, the {@link #execute()} * method will still succeed. * *

The default implementation of this method returns the empty list; * expressions will be put on the most suitable level. This is generally * the lowest possible level, except for literals, which are placed at the * level where they are used. * * @return List of cohorts, that is sets of expressions, that the splitting * algorithm should attempt to place on the same level */ protected List> getCohorts() { return Collections.emptyList(); } //~ Inner Classes ---------------------------------------------------------- /** Type of relational expression. Determines which kinds of * expressions it can handle. */ public abstract static class RelType { private final String name; protected RelType(String name) { this.name = name; } @Override public String toString() { return name; } protected abstract boolean canImplement(RexFieldAccess field); protected abstract boolean canImplement(RexDynamicParam param); protected abstract boolean canImplement(RexLiteral literal); protected abstract boolean canImplement(RexCall call); protected boolean supportsCondition() { return true; } protected RelNode makeRel(RelOptCluster cluster, RelTraitSet traitSet, RelBuilder relBuilder, RelNode input, RexProgram program) { return LogicalCalc.create(input, program); } /** * Returns whether this RelType can implement a given * expression. * * @param expr Expression * @param condition Whether expression is a condition * @return Whether this RelType can implement a given * expression. */ public boolean canImplement(RexNode expr, boolean condition) { if (condition && !supportsCondition()) { return false; } try { expr.accept(new ImplementTester(this)); return true; } catch (CannotImplement e) { Util.swallow(e, null); return false; } } /** * Returns whether this tester's RelType can implement a * given program. * * @param program Program * @return Whether this tester's RelType can implement a * given program. */ public boolean canImplement(RexProgram program) { if ((program.getCondition() != null) && !canImplement(program.getCondition(), true)) { return false; } for (RexNode expr : program.getExprList()) { if (!canImplement(expr, false)) { return false; } } return true; } } /** * Visitor which returns whether an expression can be implemented in a given * type of relational expression. */ private static class ImplementTester extends RexVisitorImpl { private final RelType relType; ImplementTester(RelType relType) { super(false); this.relType = relType; } @Override public Void visitCall(RexCall call) { if (!relType.canImplement(call)) { throw CannotImplement.INSTANCE; } return null; } @Override public Void visitDynamicParam(RexDynamicParam dynamicParam) { if (!relType.canImplement(dynamicParam)) { throw CannotImplement.INSTANCE; } return null; } @Override public Void visitFieldAccess(RexFieldAccess fieldAccess) { if (!relType.canImplement(fieldAccess)) { throw CannotImplement.INSTANCE; } return null; } @Override public Void visitLiteral(RexLiteral literal) { if (!relType.canImplement(literal)) { throw CannotImplement.INSTANCE; } return null; } } /** * Control exception for {@link ImplementTester}. */ private static class CannotImplement extends RuntimeException { @SuppressWarnings("ThrowableInstanceNeverThrown") static final CannotImplement INSTANCE = new CannotImplement(); } /** * Shuttle which converts every reference to an input field in an expression * to a reference to a common sub-expression. */ private static class InputToCommonExprConverter extends RexShuttle { private final int[] exprInverseOrdinals; private final int[] exprLevels; private final int level; private final int[] inputExprOrdinals; private final RexNode[] allExprs; InputToCommonExprConverter( int[] exprInverseOrdinals, int[] exprLevels, int level, int[] inputExprOrdinals, RexNode[] allExprs) { this.exprInverseOrdinals = exprInverseOrdinals; this.exprLevels = exprLevels; this.level = level; this.inputExprOrdinals = inputExprOrdinals; this.allExprs = allExprs; } @Override public RexNode visitInputRef(RexInputRef input) { final int index = exprInverseOrdinals[input.getIndex()]; assert index >= 0; return new RexLocalRef( index, input.getType()); } @Override public RexNode visitLocalRef(RexLocalRef local) { // A reference to a local variable becomes a reference to an input // if the local was computed at a previous level. final int localIndex = local.getIndex(); final int exprLevel = exprLevels[localIndex]; if (exprLevel < level) { if (allExprs[localIndex] instanceof RexLiteral) { // Expression is to be inlined. Use the original expression. return allExprs[localIndex]; } int inputIndex = indexOf(localIndex, inputExprOrdinals); assert inputIndex >= 0; return new RexLocalRef( inputIndex, local.getType()); } else { // It's a reference to what was a local expression at the // previous level, and was then projected. final int exprIndex = exprInverseOrdinals[localIndex]; return new RexLocalRef( exprIndex, local.getType()); } } } /** * Finds the highest level used by any of the inputs of a given expression. */ private static class MaxInputFinder extends RexVisitorImpl { int level; private final int[] exprLevels; MaxInputFinder(int[] exprLevels) { super(true); this.exprLevels = exprLevels; } @Override public Void visitLocalRef(RexLocalRef localRef) { int inputLevel = exprLevels[localRef.getIndex()]; level = Math.max(level, inputLevel); return null; } /** * Returns the highest level of any of the inputs of an expression. */ public int maxInputFor(RexNode expr) { level = 0; expr.accept(this); return level; } } /** * Builds an array of the highest level which contains an expression which * uses each expression as an input. */ private static class HighestUsageFinder extends RexVisitorImpl { private final int[] maxUsingLevelOrdinals; private int currentLevel; HighestUsageFinder(RexNode[] exprs, int[] exprLevels) { super(true); this.maxUsingLevelOrdinals = new int[exprs.length]; Arrays.fill(maxUsingLevelOrdinals, -1); for (int i = 0; i < exprs.length; i++) { if (exprs[i] instanceof RexLiteral) { // Literals are always used directly. It never makes sense // to compute them at a lower level and project them to // where they are used. maxUsingLevelOrdinals[i] = -1; continue; } currentLevel = exprLevels[i]; @SuppressWarnings("argument.type.incompatible") final Void unused = exprs[i].accept(this); } } public int[] getMaxUsingLevelOrdinals() { return maxUsingLevelOrdinals; } @Override public Void visitLocalRef(RexLocalRef ref) { final int index = ref.getIndex(); maxUsingLevelOrdinals[index] = Math.max(maxUsingLevelOrdinals[index], currentLevel); return null; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy