/** * 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; }
/** * Copies outer join data from a source MultiJoinRel to a new set of arrays. Also adjusts the * conditions to reflect the new position of an input if that input ends up being shifted to the * right. * * @param multiJoinRel the source MultiJoinRel * @param destConds the array where the join conditions will be copied * @param destJoinTypes the array where the join types will be copied * @param destPos starting position in the array where the copying starts * @param adjustmentAmount if > 0, the amount the RexInputRefs in the join conditions need to be * adjusted by * @param srcFields the source fields that the original join conditions are referencing * @param destFields the destination fields that the new join conditions will be referencing */ private void copyOuterJoinInfo( MultiJoinRel multiJoinRel, RexNode[] destConds, JoinRelType[] destJoinTypes, int destPos, int adjustmentAmount, RelDataTypeField[] srcFields, RelDataTypeField[] destFields) { RexNode[] srcConds = multiJoinRel.getOuterJoinConditions(); JoinRelType[] srcJoinTypes = multiJoinRel.getJoinTypes(); RexBuilder rexBuilder = multiJoinRel.getCluster().getRexBuilder(); int len = srcConds.length; System.arraycopy(srcJoinTypes, 0, destJoinTypes, destPos, len); if (adjustmentAmount == 0) { System.arraycopy(srcConds, 0, destConds, 0, len); } else { int nFields = srcFields.length; int[] adjustments = new int[nFields]; for (int idx = 0; idx < nFields; idx++) { adjustments[idx] = adjustmentAmount; } for (int i = 0; i < len; i++) { if (srcConds[i] != null) { destConds[i + destPos] = srcConds[i].accept( new RelOptUtil.RexInputConverter(rexBuilder, srcFields, destFields, adjustments)); } } } }
/** * 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; }
// implement RelOptRule public void onMatch(RelOptRuleCall call) { FilterRel filterRel = call.rel(0); MultiJoinRel multiJoinRel = call.rel(1); MultiJoinRel newMultiJoinRel = new MultiJoinRel( multiJoinRel.getCluster(), multiJoinRel.getInputs(), multiJoinRel.getJoinFilter(), multiJoinRel.getRowType(), multiJoinRel.isFullOuterJoin(), multiJoinRel.getOuterJoinConditions(), multiJoinRel.getJoinTypes(), multiJoinRel.getProjFields(), multiJoinRel.getJoinFieldRefCountsMap(), filterRel.getCondition()); call.transformTo(newMultiJoinRel); }
/** * 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; }