docs: public void onMatch(RelOptRuleCall call) {
  		final Aggregate aggregate = call.rel(0);
  		if (!AggregateUtil.containsAccurateDistinctCall(aggregate.getAggCallList())) {
  			return;
  		}
  		// Check unsupported aggregate which contains both approximate distinct call and
  		// accurate distinct call.
  		if (AggregateUtil.containsApproximateDistinctCall(aggregate.getAggCallList())) {
  			throw new TableException(
  					"There are both Distinct AggCall and Approximate Distinct AggCall in one sql statement, " +
  							"it is not supported yet.\nPlease choose one of them.");
  		}
  
  		// If this aggregate is a non-simple aggregate(e.g. CUBE, ROLLUP)
  		// and contains distinct calls, it should be transformed to simple aggregate first
  		// by DecomposeGroupingSetsRule. Then this rule expands it's distinct aggregates.
  		if (aggregate.getGroupSets().size() > 1) {
  			return;
  		}
  
  		// Find all of the agg expressions. We use a LinkedHashSet to ensure determinism.
  		int nonDistinctAggCallCount = 0;  // find all aggregate calls without distinct
  		int filterCount = 0;
  		int unsupportedNonDistinctAggCallCount = 0;
  		final Set<Pair<List<Integer>, Integer>> argLists = new LinkedHashSet<>();
  		for (AggregateCall aggCall : aggregate.getAggCallList()) {
  			if (aggCall.filterArg >= 0) {
  				++filterCount;
  			}
  			if (!aggCall.isDistinct()) {
  				++nonDistinctAggCallCount;
  				final SqlKind aggCallKind = aggCall.getAggregation().getKind();
  				// We only support COUNT/SUM/MIN/MAX for the "single" count distinct optimization
  				switch (aggCallKind) {
  					case COUNT:
  					case SUM:
  					case SUM0:
  					case MIN:
  					case MAX:
  						break;
  					default:
  						++unsupportedNonDistinctAggCallCount;
  				}
  			} else {
  				argLists.add(Pair.of(aggCall.getArgList(), aggCall.filterArg));
  			}
  		}
  
  		final int distinctAggCallCount =
  				aggregate.getAggCallList().size() - nonDistinctAggCallCount;
  		Preconditions.checkState(argLists.size() > 0, "containsDistinctCall lied");
  
  		// If all of the agg expressions are distinct and have the same
  		// arguments then we can use a more efficient form.
  		if (nonDistinctAggCallCount == 0
  				&& argLists.size() == 1
  				&& aggregate.getGroupType() == Group.SIMPLE) {
  			final Pair<List<Integer>, Integer> pair =
  					com.google.common.collect.Iterables.getOnlyElement(argLists);
  			final RelBuilder relBuilder = call.builder();
  			convertMonopole(relBuilder, aggregate, pair.left, pair.right);
  			call.transformTo(relBuilder.build());
  			return;
  		}
  
  		if (useGroupingSets) {
  			rewriteUsingGroupingSets(call, aggregate);
  			return;
  		}
  
  		// If only one distinct aggregate and one or more non-distinct aggregates,
  		// we can generate multi-phase aggregates
  		if (distinctAggCallCount == 1 // one distinct aggregate
  				&& filterCount == 0 // no filter
  				&& unsupportedNonDistinctAggCallCount == 0 // sum/min/max/count in non-distinct aggregate
  				&& nonDistinctAggCallCount > 0) { // one or more non-distinct aggregates
  			final RelBuilder relBuilder = call.builder();
  			convertSingletonDistinct(relBuilder, aggregate, argLists);
  			call.transformTo(relBuilder.build());
  			return;
  		}
  
  		// Create a list of the expressions which will yield the final result.
  		// Initially, the expressions point to the input field.
  		final List<RelDataTypeField> aggFields =
  				aggregate.getRowType().getFieldList();
  		final List<RexInputRef> refs = new ArrayList<>();
  		final List<String> fieldNames = aggregate.getRowType().getFieldNames();
  		final ImmutableBitSet groupSet = aggregate.getGroupSet();
  		final int groupAndIndicatorCount =
  				aggregate.getGroupCount() + aggregate.getIndicatorCount();
  		for (int i : Util.range(groupAndIndicatorCount)) {
  			refs.add(RexInputRef.of(i, aggFields));
  		}
  
  		// Aggregate the original relation, including any non-distinct aggregates.
  		final List<AggregateCall> newAggCallList = new ArrayList<>();
  		int i = -1;
  		for (AggregateCall aggCall : aggregate.getAggCallList()) {
  			++i;
  			if (aggCall.isDistinct()) {
  				refs.add(null);
  				continue;
  			}
  			refs.add(
  					new RexInputRef(
  							groupAndIndicatorCount + newAggCallList.size(),
  							aggFields.get(groupAndIndicatorCount + i).getType()));
  			newAggCallList.add(aggCall);
  		}
  
  		// In the case where there are no non-distinct aggregates (regardless of
  		// whether there are group bys), there's no need to generate the
  		// extra aggregate and join.
  		final RelBuilder relBuilder = call.builder();
  		relBuilder.push(aggregate.getInput());
  		int n = 0;
  		if (!newAggCallList.isEmpty()) {
  			final RelBuilder.GroupKey groupKey =
  					relBuilder.groupKey(groupSet, aggregate.getGroupSets());
  			relBuilder.aggregate(groupKey, newAggCallList);
  			++n;
  		}
  
  		// For each set of operands, find and rewrite all calls which have that
  		// set of operands.
  		for (Pair<List<Integer>, Integer> argList : argLists) {
  			doRewrite(relBuilder, aggregate, n++, argList.left, argList.right, refs);
  		}
  
  		relBuilder.project(refs, fieldNames);
  		call.transformTo(relBuilder.build());
  	}