@Override
  public void onMatch(RelOptRuleCall call) {
    final Aggregate aggregate = call.rel(0);
    final RelBuilder relBuilder = call.builder();

    // If any aggregate is distinct, bail out
    // If any aggregate is the grouping id, bail out
    // If any aggregate call has a filter, bail out
    // If any aggregate functions do not support splitting, bail out
    final ImmutableBitSet bottomAggregateGroupSet = aggregate.getGroupSet();
    final List<AggregateCall> topAggregateCalls = new ArrayList<>();
    for (int i = 0; i < aggregate.getAggCallList().size(); i++) {
      AggregateCall aggregateCall = aggregate.getAggCallList().get(i);
      if (aggregateCall.isDistinct()) {
        return;
      }
      if (aggregateCall.getAggregation().equals(HiveGroupingID.INSTANCE)) {
        return;
      }
      if (aggregateCall.filterArg >= 0) {
        return;
      }
      SqlAggFunction aggFunction = aggregateCall.getAggregation().getRollup();
      if (aggFunction == null) {
        return;
      }
      topAggregateCalls.add(
          AggregateCall.create(aggFunction,
              aggregateCall.isDistinct(), aggregateCall.isApproximate(),
              ImmutableList.of(bottomAggregateGroupSet.cardinality() + i), -1,
              aggregateCall.collation, aggregateCall.type,
              aggregateCall.name));
    }

    final Boolean isUnique =
        aggregate.getCluster().getMetadataQuery().areColumnsUnique(aggregate.getInput(), bottomAggregateGroupSet);
    if (isUnique != null && isUnique) {
      // Nothing to do, probably already pushed
      return;
    }

    final ImmutableBitSet topAggregateGroupSet = ImmutableBitSet.range(0, bottomAggregateGroupSet.cardinality());

    final Map<Integer, Integer> map = new HashMap<>();
    bottomAggregateGroupSet.forEach(k -> map.put(k, map.size()));
    ImmutableList<ImmutableBitSet> topAggregateGroupSets = ImmutableBitSet.ORDERING.immutableSortedCopy(
        ImmutableBitSet.permute(aggregate.groupSets, map));

    relBuilder.push(aggregate.getInput())
        .aggregate(relBuilder.groupKey(bottomAggregateGroupSet), aggregate.getAggCallList())
        .aggregate(relBuilder.groupKey(topAggregateGroupSet, topAggregateGroupSets), topAggregateCalls);

    call.transformTo(relBuilder.build());
  }
Rule that matches an aggregate with grouping sets and splits it into an aggregate
without grouping sets (bottom) and an aggregate with grouping sets (top).
ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/rules/HiveAggregateSplitRule.java