private List<AggregateCall> transformAggCalls(
     RelDataTypeFactory typeFactory, int nGroupCols, List<AggregateCall> origCalls) {
   List<AggregateCall> newCalls = new ArrayList<AggregateCall>();
   int iInput = nGroupCols;
   for (AggregateCall origCall : origCalls) {
     if (origCall.isDistinct()
         || !SUPPORTED_AGGREGATES.containsKey(origCall.getAggregation().getClass())) {
       return null;
     }
     Aggregation aggFun;
     RelDataType aggType;
     if (origCall.getAggregation().getName().equals("COUNT")) {
       aggFun = new SqlSumEmptyIsZeroAggFunction(origCall.getType());
       SqlAggFunction af = (SqlAggFunction) aggFun;
       final AggregateRelBase.AggCallBinding binding =
           new AggregateRelBase.AggCallBinding(
               typeFactory, af, Collections.singletonList(origCall.getType()), nGroupCols);
       // count(any) is always not null, however nullability of sum might
       // depend on the number of columns in GROUP BY.
       // Here we use SUM0 since we are sure we will not face nullable
       // inputs nor we'll face empty set.
       aggType = af.inferReturnType(binding);
     } else {
       aggFun = origCall.getAggregation();
       aggType = origCall.getType();
     }
     AggregateCall newCall =
         new AggregateCall(
             aggFun,
             origCall.isDistinct(),
             Collections.singletonList(iInput),
             aggType,
             origCall.getName());
     newCalls.add(newCall);
     ++iInput;
   }
   return newCalls;
 }
Example #2
0
  /** Variant of {@link #trimFields(RelNode, BitSet, Set)} for {@link AggregateRel}. */
  public TrimResult trimFields(
      AggregateRel aggregate, BitSet fieldsUsed, Set<RelDataTypeField> extraFields) {
    // Fields:
    //
    // | sys fields | group fields | agg functions |
    //
    // Two kinds of trimming:
    //
    // 1. If agg rel has system fields but none of these are used, create an
    // agg rel with no system fields.
    //
    // 2. If aggregate functions are not used, remove them.
    //
    // But grouping fields stay, even if they are not used.

    final RelDataType rowType = aggregate.getRowType();

    // Compute which input fields are used.
    BitSet inputFieldsUsed = new BitSet();
    // 1. group fields are always used
    for (int i : Util.toIter(aggregate.getGroupSet())) {
      inputFieldsUsed.set(i);
    }
    // 2. agg functions
    for (AggregateCall aggCall : aggregate.getAggCallList()) {
      for (int i : aggCall.getArgList()) {
        inputFieldsUsed.set(i);
      }
    }

    // Create input with trimmed columns.
    final RelNode input = aggregate.getInput(0);
    final Set<RelDataTypeField> inputExtraFields = Collections.emptySet();
    final TrimResult trimResult = trimChild(aggregate, input, inputFieldsUsed, inputExtraFields);
    final RelNode newInput = trimResult.left;
    final Mapping inputMapping = trimResult.right;

    // If the input is unchanged, and we need to project all columns,
    // there's nothing to do.
    if (input == newInput && fieldsUsed.equals(Util.bitSetBetween(0, rowType.getFieldCount()))) {
      return new TrimResult(aggregate, Mappings.createIdentity(rowType.getFieldCount()));
    }

    // Which agg calls are used by our consumer?
    final int groupCount = aggregate.getGroupSet().cardinality();
    int j = groupCount;
    int usedAggCallCount = 0;
    for (int i = 0; i < aggregate.getAggCallList().size(); i++) {
      if (fieldsUsed.get(j++)) {
        ++usedAggCallCount;
      }
    }

    // Offset due to the number of system fields having changed.
    Mapping mapping =
        Mappings.create(
            MappingType.InverseSurjection, rowType.getFieldCount(), groupCount + usedAggCallCount);

    final BitSet newGroupSet = Mappings.apply(inputMapping, aggregate.getGroupSet());

    // Populate mapping of where to find the fields. System and grouping
    // fields first.
    for (IntPair pair : inputMapping) {
      if (pair.source < groupCount) {
        mapping.set(pair.source, pair.target);
      }
    }

    // Now create new agg calls, and populate mapping for them.
    final List<AggregateCall> newAggCallList = new ArrayList<AggregateCall>();
    j = groupCount;
    for (AggregateCall aggCall : aggregate.getAggCallList()) {
      if (fieldsUsed.get(j)) {
        AggregateCall newAggCall =
            new AggregateCall(
                aggCall.getAggregation(),
                aggCall.isDistinct(),
                Mappings.apply2(inputMapping, aggCall.getArgList()),
                aggCall.getType(),
                aggCall.getName());
        if (newAggCall.equals(aggCall)) {
          newAggCall = aggCall; // immutable -> canonize to save space
        }
        mapping.set(j, groupCount + newAggCallList.size());
        newAggCallList.add(newAggCall);
      }
      ++j;
    }

    RelNode newAggregate =
        new AggregateRel(aggregate.getCluster(), newInput, newGroupSet, newAggCallList);

    assert newAggregate.getClass() == aggregate.getClass();

    return new TrimResult(newAggregate, mapping);
  }