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); }
/** * 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; }
/** * 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 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); }
/** * 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) { 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); }