public void collectVariablesUsed(Set<String> variableSet) { final RelOptUtil.VariableUsedVisitor vuv = new RelOptUtil.VariableUsedVisitor(); for (RexNode expr : program.getExprList()) { expr.accept(vuv); } variableSet.addAll(vuv.variables); }
public void onMatch(RelOptRuleCall call) { CalcRel calc = (CalcRel) call.getRels()[0]; RexProgram program = calc.getProgram(); final List<RexNode> exprList = program.getExprList(); // Form a list of expressions with sub-expressions fully // expanded. final List<RexNode> expandedExprList = new ArrayList<RexNode>(exprList.size()); final RexShuttle shuttle = new RexShuttle() { public RexNode visitLocalRef(RexLocalRef localRef) { return expandedExprList.get(localRef.getIndex()); } }; for (RexNode expr : exprList) { expandedExprList.add(expr.accept(shuttle)); } if (reduceExpressions(calc, expandedExprList)) { final RexProgramBuilder builder = new RexProgramBuilder( calc.getChild().getRowType(), calc.getCluster().getRexBuilder()); List<RexLocalRef> list = new ArrayList<RexLocalRef>(); for (RexNode expr : expandedExprList) { list.add(builder.registerInput(expr)); } if (program.getCondition() != null) { final int conditionIndex = program.getCondition().getIndex(); final RexNode newConditionExp = expandedExprList.get(conditionIndex); if (newConditionExp.isAlwaysTrue()) { // condition is always TRUE - drop it } else if ((newConditionExp instanceof RexLiteral) || RexUtil.isNullLiteral(newConditionExp, true)) { // condition is always NULL or FALSE - replace calc // with empty call.transformTo(new EmptyRel(calc.getCluster(), calc.getRowType())); return; } else { builder.addCondition(list.get(conditionIndex)); } } int k = 0; for (RexLocalRef projectExpr : program.getProjectList()) { final int index = projectExpr.getIndex(); builder.addProject( list.get(index).getIndex(), program.getOutputRowType().getFieldList().get(k++).getName()); } call.transformTo( new CalcRel( calc.getCluster(), calc.getTraits(), calc.getChild(), calc.getRowType(), builder.getProgram(), calc.getCollationList())); // New plan is absolutely better than old plan. call.getPlanner().setImportance(calc, 0.0); } }
public Void visitCall(RexCall call) { final RexNode[] operands = call.getOperands(); for (int i = 0; i < operands.length; i++) { RexNode operand = operands[i]; operand.accept(this); } return null; }
/** * Creates a key for {@link RexNode} which is the same as another key of another RexNode only if * the two have both the same type and textual representation. For example, "10" integer and "10" * bigint result in different keys. */ public static String makeKey(RexNode expr) { String type = expr.getType().getFullTypeString(); String separator = ";"; String node = expr.toString(); StringBuilder keyBuilder = new StringBuilder(type.length() + separator.length() + node.length()); keyBuilder.append(type).append(separator).append(node); return keyBuilder.toString(); }
public RexNode visitFieldAccess(RexFieldAccess fieldAccess) { final RexNode expr = fieldAccess.getReferenceExpr(); expr.accept(this); final RexNode normalizedExpr = lookup(expr); if (normalizedExpr != expr) { fieldAccess = new RexFieldAccess(normalizedExpr, fieldAccess.getField()); } return register(fieldAccess); }
/** * Applies a shuttle to an array of expressions. Creates a copy first. * * @param shuttle Shuttle * @param exprs Array of expressions */ public static <T extends RexNode> T[] apply(RexVisitor<T> shuttle, T[] exprs) { T[] newExprs = exprs.clone(); for (int i = 0; i < newExprs.length; i++) { final RexNode expr = newExprs[i]; if (expr != null) { newExprs[i] = expr.accept(shuttle); } } return newExprs; }
private void addResult(RexNode exp) { // Cast of literal can't be reduced, so skip those (otherwise we'd // go into an infinite loop as we add them back). if (exp.getKind() == RexKind.Cast) { RexCall cast = (RexCall) exp; RexNode operand = cast.getOperands()[0]; if (operand instanceof RexLiteral) { return; } } constExprs.add(exp); // In the case where the expression corresponds to a UDR argument, // we need to preserve casts. Note that this only applies to // the topmost argument, not expressions nested within the UDR // call. // // REVIEW zfong 6/13/08 - Are there other expressions where we // also need to preserve casts? if (parentCallTypeStack.isEmpty()) { addCasts.add(false); } else { addCasts.add( parentCallTypeStack.get(parentCallTypeStack.size() - 1) instanceof FarragoUserDefinedRoutine); } }
public RexNode visitCall(RexCall call) { List<RexNode> normalizedOperands = new ArrayList<RexNode>(); int diffCount = 0; for (RexNode operand : call.getOperands()) { operand.accept(this); final RexNode normalizedOperand = lookup(operand); normalizedOperands.add(normalizedOperand); if (normalizedOperand != operand) { ++diffCount; } } if (diffCount > 0) { call = call.clone(call.getType(), normalizedOperands); } return register(call); }
/** * Returns whether an array of expressions contains a forward reference. That is, if expression #i * contains a {@link RexInputRef} referencing field i or greater. * * @param exprs Array of expressions * @param inputRowType Input row type * @param fail Whether to assert if there is a forward reference * @return Whether there is a forward reference */ public static boolean containForwardRefs( RexNode[] exprs, RelDataType inputRowType, boolean fail) { final ForwardRefFinder visitor = new ForwardRefFinder(inputRowType); for (int i = 0; i < exprs.length; i++) { RexNode expr = exprs[i]; visitor.setLimit(i); // field cannot refer to self or later field try { expr.accept(visitor); } catch (ForwardRefFinder.IllegalForwardRefException e) { Util.swallow(e, null); assert !fail : "illegal forward reference in " + expr; return true; } } return false; }
/** * Applies a visitor to an array of expressions and, if specified, a single expression. * * @param visitor Visitor * @param exprs Array of expressions * @param expr Single expression, may be null */ public static void apply(RexVisitor<Void> visitor, RexNode[] exprs, RexNode expr) { for (int i = 0; i < exprs.length; i++) { exprs[i].accept(visitor); } if (expr != null) { expr.accept(visitor); } }
protected RexNode register(RexNode expr) { final String key = expr.toString(); final RexNode previous = mapDigestToExpr.put(key, expr); if (!allowDups && (previous != null)) { throw new SubExprExistsException(expr); } return expr; }
/** * Applies a visitor to a list of expressions and, if specified, a single expression. * * @param visitor Visitor * @param exprs List of expressions * @param expr Single expression, may be null */ public static void apply(RexVisitor<Void> visitor, List<? extends RexNode> exprs, RexNode expr) { for (int i = 0; i < exprs.size(); i++) { exprs.get(i).accept(visitor); } if (expr != null) { expr.accept(visitor); } }
// override RexShuttle public RexNode visitCall(final RexCall call) { int i = reducibleExps.indexOf(call); if (i == -1) { return super.visitCall(call); } RexNode replacement = reducedValues.get(i); if (addCasts.get(i) && (replacement.getType() != call.getType())) { // Handle change from nullable to NOT NULL by claiming // that the result is still nullable, even though // we know it isn't. // // Also, we cannot reduce CAST('abc' AS VARCHAR(4)) to 'abc'. // If we make 'abc' of type VARCHAR(4), we may later encounter // the same expression in a ProjectRel's digest where it has // type VARCHAR(3), and that's wrong. replacement = rexBuilder.makeCast(call.getType(), replacement); } return replacement; }
public void onMatch(RelOptRuleCall call) { FilterRel filter = (FilterRel) call.rels[0]; List<RexNode> expList = new ArrayList<RexNode>(Arrays.asList(filter.getChildExps())); RexNode newConditionExp; boolean reduced; if (reduceExpressions(filter, expList)) { assert (expList.size() == 1); newConditionExp = expList.get(0); reduced = true; } else { // No reduction, but let's still test the original // predicate to see if it was already a constant, // in which case we don't need any runtime decision // about filtering. newConditionExp = filter.getChildExps()[0]; reduced = false; } if (newConditionExp.isAlwaysTrue()) { call.transformTo(filter.getChild()); } else if ((newConditionExp instanceof RexLiteral) || RexUtil.isNullLiteral(newConditionExp, true)) { call.transformTo(new EmptyRel(filter.getCluster(), filter.getRowType())); } else if (reduced) { call.transformTo(CalcRel.createFilter(filter.getChild(), expList.get(0))); } else { if (newConditionExp instanceof RexCall) { RexCall rexCall = (RexCall) newConditionExp; boolean reverse = (rexCall.getOperator() == SqlStdOperatorTable.notOperator); if (reverse) { rexCall = (RexCall) rexCall.getOperands()[0]; } reduceNotNullableFilter(call, filter, rexCall, reverse); } return; } // New plan is absolutely better than old plan. call.getPlanner().setImportance(filter, 0.0); }
/** Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link FilterRel}. */ public TrimResult trimFields( FilterRel filter, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { final RelDataType rowType = filter.getRowType(); final int fieldCount = rowType.getFieldCount(); final RexNode conditionExpr = filter.getCondition(); final RelNode input = filter.getChild(); // We use the fields used by the consumer, plus any fields used in the // filter. BitSet inputFieldsUsed = (BitSet) fieldsUsed.clone(); final Set<RelDataTypeField> inputExtraFields = new LinkedHashSet<RelDataTypeField>(extraFields); RelOptUtil.InputFinder inputFinder = new RelOptUtil.InputFinder(inputFieldsUsed, inputExtraFields); conditionExpr.accept(inputFinder); // Create input with trimmed columns. TrimResult trimResult = trimChild(filter, input, inputFieldsUsed, inputExtraFields); RelNode newInput = trimResult.left; final Mapping inputMapping = trimResult.right; // If the input is unchanged, and we need to project all columns, // there's nothing we can do. if (newInput == input && fieldsUsed.cardinality() == fieldCount) { return new TrimResult(filter, Mappings.createIdentity(fieldCount)); } // Build new project expressions, and populate the mapping. final RexVisitor<RexNode> shuttle = new RexPermuteInputsShuttle(inputMapping, newInput); RexNode newConditionExpr = conditionExpr.accept(shuttle); final FilterRel newFilter = new FilterRel(filter.getCluster(), newInput, newConditionExpr); assert newFilter.getClass() == filter.getClass(); // The result has the same mapping as the input gave us. Sometimes we // return fields that the consumer didn't ask for, because the filter // needs them for its condition. return new TrimResult(newFilter, inputMapping); }
/** * Returns whether a given tree contains any {@link org.eigenbase.rex.RexFieldAccess} nodes. * * @param node a RexNode tree */ public static boolean containsFieldAccess(RexNode node) { try { RexVisitor<Void> visitor = new RexVisitorImpl<Void>(true) { public Void visitFieldAccess(RexFieldAccess fieldAccess) { throw new Util.FoundOne(fieldAccess); } }; node.accept(visitor); return false; } catch (Util.FoundOne e) { Util.swallow(e, null); return true; } }
/** * Returns whether a given tree contains any {link RexInputRef} nodes. * * @param node a RexNode tree */ public static boolean containsInputRef(RexNode node) { try { RexVisitor<Void> visitor = new RexVisitorImpl<Void>(true) { public Void visitInputRef(RexInputRef inputRef) { throw new Util.FoundOne(inputRef); } }; node.accept(visitor); return false; } catch (Util.FoundOne e) { Util.swallow(e, null); return true; } }
public void analyze(RexNode exp) { assert (stack.isEmpty()); exp.accept(this); // Deal with top of stack assert (stack.size() == 1); assert (parentCallTypeStack.isEmpty()); Constancy rootConstancy = stack.get(0); if (rootConstancy == Constancy.REDUCIBLE_CONSTANT) { // The entire subtree was constant, so add it to the result. addResult(exp); } stack.clear(); }
/** * Returns whether a given node contains a RexCall with a specified operator * * @param operator to look for * @param node a RexNode tree */ public static RexCall findOperatorCall(final SqlOperator operator, RexNode node) { try { RexVisitor<Void> visitor = new RexVisitorImpl<Void>(true) { public Void visitCall(RexCall call) { if (call.getOperator().equals(operator)) { throw new Util.FoundOne(call); } return super.visitCall(call); } }; node.accept(visitor); return null; } catch (Util.FoundOne e) { Util.swallow(e, null); return (RexCall) e.getNode(); } }
/** * Returns whether a node represents the NULL value. * * <p>Examples: * * <ul> * <li>For {@link org.eigenbase.rex.RexLiteral} Unknown, returns false. * <li>For <code>CAST(NULL AS <i>type</i>)</code>, returns true if <code> * allowCast</code> is true, false otherwise. * <li>For <code>CAST(CAST(NULL AS <i>type</i>) AS <i>type</i>))</code>, returns false. * </ul> */ public static boolean isNullLiteral(RexNode node, boolean allowCast) { if (node instanceof RexLiteral) { RexLiteral literal = (RexLiteral) node; if (literal.getTypeName() == SqlTypeName.NULL) { assert (null == literal.getValue()); return true; } else { // We don't regard UNKNOWN -- SqlLiteral(null,Boolean) -- as // NULL. return false; } } if (allowCast) { if (node.isA(RexKind.Cast)) { RexCall call = (RexCall) node; if (isNullLiteral(call.operands[0], false)) { // node is "CAST(NULL as type)" return true; } } } return false; }
/** * Returns a guess for the selectivity of an expression. * * @param exp expression of interest, or null for none (implying a selectivity of 1.0) * @return guessed selectivity */ public static double getSelectivity(RexNode exp) { if ((exp == null) || exp.isAlwaysTrue()) { return 1; } return 0.1; }
/** Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link JoinRel}. */ public TrimResult trimFields(JoinRel join, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { final RelDataType rowType = join.getRowType(); final int fieldCount = rowType.getFieldCount(); final RexNode conditionExpr = join.getCondition(); final int systemFieldCount = join.getSystemFieldList().size(); // Add in fields used in the condition. BitSet fieldsUsedPlus = (BitSet) fieldsUsed.clone(); final Set<RelDataTypeField> combinedInputExtraFields = new LinkedHashSet<RelDataTypeField>(extraFields); RelOptUtil.InputFinder inputFinder = new RelOptUtil.InputFinder(fieldsUsedPlus, combinedInputExtraFields); conditionExpr.accept(inputFinder); // If no system fields are used, we can remove them. int systemFieldUsedCount = 0; for (int i = 0; i < systemFieldCount; ++i) { if (fieldsUsed.get(i)) { ++systemFieldUsedCount; } } final int newSystemFieldCount; if (systemFieldUsedCount == 0) { newSystemFieldCount = 0; } else { newSystemFieldCount = systemFieldCount; } int offset = systemFieldCount; int changeCount = 0; int newFieldCount = newSystemFieldCount; List<RelNode> newInputs = new ArrayList<RelNode>(2); List<Mapping> inputMappings = new ArrayList<Mapping>(); List<Integer> inputExtraFieldCounts = new ArrayList<Integer>(); for (RelNode input : join.getInputs()) { final RelDataType inputRowType = input.getRowType(); final int inputFieldCount = inputRowType.getFieldCount(); // Compute required mapping. BitSet inputFieldsUsed = new BitSet(inputFieldCount); for (int bit : Util.toIter(fieldsUsedPlus)) { if (bit >= offset && bit < offset + inputFieldCount) { inputFieldsUsed.set(bit - offset); } } // If there are system fields, we automatically use the // corresponding field in each input. if (newSystemFieldCount > 0) { // calling with newSystemFieldCount == 0 should be safe but hits // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6222207 inputFieldsUsed.set(0, newSystemFieldCount); } // FIXME: We ought to collect extra fields for each input // individually. For now, we assume that just one input has // on-demand fields. Set<RelDataTypeField> inputExtraFields = input.getRowType().getField("_extra") == null ? Collections.<RelDataTypeField>emptySet() : combinedInputExtraFields; inputExtraFieldCounts.add(inputExtraFields.size()); TrimResult trimResult = trimChild(join, input, inputFieldsUsed, inputExtraFields); newInputs.add(trimResult.left); if (trimResult.left != input) { ++changeCount; } final Mapping inputMapping = trimResult.right; inputMappings.add(inputMapping); // Move offset to point to start of next input. offset += inputFieldCount; newFieldCount += inputMapping.getTargetCount() + inputExtraFields.size(); } Mapping mapping = Mappings.create(MappingType.InverseSurjection, fieldCount, newFieldCount); for (int i = 0; i < newSystemFieldCount; ++i) { mapping.set(i, i); } offset = systemFieldCount; int newOffset = newSystemFieldCount; for (int i = 0; i < inputMappings.size(); i++) { Mapping inputMapping = inputMappings.get(i); for (IntPair pair : inputMapping) { mapping.set(pair.source + offset, pair.target + newOffset); } offset += inputMapping.getSourceCount(); newOffset += inputMapping.getTargetCount() + inputExtraFieldCounts.get(i); } if (changeCount == 0 && mapping.isIdentity()) { return new TrimResult(join, Mappings.createIdentity(fieldCount)); } // Build new join. final RexVisitor<RexNode> shuttle = new RexPermuteInputsShuttle(mapping, newInputs.get(0), newInputs.get(1)); RexNode newConditionExpr = conditionExpr.accept(shuttle); final JoinRel newJoin = join.copy(join.getTraitSet(), newConditionExpr, newInputs.get(0), newInputs.get(1)); return new TrimResult(newJoin, mapping); }
/** * Returns whether a node represents the NULL value or a series of nested CAST(NULL as <TYPE>) * calls<br> * For Example:<br> * isNull(CAST(CAST(NULL as INTEGER) AS VARCHAR(1))) returns true */ public static boolean isNull(RexNode node) { /* Checks to see if the RexNode is null */ return RexLiteral.isNullLiteral(node) || ((node.getKind() == RexKind.Cast) && isNull(((RexCall) node).operands[0])); }
protected RexNode lookup(RexNode expr) { return mapDigestToExpr.get(expr.toString()); }