/** * Combines the join filters from the left and right inputs (if they are MultiJoinRels) with the * join filter in the joinrel into a single AND'd join filter, unless the inputs correspond to * null generating inputs in an outer join * * @param joinRel join rel * @param left left child of the joinrel * @param right right child of the joinrel * @return combined join filters AND'd together */ private RexNode combineJoinFilters(JoinRel joinRel, RelNode left, RelNode right) { RexBuilder rexBuilder = joinRel.getCluster().getRexBuilder(); JoinRelType joinType = joinRel.getJoinType(); // first need to adjust the RexInputs of the right child, since // those need to shift over to the right RexNode rightFilter = null; if (canCombine(right, joinType.generatesNullsOnRight())) { MultiJoinRel multiJoin = (MultiJoinRel) right; rightFilter = shiftRightFilter(joinRel, left, multiJoin, multiJoin.getJoinFilter()); } // AND the join condition if this isn't a left or right outer join; // in those cases, the outer join condition is already tracked // separately RexNode newFilter = null; if ((joinType != JoinRelType.LEFT) && (joinType != JoinRelType.RIGHT)) { newFilter = joinRel.getCondition(); } if (canCombine(left, joinType.generatesNullsOnLeft())) { RexNode leftFilter = ((MultiJoinRel) left).getJoinFilter(); newFilter = RelOptUtil.andJoinFilters(rexBuilder, newFilter, leftFilter); } newFilter = RelOptUtil.andJoinFilters(rexBuilder, newFilter, rightFilter); return newFilter; }
/** * Converts a relational expression to a form where {@link org.eigenbase.rel.JoinRel}s are as * close to leaves as possible. */ public static RelNode toLeafJoinForm(RelNode rel) { final HepProgramBuilder programBuilder = new HepProgramBuilder(); programBuilder.addRuleInstance(PullUpProjectsAboveJoinRule.instanceRightProjectChild); programBuilder.addRuleInstance(PullUpProjectsAboveJoinRule.instanceLeftProjectChild); final HepPlanner planner = new HepPlanner(programBuilder.createProgram()); planner.setRoot(rel); System.out.println( RelOptUtil.dumpPlan("before", rel, false, SqlExplainLevel.DIGEST_ATTRIBUTES)); final RelNode rel2 = planner.findBestExp(); System.out.println( RelOptUtil.dumpPlan("after", rel2, false, SqlExplainLevel.DIGEST_ATTRIBUTES)); return rel2; }
public void onMatch(RelOptRuleCall call) { assert matches(call); final JoinRel join = (JoinRel) call.rels[0]; final List<Integer> leftKeys = new ArrayList<Integer>(); final List<Integer> rightKeys = new ArrayList<Integer>(); RelNode right = join.getRight(); final RelNode left = join.getLeft(); RexNode remainingCondition = RelOptUtil.splitJoinCondition(left, right, join.getCondition(), leftKeys, rightKeys); assert leftKeys.size() == rightKeys.size(); final List<CorrelatorRel.Correlation> correlationList = new ArrayList<CorrelatorRel.Correlation>(); if (leftKeys.size() > 0) { final RelOptCluster cluster = join.getCluster(); final RexBuilder rexBuilder = cluster.getRexBuilder(); int k = 0; RexNode condition = null; for (Integer leftKey : leftKeys) { Integer rightKey = rightKeys.get(k++); final String dyn_inIdStr = cluster.getQuery().createCorrel(); final int dyn_inId = RelOptQuery.getCorrelOrdinal(dyn_inIdStr); // Create correlation to say 'each row, set variable #id // to the value of column #leftKey'. correlationList.add(new CorrelatorRel.Correlation(dyn_inId, leftKey)); condition = RelOptUtil.andJoinFilters( rexBuilder, condition, rexBuilder.makeCall( SqlStdOperatorTable.equalsOperator, rexBuilder.makeInputRef( right.getRowType().getFieldList().get(rightKey).getType(), rightKey), rexBuilder.makeCorrel( left.getRowType().getFieldList().get(leftKey).getType(), dyn_inIdStr))); } right = CalcRel.createFilter(right, condition); } RelNode newRel = new CorrelatorRel( join.getCluster(), left, right, remainingCondition, correlationList, join.getJoinType()); call.transformTo(newRel); }
public void visit(RelNode rel, int ordinal, RelNode parent) { // REVIEW: SWZ: 1/31/06: We assume that any special RelNodes, such // as the VolcanoPlanner's RelSubset always have a full complement // of traits and that they either appear as registered or do nothing // when childrenAccept is called on them. if (planner.isRegistered(rel)) { return; } RelTraitSet relTraits = rel.getTraitSet(); for (int i = 0; i < baseTraits.size(); i++) { if (i >= relTraits.size()) { // Copy traits that the new rel doesn't know about. Util.discard(RelOptUtil.addTrait(rel, baseTraits.getTrait(i))); // FIXME: Return the new rel. We can no longer traits in-place, // because rels and traits are immutable. throw new AssertionError(); } else { // Verify that the traits are from the same RelTraitDef assert relTraits.getTrait(i).getTraitDef() == baseTraits.getTrait(i).getTraitDef(); } } rel.childrenAccept(this); }
public void onMatch(RelOptRuleCall call) { UnionRel union = call.rel(0); if (union.all) { return; // nothing to do } UnionRel unionAll = new UnionRel(union.getCluster(), union.getInputs(), true); call.transformTo(RelOptUtil.createDistinctRel(unionAll)); }
/** * Converts a relational expression to a form where {@link org.eigenbase.rel.JoinRel}s are as * close to leaves as possible. */ public static RelNode toLeafJoinForm(RelNode rel) { HepProgram program = HepProgram.builder() .addRuleInstance(PullUpProjectsAboveJoinRule.RIGHT_PROJECT) .addRuleInstance(PullUpProjectsAboveJoinRule.LEFT_PROJECT) .build(); final HepPlanner planner = new HepPlanner( program, // rel.getCluster().getPlanner().getContext()); planner.setRoot(rel); System.out.println( RelOptUtil.dumpPlan("before", rel, false, SqlExplainLevel.DIGEST_ATTRIBUTES)); final RelNode rel2 = planner.findBestExp(); System.out.println( RelOptUtil.dumpPlan("after", rel2, false, SqlExplainLevel.DIGEST_ATTRIBUTES)); return rel2; }
/** Splits a condition into conjunctions that do or do not intersect with a given bit set. */ static void split( RexNode condition, BitSet bitSet, List<RexNode> intersecting, List<RexNode> nonIntersecting) { for (RexNode node : RelOptUtil.conjunctions(condition)) { BitSet inputBitSet = RelOptUtil.InputFinder.bits(node); if (bitSet.intersects(inputBitSet)) { intersecting.add(node); } else { nonIntersecting.add(node); } } }
/** * Analyzes a rex predicate. * * @param rexPredicate predicate to be analyzed * @return a list of SargBindings contained in the input rex predicate */ public List<SargBinding> analyzeAll(RexNode rexPredicate) { sargBindingList = new ArrayList<SargBinding>(); sarg2RexMap = new HashMap<SargExpr, RexNode>(); nonSargFilterList = new ArrayList<RexNode>(); // Flatten out the RexNode tree into a list of terms that // are AND'ed together final List<RexNode> rexCFList = RelOptUtil.conjunctions(rexPredicate); // In simple mode, each input ref can only be referenced once, so // keep a list of them. We also only allow one non-point expression. List<Integer> boundRefList = new ArrayList<Integer>(); boolean rangeFound = false; for (RexNode rexPred : rexCFList) { final SargBinding sargBinding = analyze(rexPred); if (sargBinding != null) { if (simpleMode) { RexInputRef inputRef = sargBinding.getInputRef(); if (boundRefList.contains(inputRef.getIndex())) { nonSargFilterList.add(rexPred); continue; } else { boundRefList.add(inputRef.getIndex()); } SargIntervalSequence sargSeq = sargBinding.getExpr().evaluate(); if (sargSeq.isRange()) { if (rangeFound) { nonSargFilterList.add(rexPred); continue; } else { rangeFound = true; } } } sargBindingList.add(sargBinding); sarg2RexMap.put(sargBinding.getExpr(), rexPred); } else { nonSargFilterList.add(rexPred); } } // Reset the state variables used during analyze, just for sanity sake. failed = false; boundInputRef = null; clearLeaf(); // Combine the AND terms back together. recomposeConjunction(); return sargBindingList; }
/** * Creates new RelNodes replacing/removing the original project/row scan * * @param projectedScan new scan that is now projected * @param origProject original projection * @param needRename true if fields from the row scan need to be renamed * @param newProject projection that contains the new projection expressions, in the case where * the original projection cannot be removed because it projects expressions * @return new RelNode */ public RelNode createNewRelNode( RelNode projectedScan, ProjectRel origProject, boolean needRename, ProjectRel newProject) { RelNode scanRel; if (needRename) { // Replace calling convention with FENNEL_EXEC_CONVENTION RelTraitSet traits = RelOptUtil.clone(origProject.getTraits()); traits.setTrait(CallingConventionTraitDef.instance, FennelRel.FENNEL_EXEC_CONVENTION); if (!traits.equals(projectedScan.getTraits())) { RelNode mergedProjectedScan = convert(projectedScan, traits); RelOptPlanner planner = projectedScan.getCluster().getPlanner(); // register projectedScan == mergedProjectedScan // so mergedProjectedScan will have a set later on projectedScan = planner.ensureRegistered(mergedProjectedScan, projectedScan); } scanRel = new FennelRenameRel( origProject.getCluster(), projectedScan, RelOptUtil.getFieldNames(origProject.getRowType()), traits); } else { scanRel = projectedScan; } if (newProject == null) { return scanRel; } else { // in the case where the projection had expressions, put the // new, modified projection on top of the projected row scan return (ProjectRel) CalcRel.createProject( scanRel, newProject.getProjectExps(), RelOptUtil.getFieldNames(newProject.getRowType())); } }
/** * Returns whether the type of an array of expressions is compatible with a struct type. * * @param exprs Array of expressions * @param type Type * @param fail Whether to fail if there is a mismatch * @return Whether every expression has the same type as the corresponding member of the struct * type * @see RelOptUtil#eq(String, RelDataType, String, RelDataType, boolean) */ public static boolean compatibleTypes(RexNode[] exprs, RelDataType type, boolean fail) { final RelDataTypeField[] fields = type.getFields(); if (exprs.length != fields.length) { assert !fail : "rowtype mismatches expressions"; return false; } for (int i = 0; i < fields.length; i++) { final RelDataType exprType = exprs[i].getType(); final RelDataType fieldType = fields[i].getType(); if (!RelOptUtil.eq("type1", exprType, "type2", fieldType, fail)) { return false; } } return true; }
public OptiqPreparingStmt( CatalogReader catalogReader, RelDataTypeFactory typeFactory, Schema schema) { super(catalogReader); this.schema = schema; planner = new VolcanoPlanner(); planner.addRelTraitDef(CallingConventionTraitDef.instance); RelOptUtil.registerAbstractRels(planner); planner.addRule(JavaRules.ENUMERABLE_JOIN_RULE); planner.addRule(JavaRules.ENUMERABLE_CALC_RULE); planner.addRule(JavaRules.ENUMERABLE_AGGREGATE_RULE); planner.addRule(JavaRules.ENUMERABLE_SORT_RULE); planner.addRule(JavaRules.ENUMERABLE_UNION_RULE); planner.addRule(JavaRules.ENUMERABLE_INTERSECT_RULE); planner.addRule(JavaRules.ENUMERABLE_MINUS_RULE); planner.addRule(TableAccessRule.instance); rexBuilder = new RexBuilder(typeFactory); }
public void assertConvertsTo(String sql, String plan, boolean trim) { String sql2 = getDiffRepos().expand("sql", sql); RelNode rel = convertSqlToRel(sql2); assertTrue(rel != null); assertValid(rel); if (trim) { final RelFieldTrimmer trimmer = createFieldTrimmer(); rel = trimmer.trim(rel); assertTrue(rel != null); assertValid(rel); } // NOTE jvs 28-Mar-2006: insert leading newline so // that plans come out nicely stacked instead of first // line immediately after CDATA start String actual = NL + RelOptUtil.toString(rel); diffRepos.assertEquals("plan", plan, actual); }
/** * Combines the post-join filters from the left and right inputs (if they are MultiJoinRels) into * a single AND'd filter. * * @param joinRel the original JoinRel * @param left left child of the JoinRel * @param right right child of the JoinRel * @return combined post-join filters AND'd together */ private RexNode combinePostJoinFilters(JoinRel joinRel, RelNode left, RelNode right) { RexNode rightPostJoinFilter = null; if (right instanceof MultiJoinRel) { rightPostJoinFilter = shiftRightFilter( joinRel, left, (MultiJoinRel) right, ((MultiJoinRel) right).getPostJoinFilter()); } RexNode leftPostJoinFilter = null; if (left instanceof MultiJoinRel) { leftPostJoinFilter = ((MultiJoinRel) left).getPostJoinFilter(); } if ((leftPostJoinFilter == null) && (rightPostJoinFilter == null)) { return null; } else { return RelOptUtil.andJoinFilters( joinRel.getCluster().getRexBuilder(), leftPostJoinFilter, rightPostJoinFilter); } }
// implement RelOptRule public void onMatch(RelOptRuleCall call) { final FilterRelBase filterRel = call.rel(0); final ProjectRelBase projRel = call.rel(1); // convert the filter to one that references the child of the project RexNode newCondition = RelOptUtil.pushFilterPastProject(filterRel.getCondition(), projRel); RelNode newFilterRel = filterFactory == null ? filterRel.copy(filterRel.getTraitSet(), projRel.getChild(), newCondition) : filterFactory.createFilter(projRel.getChild(), newCondition); RelNode newProjRel = projectFactory == null ? projRel.copy( projRel.getTraitSet(), newFilterRel, projRel.getProjects(), projRel.getRowType()) : projectFactory.createProject( newFilterRel, projRel.getProjects(), projRel.getRowType().getFieldNames()); call.transformTo(newProjRel); }
/** * Returns whether the leading edge of a given array of expressions is wholly {@link RexInputRef} * objects with types corresponding to the underlying datatype. */ public static boolean containIdentity(RexNode[] exprs, RelDataType rowType, boolean fail) { final RelDataTypeField[] fields = rowType.getFields(); if (exprs.length < fields.length) { assert !fail : "exprs/rowType length mismatch"; return false; } for (int i = 0; i < fields.length; i++) { if (!(exprs[i] instanceof RexInputRef)) { assert !fail : "expr[" + i + "] is not a RexInputRef"; return false; } RexInputRef inputRef = (RexInputRef) exprs[i]; if (inputRef.getIndex() != i) { assert !fail : "expr[" + i + "] has ordinal " + inputRef.getIndex(); return false; } if (!RelOptUtil.eq("type1", exprs[i].getType(), "type2", fields[i].getType(), fail)) { return false; } } return true; }
public void onMatch(RelOptRuleCall call) { final TableAccessRel oldRel = call.rel(0); RelNode newRel = oldRel.getTable().toRel(RelOptUtil.getContext(oldRel.getCluster())); call.transformTo(newRel); }
// implement RelOptRuleCall public void transformTo(RelNode rel) { RelOptUtil.verifyTypeEquivalence(getRels()[0], rel, getRels()[0]); results.add(rel); }
public void onMatch(RelOptRuleCall call) { AggregateRel aggRel = call.rel(0); UnionRel unionRel = call.rel(1); if (!unionRel.all) { // This transformation is only valid for UNION ALL. // Consider t1(i) with rows (5), (5) and t2(i) with // rows (5), (10), and the query // select sum(i) from (select i from t1) union (select i from t2). // The correct answer is 15. If we apply the transformation, // we get // select sum(i) from // (select sum(i) as i from t1) union (select sum(i) as i from t2) // which yields 25 (incorrect). return; } RelOptCluster cluster = unionRel.getCluster(); List<AggregateCall> transformedAggCalls = transformAggCalls( aggRel.getCluster().getTypeFactory(), aggRel.getGroupSet().cardinality(), aggRel.getAggCallList()); if (transformedAggCalls == null) { // we've detected the presence of something like AVG, // which we can't handle return; } boolean anyTransformed = false; // create corresponding aggs on top of each union child List<RelNode> newUnionInputs = new ArrayList<RelNode>(); for (RelNode input : unionRel.getInputs()) { boolean alreadyUnique = RelMdUtil.areColumnsDefinitelyUnique(input, aggRel.getGroupSet()); if (alreadyUnique) { newUnionInputs.add(input); } else { anyTransformed = true; newUnionInputs.add( new AggregateRel(cluster, input, aggRel.getGroupSet(), aggRel.getAggCallList())); } } if (!anyTransformed) { // none of the children could benefit from the pushdown, // so bail out (preventing the infinite loop to which most // planners would succumb) return; } // create a new union whose children are the aggs created above UnionRel newUnionRel = new UnionRel(cluster, newUnionInputs, true); AggregateRel newTopAggRel = new AggregateRel(cluster, newUnionRel, aggRel.getGroupSet(), transformedAggCalls); // In case we transformed any COUNT (which is always NOT NULL) // to SUM (which is always NULLABLE), cast back to keep the // planner happy. RelNode castRel = RelOptUtil.createCastRel(newTopAggRel, aggRel.getRowType(), false); call.transformTo(castRel); }