Beispiel #1
0
  /**
   * 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;
  }
Beispiel #2
0
  /**
   * 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;
  }
Beispiel #3
0
  /**
   * 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;
  }
Beispiel #4
0
  /**
   * 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;
  }
Beispiel #5
0
  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);
          }
        }
Beispiel #7
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);
  }
Beispiel #9
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);
  }