/** * 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; }
/** * Combines the outer join conditions and join types from the left and right join inputs. If the * join itself is either a left or right outer join, then the join condition corresponding to the * join is also set in the position corresponding to the null-generating input into the join. The * join type is also set. * * @param joinRel join rel * @param combinedInputs the combined inputs to the join * @param left left child of the joinrel * @param right right child of the joinrel * @param combinedConds the array containing the combined join conditions * @param joinTypes the array containing the combined join types * @return combined join filters AND'd together */ private RexNode[] combineOuterJoins( JoinRel joinRel, RelNode[] combinedInputs, RelNode left, RelNode right, RexNode[] combinedConds, JoinRelType[] joinTypes) { JoinRelType joinType = joinRel.getJoinType(); int nCombinedInputs = combinedInputs.length; boolean leftCombined = canCombine(left, joinType.generatesNullsOnLeft()); boolean rightCombined = canCombine(right, joinType.generatesNullsOnRight()); if (joinType == JoinRelType.LEFT) { if (leftCombined) { copyOuterJoinInfo((MultiJoinRel) left, combinedConds, joinTypes, 0, 0, null, null); } else { joinTypes[0] = JoinRelType.INNER; } combinedConds[nCombinedInputs - 1] = joinRel.getCondition(); joinTypes[nCombinedInputs - 1] = joinType; } else if (joinType == JoinRelType.RIGHT) { if (rightCombined) { copyOuterJoinInfo( (MultiJoinRel) right, combinedConds, joinTypes, 1, left.getRowType().getFieldCount(), right.getRowType().getFields(), joinRel.getRowType().getFields()); } else { joinTypes[nCombinedInputs - 1] = JoinRelType.INNER; } combinedConds[0] = joinRel.getCondition(); joinTypes[0] = joinType; } else { int nInputsLeft; if (leftCombined) { nInputsLeft = left.getInputs().length; copyOuterJoinInfo((MultiJoinRel) left, combinedConds, joinTypes, 0, 0, null, null); } else { nInputsLeft = 1; joinTypes[0] = JoinRelType.INNER; } if (rightCombined) { copyOuterJoinInfo( (MultiJoinRel) right, combinedConds, joinTypes, nInputsLeft, left.getRowType().getFieldCount(), right.getRowType().getFields(), joinRel.getRowType().getFields()); } else { joinTypes[nInputsLeft] = JoinRelType.INNER; } } return combinedConds; }
public boolean matches(RelOptRuleCall call) { JoinRel join = (JoinRel) call.rels[0]; switch (join.getJoinType()) { case INNER: case LEFT: return true; case FULL: case RIGHT: return false; default: throw Util.unexpected(join.getJoinType()); } }
/** * Combines the inputs into a JoinRel into an array of inputs. * * @param join original join * @param left left input into join * @param right right input into join * @param projFieldsList returns a list of the new combined projection fields * @param joinFieldRefCountsList returns a list of the new combined join field reference counts * @return combined left and right inputs in an array */ private RelNode[] combineInputs( JoinRel join, RelNode left, RelNode right, List<BitSet> projFieldsList, List<int[]> joinFieldRefCountsList) { // leave the null generating sides of an outer join intact; don't // pull up those children inputs into the array we're constructing int nInputs; int nInputsOnLeft; MultiJoinRel leftMultiJoin = null; JoinRelType joinType = join.getJoinType(); boolean combineLeft = canCombine(left, joinType.generatesNullsOnLeft()); if (combineLeft) { leftMultiJoin = (MultiJoinRel) left; nInputs = left.getInputs().length; nInputsOnLeft = nInputs; } else { nInputs = 1; nInputsOnLeft = 1; } MultiJoinRel rightMultiJoin = null; boolean combineRight = canCombine(right, joinType.generatesNullsOnRight()); if (combineRight) { rightMultiJoin = (MultiJoinRel) right; nInputs += right.getInputs().length; } else { nInputs += 1; } RelNode[] newInputs = new RelNode[nInputs]; int i = 0; if (combineLeft) { for (; i < left.getInputs().length; i++) { newInputs[i] = leftMultiJoin.getInput(i); projFieldsList.add(((MultiJoinRel) left).getProjFields()[i]); joinFieldRefCountsList.add(((MultiJoinRel) left).getJoinFieldRefCountsMap().get(i)); } } else { newInputs[0] = left; i = 1; projFieldsList.add(null); joinFieldRefCountsList.add(new int[left.getRowType().getFieldCount()]); } if (combineRight) { for (; i < nInputs; i++) { newInputs[i] = rightMultiJoin.getInput(i - nInputsOnLeft); projFieldsList.add(((MultiJoinRel) right).getProjFields()[i - nInputsOnLeft]); joinFieldRefCountsList.add( ((MultiJoinRel) right).getJoinFieldRefCountsMap().get(i - nInputsOnLeft)); } } else { newInputs[i] = right; projFieldsList.add(null); joinFieldRefCountsList.add(new int[right.getRowType().getFieldCount()]); } return newInputs; }
/** * Shifts a filter originating from the right child of the JoinRel to the right, to reflect the * filter now being applied on the resulting MultiJoinRel. * * @param joinRel the original JoinRel * @param left the left child of the JoinRel * @param right the right child of the JoinRel * @param rightFilter the filter originating from the right child * @return the adjusted right filter */ private RexNode shiftRightFilter( JoinRel joinRel, RelNode left, MultiJoinRel right, RexNode rightFilter) { if (rightFilter == null) { return null; } int nFieldsOnLeft = left.getRowType().getFields().length; int nFieldsOnRight = right.getRowType().getFields().length; int[] adjustments = new int[nFieldsOnRight]; for (int i = 0; i < nFieldsOnRight; i++) { adjustments[i] = nFieldsOnLeft; } rightFilter = rightFilter.accept( new RelOptUtil.RexInputConverter( joinRel.getCluster().getRexBuilder(), right.getRowType().getFields(), joinRel.getRowType().getFields(), adjustments)); return rightFilter; }
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 onMatch(RelOptRuleCall call) { JoinRel origJoinRel = (JoinRel) call.rels[0]; RelNode left = call.rels[1]; RelNode right = call.rels[2]; // combine the children MultiJoinRel inputs into an array of inputs // for the new MultiJoinRel List<BitSet> projFieldsList = new ArrayList<BitSet>(); List<int[]> joinFieldRefCountsList = new ArrayList<int[]>(); RelNode[] newInputs = combineInputs(origJoinRel, left, right, projFieldsList, joinFieldRefCountsList); // combine the outer join information from the left and right // inputs, and include the outer join information from the current // join, if it's a left/right outer join RexNode[] newOuterJoinConds = new RexNode[newInputs.length]; JoinRelType[] joinTypes = new JoinRelType[newInputs.length]; combineOuterJoins(origJoinRel, newInputs, left, right, newOuterJoinConds, joinTypes); // pull up the join filters from the children MultiJoinRels and // combine them with the join filter associated with this JoinRel to // form the join filter for the new MultiJoinRel RexNode newJoinFilter = combineJoinFilters(origJoinRel, left, right); // add on the join field reference counts for the join condition // associated with this JoinRel Map<Integer, int[]> newJoinFieldRefCountsMap = new HashMap<Integer, int[]>(); addOnJoinFieldRefCounts( newInputs, origJoinRel.getRowType().getFieldCount(), origJoinRel.getCondition(), joinFieldRefCountsList, newJoinFieldRefCountsMap); RexNode newPostJoinFilter = combinePostJoinFilters(origJoinRel, left, right); RelNode multiJoin = new MultiJoinRel( origJoinRel.getCluster(), newInputs, newJoinFilter, origJoinRel.getRowType(), (origJoinRel.getJoinType() == JoinRelType.FULL), newOuterJoinConds, joinTypes, projFieldsList.toArray(new BitSet[projFieldsList.size()]), newJoinFieldRefCountsMap, newPostJoinFilter); call.transformTo(multiJoin); }
public void onMatch(RelOptRuleCall call) { JoinRel join = (JoinRel) call.rels[0]; List<RexNode> expList = new ArrayList<RexNode>(Arrays.asList(join.getChildExps())); if (reduceExpressions(join, expList)) { call.transformTo( new JoinRel( join.getCluster(), join.getLeft(), join.getRight(), expList.get(0), join.getJoinType(), join.getVariablesStopped())); // New plan is absolutely better than old plan. call.getPlanner().setImportance(join, 0.0); } }
/** * 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) { ProjectRel origProj = call.rel(0); JoinRel joinRel = call.rel(1); // locate all fields referenced in the projection and join condition; // determine which inputs are referenced in the projection and // join condition; if all fields are being referenced and there are no // special expressions, no point in proceeding any further PushProjector pushProject = new PushProjector(origProj, joinRel.getCondition(), joinRel, preserveExprCondition); if (pushProject.locateAllRefs()) { return; } // create left and right projections, projecting only those // fields referenced on each side RelNode leftProjRel = pushProject.createProjectRefsAndExprs(joinRel.getLeft(), true, false); RelNode rightProjRel = pushProject.createProjectRefsAndExprs(joinRel.getRight(), true, true); // convert the join condition to reference the projected columns RexNode newJoinFilter = null; int[] adjustments = pushProject.getAdjustments(); if (joinRel.getCondition() != null) { List<RelDataTypeField> projJoinFieldList = new ArrayList<RelDataTypeField>(); projJoinFieldList.addAll(joinRel.getSystemFieldList()); projJoinFieldList.addAll(leftProjRel.getRowType().getFieldList()); projJoinFieldList.addAll(rightProjRel.getRowType().getFieldList()); newJoinFilter = pushProject.convertRefsAndExprs(joinRel.getCondition(), projJoinFieldList, adjustments); } // create a new joinrel with the projected children JoinRel newJoinRel = new JoinRel( joinRel.getCluster(), leftProjRel, rightProjRel, newJoinFilter, joinRel.getJoinType(), Collections.<String>emptySet(), joinRel.isSemiJoinDone(), joinRel.getSystemFieldList()); // put the original project on top of the join, converting it to // reference the modified projection list ProjectRel topProject = pushProject.createNewProject(newJoinRel, adjustments); call.transformTo(topProject); }
/** 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); }