/**
   * 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;
  }
Exemple #4
0
  /** 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);
  }