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