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

com.hazelcast.org.apache.calcite.rex.RexProgramBuilder 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.rex;

import com.hazelcast.org.apache.calcite.plan.RelOptPredicateList;
import com.hazelcast.org.apache.calcite.plan.RelOptUtil;
import com.hazelcast.org.apache.calcite.rel.type.RelDataType;
import com.hazelcast.org.apache.calcite.rel.type.RelDataTypeField;
import com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable;
import com.hazelcast.org.apache.calcite.util.Litmus;
import com.hazelcast.org.apache.calcite.util.Pair;

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

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static java.util.Objects.requireNonNull;

/**
 * Workspace for constructing a {@link RexProgram}.
 *
 * 

RexProgramBuilder is necessary because a {@link RexProgram} is immutable. * (The {@link String} class has the same problem: it is immutable, so they * introduced {@link StringBuilder}.) */ public class RexProgramBuilder { //~ Instance fields -------------------------------------------------------- private final RexBuilder rexBuilder; private final RelDataType inputRowType; private final List exprList = new ArrayList<>(); private final Map, RexLocalRef> exprMap = new HashMap<>(); private final List localRefList = new ArrayList<>(); private final List projectRefList = new ArrayList<>(); private final List<@Nullable String> projectNameList = new ArrayList<>(); @SuppressWarnings("unused") private final @Nullable RexSimplify simplify; private @Nullable RexLocalRef conditionRef = null; private boolean validating; //~ Constructors ----------------------------------------------------------- /** * Creates a program-builder that will not simplify. */ public RexProgramBuilder(RelDataType inputRowType, RexBuilder rexBuilder) { this(inputRowType, rexBuilder, null); } /** * Creates a program-builder. */ @SuppressWarnings("method.invocation.invalid") private RexProgramBuilder(RelDataType inputRowType, RexBuilder rexBuilder, @Nullable RexSimplify simplify) { this.inputRowType = requireNonNull(inputRowType, "inputRowType"); this.rexBuilder = requireNonNull(rexBuilder, "rexBuilder"); this.simplify = simplify; // may be null this.validating = assertionsAreEnabled(); // Pre-create an expression for each input field. if (inputRowType.isStruct()) { final List fields = inputRowType.getFieldList(); for (int i = 0; i < fields.size(); i++) { registerInternal(RexInputRef.of(i, fields), false); } } } /** * Creates a program builder with the same contents as a program. * * @param rexBuilder Rex builder * @param inputRowType Input row type * @param exprList Common expressions * @param projectList Projections * @param condition Condition, or null * @param outputRowType Output row type * @param normalize Whether to normalize * @param simplify Simplifier, or null to not simplify */ @SuppressWarnings("method.invocation.invalid") private RexProgramBuilder( RexBuilder rexBuilder, final RelDataType inputRowType, final List exprList, final Iterable projectList, @Nullable RexNode condition, final RelDataType outputRowType, boolean normalize, @Nullable RexSimplify simplify) { this(inputRowType, rexBuilder, simplify); // Create a shuttle for registering input expressions. final RexShuttle shuttle = new RegisterMidputShuttle(true, exprList); // If we are not normalizing, register all internal expressions. If we // are normalizing, expressions will be registered if and when they are // first used. if (!normalize) { shuttle.visitEach(exprList); } final RexShuttle expander = new RexProgram.ExpansionShuttle(exprList); // Register project expressions // and create a named project item. final List fieldList = outputRowType.getFieldList(); for (Pair pair : Pair.zip(projectList, fieldList)) { final RexNode project; if (simplify != null) { project = simplify.simplify(pair.left.accept(expander)); } else { project = pair.left; } final String name = pair.right.getName(); final RexLocalRef ref = (RexLocalRef) project.accept(shuttle); addProject(ref.getIndex(), name); } // Register the condition, if there is one. if (condition != null) { if (simplify != null) { condition = simplify.simplify( rexBuilder.makeCall(SqlStdOperatorTable.IS_TRUE, condition.accept(expander))); if (condition.isAlwaysTrue()) { condition = null; } } if (condition != null) { final RexLocalRef ref = (RexLocalRef) condition.accept(shuttle); addCondition(ref); } } } //~ Methods ---------------------------------------------------------------- /** * Returns whether assertions are enabled in this class. */ private static boolean assertionsAreEnabled() { boolean assertionsEnabled = false; //noinspection AssertWithSideEffects assert assertionsEnabled = true; return assertionsEnabled; } private void validate(final RexNode expr, final int fieldOrdinal) { final RexVisitor validator = new RexVisitorImpl(true) { @Override public Void visitInputRef(RexInputRef input) { final int index = input.getIndex(); final List fields = inputRowType.getFieldList(); if (index < fields.size()) { final RelDataTypeField inputField = fields.get(index); if (input.getType() != inputField.getType()) { throw new AssertionError("in expression " + expr + ", field reference " + input + " has inconsistent type"); } } else { if (index >= fieldOrdinal) { throw new AssertionError("in expression " + expr + ", field reference " + input + " is out of bounds"); } RexNode refExpr = exprList.get(index); if (refExpr.getType() != input.getType()) { throw new AssertionError("in expression " + expr + ", field reference " + input + " has inconsistent type"); } } return null; } }; expr.accept(validator); } /** * Adds a project expression to the program. * *

The expression specified in terms of the input fields. If not, call * {@link #registerOutput(RexNode)} first. * * @param expr Expression to add * @param name Name of field in output row type; if null, a unique name will * be generated when the program is created * @return the ref created */ public RexLocalRef addProject(RexNode expr, @Nullable String name) { final RexLocalRef ref = registerInput(expr); return addProject(ref.getIndex(), name); } /** * Adds a projection based upon the indexth expression. * * @param ordinal Index of expression to project * @param name Name of field in output row type; if null, a unique name * will be generated when the program is created * @return the ref created */ public RexLocalRef addProject(int ordinal, final @Nullable String name) { final RexLocalRef ref = localRefList.get(ordinal); projectRefList.add(ref); projectNameList.add(name); return ref; } /** * Adds a project expression to the program at a given position. * *

The expression specified in terms of the input fields. If not, call * {@link #registerOutput(RexNode)} first. * * @param at Position in project list to add expression * @param expr Expression to add * @param name Name of field in output row type; if null, a unique name will * be generated when the program is created * @return the ref created */ public RexLocalRef addProject(int at, RexNode expr, String name) { final RexLocalRef ref = registerInput(expr); projectRefList.add(at, ref); projectNameList.add(at, name); return ref; } /** * Adds a projection based upon the indexth expression at a * given position. * * @param at Position in project list to add expression * @param ordinal Index of expression to project * @param name Name of field in output row type; if null, a unique name * will be generated when the program is created * @return the ref created */ public RexLocalRef addProject(int at, int ordinal, final String name) { return addProject( at, localRefList.get(ordinal), name); } /** * Sets the condition of the program. * *

The expression must be specified in terms of the input fields. If * not, call {@link #registerOutput(RexNode)} first.

*/ public void addCondition(RexNode expr) { assert expr != null; RexLocalRef conditionRef = this.conditionRef; if (conditionRef == null) { this.conditionRef = conditionRef = registerInput(expr); } else { // AND the new condition with the existing condition. // If the new condition is identical to the existing condition, skip it. RexLocalRef ref = registerInput(expr); if (!ref.equals(conditionRef)) { this.conditionRef = registerInput( rexBuilder.makeCall( SqlStdOperatorTable.AND, conditionRef, ref)); } } } /** * Registers an expression in the list of common sub-expressions, and * returns a reference to that expression. *

The expression must be expressed in terms of the inputs of * this program.

*/ public RexLocalRef registerInput(RexNode expr) { final RexShuttle shuttle = new RegisterInputShuttle(true); final RexNode ref = expr.accept(shuttle); return (RexLocalRef) ref; } /** * Converts an expression expressed in terms of the outputs of this * program into an expression expressed in terms of the inputs, * registers it in the list of common sub-expressions, and returns a * reference to that expression. * * @param expr Expression to register */ public RexLocalRef registerOutput(RexNode expr) { final RexShuttle shuttle = new RegisterOutputShuttle(exprList); final RexNode ref = expr.accept(shuttle); return (RexLocalRef) ref; } /** * Registers an expression in the list of common sub-expressions, and * returns a reference to that expression. * *

If an equivalent sub-expression already exists, creates another * expression only if force is true. * * @param expr Expression to register * @param force Whether to create a new sub-expression if an equivalent * sub-expression exists. */ private RexLocalRef registerInternal(RexNode expr, boolean force) { final RexSimplify simplify = new RexSimplify(rexBuilder, RelOptPredicateList.EMPTY, RexUtil.EXECUTOR); expr = simplify.simplifyPreservingType(expr); RexLocalRef ref; final Pair key; if (expr instanceof RexLocalRef) { key = null; ref = (RexLocalRef) expr; } else { key = RexUtil.makeKey(expr); ref = exprMap.get(key); } if (ref == null) { if (validating) { validate( expr, exprList.size()); } // Add expression to list, and return a new reference to it. ref = addExpr(expr); exprMap.put(requireNonNull(key, "key"), ref); } else { if (force) { // Add expression to list, but return the previous ref. addExpr(expr); } } for (;;) { int index = ref.index; final RexNode expr2 = exprList.get(index); if (expr2 instanceof RexLocalRef) { ref = (RexLocalRef) expr2; } else { return ref; } } } /** * Adds an expression to the list of common expressions, and returns a * reference to the expression. DOES NOT CHECK WHETHER THE EXPRESSION * ALREADY EXISTS. * * @param expr Expression * @return Reference to expression */ public RexLocalRef addExpr(RexNode expr) { RexLocalRef ref; final int index = exprList.size(); exprList.add(expr); ref = new RexLocalRef( index, expr.getType()); localRefList.add(ref); return ref; } /** * Converts the state of the program builder to an immutable program, * normalizing in the process. * *

It is OK to call this method, modify the program specification (by * adding projections, and so forth), and call this method again. */ public RexProgram getProgram() { return getProgram(true); } /** * Converts the state of the program builder to an immutable program. * *

It is OK to call this method, modify the program specification (by * adding projections, and so forth), and call this method again. * * @param normalize Whether to normalize */ public RexProgram getProgram(boolean normalize) { assert projectRefList.size() == projectNameList.size(); // Make sure all fields have a name. generateMissingNames(); RelDataType outputRowType = computeOutputRowType(); if (normalize) { return create( rexBuilder, inputRowType, exprList, projectRefList, conditionRef, outputRowType, true) .getProgram(false); } return new RexProgram( inputRowType, exprList, projectRefList, conditionRef, outputRowType); } private RelDataType computeOutputRowType() { return RexUtil.createStructType(rexBuilder.typeFactory, projectRefList, projectNameList, null); } private void generateMissingNames() { int i = -1; int j = 0; for (String projectName : projectNameList) { ++i; if (projectName == null) { while (true) { final String candidateName = "$" + j++; if (!projectNameList.contains(candidateName)) { projectNameList.set(i, candidateName); break; } } } } } /** * Creates a program builder and initializes it from an existing program. * *

Calling {@link #getProgram()} immediately after creation will return a * program equivalent (in terms of external behavior) to the existing * program. * *

The existing program will not be changed. (It cannot: programs are * immutable.) * * @param program Existing program * @param rexBuilder Rex builder * @param normalize Whether to normalize * @return A program builder initialized with an equivalent program */ public static RexProgramBuilder forProgram( RexProgram program, RexBuilder rexBuilder, boolean normalize) { assert program.isValid(Litmus.THROW, null); final RelDataType inputRowType = program.getInputRowType(); final List projectRefs = program.getProjectList(); final RexLocalRef conditionRef = program.getCondition(); final List exprs = program.getExprList(); final RelDataType outputRowType = program.getOutputRowType(); return create( rexBuilder, inputRowType, exprs, projectRefs, conditionRef, outputRowType, normalize, false); } /** * Creates a program builder with the same contents as a program. * *

If {@code normalize}, converts the program to canonical form. In * canonical form, in addition to the usual constraints: * *

    *
  • The first N internal expressions are {@link RexInputRef}s to the N * input fields; *
  • Subsequent internal expressions reference only preceding expressions; *
  • Arguments to {@link RexCall}s must be {@link RexLocalRef}s (that is, * expressions must have maximum depth 1) *
* *

there are additional constraints: * *

    *
  • Expressions appear in the left-deep order they are needed by * the projections and (if present) the condition. Thus, expression N+1 * is the leftmost argument (literal or or call) in the expansion of * projection #0. *
  • There are no duplicate expressions *
  • There are no unused expressions *
* * @param rexBuilder Rex builder * @param inputRowType Input row type * @param exprList Common expressions * @param projectList Projections * @param condition Condition, or null * @param outputRowType Output row type * @param normalize Whether to normalize * @param simplify Whether to simplify expressions * @return A program builder */ public static RexProgramBuilder create( RexBuilder rexBuilder, final RelDataType inputRowType, final List exprList, final List projectList, final @Nullable RexNode condition, final RelDataType outputRowType, boolean normalize, @Nullable RexSimplify simplify) { return new RexProgramBuilder(rexBuilder, inputRowType, exprList, projectList, condition, outputRowType, normalize, simplify); } @Deprecated // to be removed before 2.0 public static RexProgramBuilder create( RexBuilder rexBuilder, final RelDataType inputRowType, final List exprList, final List projectList, final @Nullable RexNode condition, final RelDataType outputRowType, boolean normalize, boolean simplify_) { RexSimplify simplify = null; if (simplify_) { simplify = new RexSimplify(rexBuilder, RelOptPredicateList.EMPTY, RexUtil.EXECUTOR); } return new RexProgramBuilder(rexBuilder, inputRowType, exprList, projectList, condition, outputRowType, normalize, simplify); } @Deprecated // to be removed before 2.0 public static RexProgramBuilder create( RexBuilder rexBuilder, final RelDataType inputRowType, final List exprList, final List projectList, final @Nullable RexNode condition, final RelDataType outputRowType, boolean normalize) { return create(rexBuilder, inputRowType, exprList, projectList, condition, outputRowType, normalize, null); } /** * Creates a program builder with the same contents as a program, applying a * shuttle first. * *

TODO: Refactor the above create method in terms of this one. * * @param rexBuilder Rex builder * @param inputRowType Input row type * @param exprList Common expressions * @param projectRefList Projections * @param conditionRef Condition, or null * @param outputRowType Output row type * @param shuttle Shuttle to apply to each expression before adding it * to the program builder * @param updateRefs Whether to update references that changes as a result * of rewrites made by the shuttle * @return A program builder */ public static RexProgramBuilder create( RexBuilder rexBuilder, final RelDataType inputRowType, final List exprList, final List projectRefList, final @Nullable RexLocalRef conditionRef, final RelDataType outputRowType, final RexShuttle shuttle, final boolean updateRefs) { final RexProgramBuilder progBuilder = new RexProgramBuilder(inputRowType, rexBuilder); progBuilder.add( exprList, projectRefList, conditionRef, outputRowType, shuttle, updateRefs); return progBuilder; } @Deprecated // to be removed before 2.0 public static RexProgram normalize( RexBuilder rexBuilder, RexProgram program) { return program.normalize(rexBuilder, null); } /** * Adds a set of expressions, projections and filters, applying a shuttle * first. * * @param exprList Common expressions * @param projectRefList Projections * @param conditionRef Condition, or null * @param outputRowType Output row type * @param shuttle Shuttle to apply to each expression before adding it * to the program builder * @param updateRefs Whether to update references that changes as a result * of rewrites made by the shuttle */ private void add( List exprList, List projectRefList, @Nullable RexLocalRef conditionRef, final RelDataType outputRowType, RexShuttle shuttle, boolean updateRefs) { final List outFields = outputRowType.getFieldList(); final RexShuttle registerInputShuttle = new RegisterInputShuttle(false); // For each common expression, first apply the user's shuttle, then // register the result. // REVIEW jpham 28-Apr-2006: if the user shuttle rewrites an input // expression, then input references may change List newRefs = new ArrayList<>(exprList.size()); RexShuttle refShuttle = new UpdateRefShuttle(newRefs); int i = 0; for (RexNode expr : exprList) { RexNode newExpr = expr; if (updateRefs) { newExpr = expr.accept(refShuttle); } newExpr = newExpr.accept(shuttle); newRefs.add( i++, (RexLocalRef) newExpr.accept(registerInputShuttle)); } i = -1; for (RexLocalRef oldRef : projectRefList) { ++i; RexLocalRef ref = oldRef; if (updateRefs) { ref = (RexLocalRef) oldRef.accept(refShuttle); } ref = (RexLocalRef) ref.accept(shuttle); this.projectRefList.add(ref); final String name = outFields.get(i).getName(); assert name != null; projectNameList.add(name); } if (conditionRef != null) { if (updateRefs) { conditionRef = (RexLocalRef) conditionRef.accept(refShuttle); } conditionRef = (RexLocalRef) conditionRef.accept(shuttle); addCondition(conditionRef); } } /** * Merges two programs together, and normalizes the result. * * @param topProgram Top program. Its expressions are in terms of the * outputs of the bottom program. * @param bottomProgram Bottom program. Its expressions are in terms of the * result fields of the relational expression's input * @param rexBuilder Rex builder * @return Merged program * @see #mergePrograms(RexProgram, RexProgram, RexBuilder, boolean) */ public static RexProgram mergePrograms( RexProgram topProgram, RexProgram bottomProgram, RexBuilder rexBuilder) { return mergePrograms(topProgram, bottomProgram, rexBuilder, true); } /** * Merges two programs together. * *

All expressions become common sub-expressions. For example, the query * *

SELECT x + 1 AS p, x + y AS q FROM (
   *   SELECT a + b AS x, c AS y
   *   FROM t
   *   WHERE c = 6)}
* *

would be represented as the programs * *

   *   Calc:
   *       Projects={$2, $3},
   *       Condition=null,
   *       Exprs={$0, $1, $0 + 1, $0 + $1})
   *   Calc(
   *       Projects={$3, $2},
   *       Condition={$4}
   *       Exprs={$0, $1, $2, $0 + $1, $2 = 6}
   * 
* *

The merged program is * *

   *   Calc(
   *      Projects={$4, $5}
   *      Condition=$6
   *      Exprs={0: $0       // a
   *             1: $1        // b
   *             2: $2        // c
   *             3: ($0 + $1) // x = a + b
   *             4: ($3 + 1)  // p = x + 1
   *             5: ($3 + $2) // q = x + y
   *             6: ($2 = 6)  // c = 6
   * 
* *

Another example:

* *
*
SELECT *
   * FROM (
   *   SELECT a + b AS x, c AS y
   *   FROM t
   *   WHERE c = 6)
   * WHERE x = 5
*
* *

becomes * *

*
SELECT a + b AS x, c AS y
   * FROM t
   * WHERE c = 6 AND (a + b) = 5
*
* * @param topProgram Top program. Its expressions are in terms of the * outputs of the bottom program. * @param bottomProgram Bottom program. Its expressions are in terms of the * result fields of the relational expression's input * @param rexBuilder Rex builder * @param normalize Whether to convert program to canonical form * @return Merged program */ public static RexProgram mergePrograms( RexProgram topProgram, RexProgram bottomProgram, RexBuilder rexBuilder, boolean normalize) { // Initialize a program builder with the same expressions, outputs // and condition as the bottom program. assert bottomProgram.isValid(Litmus.THROW, null); assert topProgram.isValid(Litmus.THROW, null); final RexProgramBuilder progBuilder = RexProgramBuilder.forProgram(bottomProgram, rexBuilder, false); // Drive from the outputs of the top program. Register each expression // used as an output. final List projectRefList = progBuilder.registerProjectsAndCondition(topProgram); // Switch to the projects needed by the top program. The original // projects of the bottom program are no longer needed. progBuilder.clearProjects(); final RelDataType outputRowType = topProgram.getOutputRowType(); for (Pair pair : Pair.zip(projectRefList, outputRowType.getFieldNames(), true)) { progBuilder.addProject(pair.left, pair.right); } RexProgram mergedProg = progBuilder.getProgram(normalize); assert mergedProg.isValid(Litmus.THROW, null); assert mergedProg.getOutputRowType() == topProgram.getOutputRowType(); return mergedProg; } private List registerProjectsAndCondition(RexProgram program) { final List exprList = program.getExprList(); final List projectRefList = new ArrayList<>(); final RexShuttle shuttle = new RegisterOutputShuttle(exprList); // For each project, lookup the expr and expand it so it is in terms of // bottomCalc's input fields for (RexLocalRef topProject : program.getProjectList()) { final RexNode topExpr = exprList.get(topProject.getIndex()); final RexLocalRef expanded = (RexLocalRef) topExpr.accept(shuttle); // Remember the expr, but don't add to the project list yet. projectRefList.add(expanded); } // Similarly for the condition. final RexLocalRef topCondition = program.getCondition(); if (topCondition != null) { final RexNode topExpr = exprList.get(topCondition.getIndex()); final RexLocalRef expanded = (RexLocalRef) topExpr.accept(shuttle); addCondition(registerInput(expanded)); } return projectRefList; } /** * Removes all project items. * *

After calling this method, you may need to re-normalize.

*/ public void clearProjects() { projectRefList.clear(); projectNameList.clear(); } /** * Clears the condition. * *

After calling this method, you may need to re-normalize.

*/ public void clearCondition() { conditionRef = null; } /** * Adds a project item for every input field. * *

You cannot call this method if there are other project items. */ public void addIdentity() { assert projectRefList.isEmpty(); for (RelDataTypeField field : inputRowType.getFieldList()) { addProject( new RexInputRef( field.getIndex(), field.getType()), field.getName()); } } /** * Creates a reference to a given input field. * * @param index Ordinal of input field, must be less than the number of * fields in the input type * @return Reference to input field */ public RexLocalRef makeInputRef(int index) { final List fields = inputRowType.getFieldList(); assert index < fields.size(); final RelDataTypeField field = fields.get(index); return new RexLocalRef( index, field.getType()); } /** * Returns the row type of the input to the program. */ public RelDataType getInputRowType() { return inputRowType; } /** * Returns the list of project expressions. */ public List getProjectList() { return projectRefList; } //~ Inner Classes ---------------------------------------------------------- /** Shuttle that visits a tree of {@link RexNode} and registers them * in a program. */ private abstract class RegisterShuttle extends RexShuttle { @Override public RexNode visitCall(RexCall call) { final RexNode expr = super.visitCall(call); return registerInternal(expr, false); } @Override public RexNode visitOver(RexOver over) { final RexNode expr = super.visitOver(over); return registerInternal(expr, false); } @Override public RexNode visitLiteral(RexLiteral literal) { final RexNode expr = super.visitLiteral(literal); return registerInternal(expr, false); } @Override public RexNode visitFieldAccess(RexFieldAccess fieldAccess) { final RexNode expr = super.visitFieldAccess(fieldAccess); return registerInternal(expr, false); } @Override public RexNode visitDynamicParam(RexDynamicParam dynamicParam) { final RexNode expr = super.visitDynamicParam(dynamicParam); return registerInternal(expr, false); } @Override public RexNode visitCorrelVariable(RexCorrelVariable variable) { final RexNode expr = super.visitCorrelVariable(variable); return registerInternal(expr, false); } } /** * Shuttle which walks over an expression, registering each sub-expression. * Each {@link RexInputRef} is assumed to refer to an input of the * program. */ private class RegisterInputShuttle extends RegisterShuttle { private final boolean valid; protected RegisterInputShuttle(boolean valid) { this.valid = valid; } @Override public RexNode visitInputRef(RexInputRef input) { final int index = input.getIndex(); if (valid) { // The expression should already be valid. Check that its // index is within bounds. if ((index < 0) || (index >= inputRowType.getFieldCount())) { assert false : "RexInputRef index " + index + " out of range 0.." + (inputRowType.getFieldCount() - 1); } // Check that the type is consistent with the referenced // field. If it is an object type, the rules are different, so // skip the check. assert input.getType().isStruct() || RelOptUtil.eq("type1", input.getType(), "type2", inputRowType.getFieldList().get(index).getType(), Litmus.THROW); } // Return a reference to the N'th expression, which should be // equivalent. final RexLocalRef ref = localRefList.get(index); return ref; } @Override public RexNode visitLocalRef(RexLocalRef local) { if (valid) { // The expression should already be valid. final int index = local.getIndex(); assert index >= 0 : index; assert index < exprList.size() : "index=" + index + ", exprList=" + exprList; assert RelOptUtil.eq( "expr type", exprList.get(index).getType(), "ref type", local.getType(), Litmus.THROW); } // Resolve the expression to an input. while (true) { final int index = local.getIndex(); final RexNode expr = exprList.get(index); if (expr instanceof RexLocalRef) { local = (RexLocalRef) expr; if (local.index >= index) { throw new AssertionError( "expr " + local + " references later expr " + local.index); } } else { // Add expression to the list, just so that subsequent // expressions don't get screwed up. This expression is // unused, so will be eliminated soon. return registerInternal(local, false); } } } } /** * Extension to {@link RegisterInputShuttle} which allows expressions to be * in terms of inputs or previous common sub-expressions. */ private class RegisterMidputShuttle extends RegisterInputShuttle { private final List localExprList; protected RegisterMidputShuttle( boolean valid, List localExprList) { super(valid); this.localExprList = localExprList; } @Override public RexNode visitLocalRef(RexLocalRef local) { // Convert a local ref into the common-subexpression it references. final int index = local.getIndex(); return localExprList.get(index).accept(this); } } /** * Shuttle which walks over an expression, registering each sub-expression. * Each {@link RexInputRef} is assumed to refer to an output of the * program. */ private class RegisterOutputShuttle extends RegisterShuttle { private final List localExprList; RegisterOutputShuttle(List localExprList) { super(); this.localExprList = localExprList; } @Override public RexNode visitInputRef(RexInputRef input) { // This expression refers to the Nth project column. Lookup that // column and find out what common sub-expression IT refers to. final int index = input.getIndex(); final RexLocalRef local = projectRefList.get(index); assert RelOptUtil.eq( "type1", local.getType(), "type2", input.getType(), Litmus.THROW); return local; } @Override public RexNode visitLocalRef(RexLocalRef local) { // Convert a local ref into the common-subexpression it references. final int index = local.getIndex(); return localExprList.get(index).accept(this); } } /** * Shuttle that rewires {@link RexLocalRef} using a list of updated * references. */ private static class UpdateRefShuttle extends RexShuttle { private List newRefs; private UpdateRefShuttle(List newRefs) { this.newRefs = newRefs; } @Override public RexNode visitLocalRef(RexLocalRef localRef) { return newRefs.get(localRef.getIndex()); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy