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(); }
/** * Applies a mapping to a collation list. * * @param mapping Mapping * @param collationList Collation list * @return collation list with mapping applied to each field */ public static List<RelCollation> apply( Mappings.TargetMapping mapping, List<RelCollation> collationList) { final List<RelCollation> newCollationList = new ArrayList<RelCollation>(); for (RelCollation collation : collationList) { final List<RelFieldCollation> newFieldCollationList = new ArrayList<RelFieldCollation>(); for (RelFieldCollation fieldCollation : collation.getFieldCollations()) { final RelFieldCollation newFieldCollation = apply(mapping, fieldCollation); if (newFieldCollation == null) { // This field is not mapped. Stop here. The leading edge // of the collation is still valid (although it's useless // if it's empty). break; } newFieldCollationList.add(newFieldCollation); } // Truncation to collations to their leading edge creates empty // and duplicate collations. Ignore these. if (!newFieldCollationList.isEmpty()) { final RelCollationImpl newCollation = new RelCollationImpl(newFieldCollationList); if (!newCollationList.contains(newCollation)) { newCollationList.add(newCollation); } } } // REVIEW: There might be redundant collations in the list. For example, // in {(x), (x, y)}, (x) is redundant because it is a leading edge of // another collation in the list. Could remove redundant collations. return newCollationList; }
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); } }
/** * Creates an OR expression from a list of RexNodes * * @param rexList list of RexNodes * @return OR'd expression */ public static RexNode orRexNodeList(RexBuilder rexBuilder, List<RexNode> rexList) { if (rexList.isEmpty()) { return null; } RexNode orExpr = rexList.get(rexList.size() - 1); for (int i = rexList.size() - 2; i >= 0; i--) { orExpr = rexBuilder.makeCall(SqlStdOperatorTable.orOperator, rexList.get(i), orExpr); } return orExpr; }
/** * Creates an AND expression from a list of RexNodes * * @param rexList list of RexNodes * @return AND'd expression */ public static RexNode andRexNodeList(RexBuilder rexBuilder, List<RexNode> rexList) { if (rexList.isEmpty()) { return null; } // create a right-deep tree to allow short-circuiting during // expression evaluation RexNode andExpr = rexList.get(rexList.size() - 1); for (int i = rexList.size() - 2; i >= 0; i--) { andExpr = rexBuilder.makeCall(SqlStdOperatorTable.andOperator, rexList.get(i), andExpr); } return andExpr; }
private void onMatchRight(RelOptRuleCall call) { final JoinRelBase topJoin = call.rel(0); final JoinRelBase bottomJoin = call.rel(1); final RelNode relC = call.rel(2); final RelNode relA = bottomJoin.getLeft(); final RelNode relB = bottomJoin.getRight(); final RelOptCluster cluster = topJoin.getCluster(); // topJoin // / \ // bottomJoin C // / \ // A B final int aCount = relA.getRowType().getFieldCount(); final int bCount = relB.getRowType().getFieldCount(); final int cCount = relC.getRowType().getFieldCount(); final BitSet bBitSet = BitSets.range(aCount, aCount + bCount); // becomes // // newTopJoin // / \ // newBottomJoin B // / \ // A C // If either join is not inner, we cannot proceed. // (Is this too strict?) if (topJoin.getJoinType() != JoinRelType.INNER || bottomJoin.getJoinType() != JoinRelType.INNER) { return; } // Split the condition of topJoin into a conjunction. Each of the // parts that does not use columns from B can be pushed down. final List<RexNode> intersecting = new ArrayList<RexNode>(); final List<RexNode> nonIntersecting = new ArrayList<RexNode>(); split(topJoin.getCondition(), bBitSet, intersecting, nonIntersecting); // If there's nothing to push down, it's not worth proceeding. if (nonIntersecting.isEmpty()) { return; } // Split the condition of bottomJoin into a conjunction. Each of the // parts that use columns from B will need to be pulled up. final List<RexNode> bottomIntersecting = new ArrayList<RexNode>(); final List<RexNode> bottomNonIntersecting = new ArrayList<RexNode>(); split(bottomJoin.getCondition(), bBitSet, bottomIntersecting, bottomNonIntersecting); // target: | A | C | // source: | A | B | C | final Mappings.TargetMapping bottomMapping = Mappings.createShiftMapping( aCount + bCount + cCount, 0, 0, aCount, aCount, aCount + bCount, cCount); List<RexNode> newBottomList = new ArrayList<RexNode>(); new RexPermuteInputsShuttle(bottomMapping, relA, relC) .visitList(nonIntersecting, newBottomList); final Mappings.TargetMapping bottomBottomMapping = Mappings.createShiftMapping(aCount + bCount, 0, 0, aCount); new RexPermuteInputsShuttle(bottomBottomMapping, relA, relC) .visitList(bottomNonIntersecting, newBottomList); final RexBuilder rexBuilder = cluster.getRexBuilder(); RexNode newBottomCondition = RexUtil.composeConjunction(rexBuilder, newBottomList, false); final JoinRelBase newBottomJoin = bottomJoin.copy( bottomJoin.getTraitSet(), newBottomCondition, relA, relC, bottomJoin.getJoinType()); // target: | A | C | B | // source: | A | B | C | final Mappings.TargetMapping topMapping = Mappings.createShiftMapping( aCount + bCount + cCount, 0, 0, aCount, aCount + cCount, aCount, bCount, aCount, aCount + bCount, cCount); List<RexNode> newTopList = new ArrayList<RexNode>(); new RexPermuteInputsShuttle(topMapping, newBottomJoin, relB) .visitList(intersecting, newTopList); new RexPermuteInputsShuttle(topMapping, newBottomJoin, relB) .visitList(bottomIntersecting, newTopList); RexNode newTopCondition = RexUtil.composeConjunction(rexBuilder, newTopList, false); @SuppressWarnings("SuspiciousNameCombination") final JoinRelBase newTopJoin = topJoin.copy( topJoin.getTraitSet(), newTopCondition, newBottomJoin, relB, topJoin.getJoinType()); assert !Mappings.isIdentity(topMapping); final RelNode newProject = RelFactories.createProject(projectFactory, newTopJoin, Mappings.asList(topMapping)); call.transformTo(newProject); }
/** Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link ProjectRel}. */ public TrimResult trimFields( ProjectRel project, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) { final RelDataType rowType = project.getRowType(); final int fieldCount = rowType.getFieldCount(); final RelNode input = project.getChild(); final RelDataType inputRowType = input.getRowType(); // Which fields are required from the input? BitSet inputFieldsUsed = new BitSet(inputRowType.getFieldCount()); final Set<RelDataTypeField> inputExtraFields = new LinkedHashSet<RelDataTypeField>(extraFields); RelOptUtil.InputFinder inputFinder = new RelOptUtil.InputFinder(inputFieldsUsed, inputExtraFields); for (Ord<RexNode> ord : Ord.zip(project.getProjects())) { if (fieldsUsed.get(ord.i)) { ord.e.accept(inputFinder); } } // Create input with trimmed columns. TrimResult trimResult = trimChild(project, 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(project, Mappings.createIdentity(fieldCount)); } // Some parts of the system can't handle rows with zero fields, so // pretend that one field is used. if (fieldsUsed.cardinality() == 0) { final Mapping mapping = Mappings.create(MappingType.InverseSurjection, fieldCount, 1); final RexLiteral expr = project.getCluster().getRexBuilder().makeExactLiteral(BigDecimal.ZERO); RelDataType newRowType = project .getCluster() .getTypeFactory() .createStructType( Collections.singletonList(expr.getType()), Collections.singletonList("DUMMY")); ProjectRel newProject = new ProjectRel( project.getCluster(), project.getCluster().traitSetOf(RelCollationImpl.EMPTY), newInput, Collections.<RexNode>singletonList(expr), newRowType, project.getFlags()); return new TrimResult(newProject, mapping); } // Build new project expressions, and populate the mapping. List<RexNode> newProjectExprList = new ArrayList<RexNode>(); final RexVisitor<RexNode> shuttle = new RexPermuteInputsShuttle(inputMapping, newInput); final Mapping mapping = Mappings.create(MappingType.InverseSurjection, fieldCount, fieldsUsed.cardinality()); for (Ord<RexNode> ord : Ord.zip(project.getProjects())) { if (fieldsUsed.get(ord.i)) { mapping.set(ord.i, newProjectExprList.size()); RexNode newProjectExpr = ord.e.accept(shuttle); newProjectExprList.add(newProjectExpr); } } final RelDataType newRowType = project .getCluster() .getTypeFactory() .createStructType(Mappings.apply3(mapping, rowType.getFieldList())); final List<RelCollation> newCollations = RexUtil.apply(inputMapping, project.getCollationList()); final RelNode newProject; if (RemoveTrivialProjectRule.isIdentity( newProjectExprList, newRowType, newInput.getRowType())) { // The new project would be the identity. It is equivalent to return // its child. newProject = newInput; } else { newProject = new ProjectRel( project.getCluster(), project .getCluster() .traitSetOf( newCollations.isEmpty() ? RelCollationImpl.EMPTY : newCollations.get(0)), newInput, newProjectExprList, newRowType, project.getFlags()); assert newProject.getClass() == project.getClass(); } return new TrimResult(newProject, mapping); }
/** * Reduces a list of expressions. * * @param rel Relational expression * @param expList List of expressions, modified in place * @return whether reduction found something to change, and succeeded */ static boolean reduceExpressions(RelNode rel, List<RexNode> expList) { RexBuilder rexBuilder = rel.getCluster().getRexBuilder(); // Find reducible expressions. FarragoSessionPlanner planner = (FarragoSessionPlanner) rel.getCluster().getPlanner(); FarragoSessionPreparingStmt preparingStmt = planner.getPreparingStmt(); List<RexNode> constExps = new ArrayList<RexNode>(); List<Boolean> addCasts = new ArrayList<Boolean>(); List<RexNode> removableCasts = new ArrayList<RexNode>(); findReducibleExps(preparingStmt, expList, constExps, addCasts, removableCasts); if (constExps.isEmpty() && removableCasts.isEmpty()) { return false; } // Remove redundant casts before reducing constant expressions. // If the argument to the redundant cast is a reducible constant, // reducing that argument to a constant first will result in not being // able to locate the original cast expression. if (!removableCasts.isEmpty()) { List<RexNode> reducedExprs = new ArrayList<RexNode>(); List<Boolean> noCasts = new ArrayList<Boolean>(); for (RexNode exp : removableCasts) { RexCall call = (RexCall) exp; reducedExprs.add(call.getOperands()[0]); noCasts.add(false); } RexReplacer replacer = new RexReplacer(rexBuilder, removableCasts, reducedExprs, noCasts); replacer.apply(expList); } if (constExps.isEmpty()) { return true; } // Compute the values they reduce to. List<RexNode> reducedValues = new ArrayList<RexNode>(); ReentrantValuesStmt reentrantStmt = new ReentrantValuesStmt( preparingStmt.getRootStmtContext(), rexBuilder, constExps, reducedValues); FarragoSession session = getSession(rel); reentrantStmt.execute(session, true); if (reentrantStmt.failed) { return false; } // For ProjectRel, we have to be sure to preserve the result // types, so always cast regardless of the expression type. // For other RelNodes like FilterRel, in general, this isn't necessary, // and the presence of casts could hinder other rules such as sarg // analysis, which require bare literals. But there are special cases, // like when the expression is a UDR argument, that need to be // handled as special cases. if (rel instanceof ProjectRel) { for (int i = 0; i < reducedValues.size(); i++) { addCasts.set(i, true); } } RexReplacer replacer = new RexReplacer(rexBuilder, constExps, reducedValues, addCasts); replacer.apply(expList); return true; }