Source:
<code>public Optional<PlanNode> apply(PlanNode node, Context context) { AggregationNode aggregationNode = (AggregationNode) node; PlanNode childNode = context.getLookup().resolve(aggregationNode.getSource()); if (!(childNode instanceof ExchangeNode)) { return Optional.empty(); } ExchangeNode exchangeNode = (ExchangeNode) childNode; <START> <END> boolean decomposable = aggregationNode.isDecomposable(functionRegistry); if (aggregationNode.getStep().equals(SINGLE) && aggregationNode.hasEmptyGroupingSet() && aggregationNode.hasNonEmptyGroupingSet()) { checkState( decomposable, "Distributed aggregation with empty grouping set requires partial but functions are not decomposable"); return Optional.of(split(aggregationNode, context)); } if (!decomposable) { return Optional.empty(); } if ((exchangeNode.getType() != GATHER && exchangeNode.getType() != REPARTITION) || exchangeNode.getPartitioningScheme().isReplicateNullsAndAny()) { return Optional.empty(); } if (exchangeNode.getType() == REPARTITION) { List<Symbol> partitioningColumns = exchangeNode.getPartitioningScheme() .getPartitioning() .getArguments() .stream() .filter(Partitioning.ArgumentBinding::isVariable) .map(Partitioning.ArgumentBinding::getColumn) .collect(Collectors.toList()); if (!aggregationNode.getGroupingKeys().containsAll(partitioningColumns)) { return Optional.empty(); } } if (aggregationNode.getHashSymbol().isPresent() || exchangeNode.getPartitioningScheme().getHashColumn().isPresent()) { return Optional.empty(); } switch (aggregationNode.getStep()) { case SINGLE: return Optional.of(split(aggregationNode, context)); case PARTIAL: return Optional.of(pushPartial(aggregationNode, exchangeNode, context)); default: return Optional.empty(); } }</code><technical_language>IMO some UDF decomposable definition, this check this situaiton is supported</technical_language>

Target:
public Optional<PlanNode> apply(PlanNode node, Context context) { AggregationNode aggregationNode = (AggregationNode) node; boolean decomposable = aggregationNode.isDecomposable(functionRegistry); if (aggregationNode.getStep().equals(SINGLE) && aggregationNode.hasEmptyGroupingSet() && aggregationNode.hasNonEmptyGroupingSet()) { checkState( decomposable, "Distributed aggregation with empty grouping set requires partial but functions are not decomposable"); return Optional.of(split(aggregationNode, context)); } if (!decomposable) { return Optional.empty(); } PlanNode childNode = context.getLookup().resolve(aggregationNode.getSource()); if (!(childNode instanceof ExchangeNode)) { return Optional.empty(); } ExchangeNode exchangeNode = (ExchangeNode) childNode; if ((exchangeNode.getType() != GATHER && exchangeNode.getType() != REPARTITION) || exchangeNode.getPartitioningScheme().isReplicateNullsAndAny()) { return Optional.empty(); } if (exchangeNode.getType() == REPARTITION) { List<Symbol> partitioningColumns = exchangeNode.getPartitioningScheme() .getPartitioning() .getArguments() .stream() .filter(Partitioning.ArgumentBinding::isVariable) .map(Partitioning.ArgumentBinding::getColumn) .collect(Collectors.toList()); if (!aggregationNode.getGroupingKeys().containsAll(partitioningColumns)) { return Optional.empty(); } } if (aggregationNode.getHashSymbol().isPresent() || exchangeNode.getPartitioningScheme().getHashColumn().isPresent()) { return Optional.empty(); } switch (aggregationNode.getStep()) { case SINGLE: return Optional.of(split(aggregationNode, context)); case PARTIAL: return Optional.of(pushPartial(aggregationNode, exchangeNode, context)); default: return Optional.empty(); } }