[ASTERIXDB-2483][COMP][FUN] Eliminate listify for distinct aggregates

- user model changes: no
- storage format changes: no
- interface changes: no

Details:
- Move distinct aggregate rewriting from SqlppQueryRewriter
  to RewriteDistinctAggregateRule in the optimizer
- Add runtime for scalar distinct aggregates
- Fix ExtractCommonOperatorsRule handling of binary operators
- Additional tests for distinct aggregates

Change-Id: If13ea2696e9e0a8a639db684656e5642991c1f99
Reviewed-on: https://asterix-gerrit.ics.uci.edu/3293
Reviewed-by: Ali Alsuliman <ali.al.solaiman@gmail.com>
Tested-by: Dmitry Lychagin <dmitry.lychagin@couchbase.com>
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java
index c116b2a..f42f0d5 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java
@@ -75,6 +75,7 @@
 import org.apache.asterix.optimizer.rules.RemoveRedundantSelectRule;
 import org.apache.asterix.optimizer.rules.RemoveSortInFeedIngestionRule;
 import org.apache.asterix.optimizer.rules.RemoveUnusedOneToOneEquiJoinRule;
+import org.apache.asterix.optimizer.rules.RewriteDistinctAggregateRule;
 import org.apache.asterix.optimizer.rules.SetAsterixPhysicalOperatorsRule;
 import org.apache.asterix.optimizer.rules.SetClosedRecordConstructorsRule;
 import org.apache.asterix.optimizer.rules.SetupCommitExtensionOpRule;
@@ -291,6 +292,8 @@
         consolidation.add(new ConsolidateSelectsRule());
         consolidation.add(new ConsolidateAssignsRule());
         consolidation.add(new InlineAssignIntoAggregateRule());
+        consolidation.add(new RewriteDistinctAggregateRule());
+        // The following rule should run after RewriteDistinctAggregateRule
         consolidation.add(new AsterixIntroduceGroupByCombinerRule());
         consolidation.add(new IntroduceAggregateCombinerRule());
         // Re-infer all types after introducing aggregate combiners
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/RewriteDistinctAggregateRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/RewriteDistinctAggregateRule.java
new file mode 100644
index 0000000..92b1ab6
--- /dev/null
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/RewriteDistinctAggregateRule.java
@@ -0,0 +1,340 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.optimizer.rules;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.util.container.IObjectPool;
+import org.apache.asterix.om.util.container.ListObjectPool;
+import org.apache.asterix.om.util.container.ObjectFactories;
+import org.apache.commons.lang3.mutable.Mutable;
+import org.apache.commons.lang3.mutable.MutableObject;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
+import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalExpressionTag;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalOperatorTag;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalVariable;
+import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
+import org.apache.hyracks.algebricks.core.algebra.expressions.ConstantExpression;
+import org.apache.hyracks.algebricks.core.algebra.expressions.VariableReferenceExpression;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.AggregateOperator;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.AssignOperator;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.DistinctOperator;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.GroupByOperator;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.InnerJoinOperator;
+import org.apache.hyracks.algebricks.core.algebra.plan.ALogicalPlanImpl;
+import org.apache.hyracks.algebricks.core.algebra.util.OperatorManipulationUtil;
+import org.apache.hyracks.algebricks.core.rewriter.base.IAlgebraicRewriteRule;
+
+/**
+ * This rule rewrites agg-distinct() function calls inside AGGREGATE operator into into regular agg() function calls
+ * over DISTINCT operator. <br/>
+ * In the simplest case:
+ * <pre>
+ * AGGREGATE [$x] <- [agg-distinct($y)]
+ * INPUT_OP
+ * --->
+ * AGGREGATE [$x] <- [agg($y)]
+ * DISTINCT [$y]
+ * INPUT_OP
+ * </pre>
+ *
+ * In the general case a single AGGREGATE operator could contain regular aggregate function calls and agg-distinct()
+ * function calls that work on different arguments:
+ * <pre>
+ * AGGREGATE [...] <- [avg($a), sum($b), avg-distinct($c), sum-distinct($c), avg-distinct($d), sum-distinct($d)]
+ * </pre>
+ * In this case distinct aggregate function calls are divided into groups based on their arguments, with one additional
+ * group introduced for all non-distinct aggregates. Each distinct aggregate is the rewritten into regular aggregate
+ * over DISTINCT operator as described above. Then all groups are combined together as follows.
+ * If the original AGGREGATE operator was in a nested plan of a GROUPBY operator then each aggregate group becomes
+ * a separate nested plan, otherwise (if the AGGREGATE operator was not inside GROUPBY) these groups are joined
+ * together by a trivial join:
+ *
+ * <pre>
+ * 1. AGGREGATE inside GROUPBY:
+ *
+ *   GROUPBY (
+ *     {
+ *       AGGREGATE [...] <- [avg($a), sum($b), avg-distinct($c), sum-distinct($c), avg-distinct($d), sum-distinct($d)]
+ *       NESTED_TUPLE_SOURCE
+ *     }
+ *   )
+ *   -->
+ *   GROUPBY (
+ *     {
+ *       AGGREGATE [...] <- [avg($a), sum($b)]
+ *       NESTED_TUPLE_SOURCE
+ *     },
+ *     {
+ *       AGGREGATE [...] <- [avg($c), sum($c)]
+ *       DISTINCT [$c]
+ *       NESTED_TUPLE_SOURCE
+ *     },
+ *     {
+ *       AGGREGATE [...] <- [avg($d), sum($d)]
+ *       DISTINCT [$d]
+ *       NESTED_TUPLE_SOURCE
+ *     }
+ *   )
+ *
+ *  2. AGGREGATE not inside GROUPBY:
+ *
+ *    AGGREGATE [...] <- [avg($a), sum($b), avg-distinct($c), sum-distinct($c), avg-distinct($d), sum-distinct($d)]
+ *    INPUT_OP
+ *    -->
+ *    JOIN (TRUE) {
+ *      JOIN (TRUE) {
+ *        AGGREGATE [...] <- [avg($a), sum($b)]
+ *          INPUT_OP
+ *        AGGREGATE [...] <- [avg($c), sum($c)]
+ *          DISTINCT [$c]
+ *           INPUT_OP
+ *      },
+ *      AGGREGATE [...] <- [avg($d), sum($d)]
+ *        DISTINCT [$d]
+ *          INPUT_OP
+ *    }
+ *
+ *    Note that in this case the input operator (INPUT_OP) is cloned for each aggregate group.
+ * </pre>
+ *
+ * Notes:
+ * <ul>
+ * <li>
+ * If distinct aggregate function argument is not a variable reference expression then an additional ASSIGN
+ * operator will be introduced below the DISTINCT</li>
+ * <li>
+ * This rule must run before {@link AsterixIntroduceGroupByCombinerRule} because distinct aggregates must be converted
+ * into regular before the local/global split.
+ * </li>
+ * <li>
+ * Scalar distinct aggregate function calls are not handled by this rule. If they were not rewritten into regular
+ * aggregates then they will be evaluated at runtime.
+ * </li>
+ * </ul>
+ */
+public final class RewriteDistinctAggregateRule implements IAlgebraicRewriteRule {
+
+    private final IObjectPool<BitSet, Void> bitSetAllocator = new ListObjectPool<>(ObjectFactories.BIT_SET_FACTORY);
+
+    private final Map<ILogicalExpression, BitSet> aggGroups = new LinkedHashMap<>();
+
+    private final List<AggregateOperator> newAggOps = new ArrayList<>();
+
+    @Override
+    public boolean rewritePre(Mutable<ILogicalOperator> opRef, IOptimizationContext context)
+            throws AlgebricksException {
+        ILogicalOperator op = opRef.getValue();
+        switch (op.getOperatorTag()) {
+            case AGGREGATE:
+                if (context.checkIfInDontApplySet(this, op)) {
+                    return false;
+                }
+                newAggOps.clear();
+                if (rewriteAggregate((AggregateOperator) op, newAggOps, context)) {
+                    ILogicalOperator newOp = join(newAggOps, context);
+                    opRef.setValue(newOp);
+                    return true;
+                }
+                return false;
+
+            case GROUP:
+                if (context.checkIfInDontApplySet(this, op)) {
+                    return false;
+                }
+                GroupByOperator gbyOp = (GroupByOperator) op;
+                List<ILogicalPlan> nestedPlans = gbyOp.getNestedPlans();
+                boolean applied = false;
+                for (int i = nestedPlans.size() - 1; i >= 0; i--) {
+                    ILogicalPlan nestedPlan = nestedPlans.get(i);
+                    for (Mutable<ILogicalOperator> rootOpRef : nestedPlan.getRoots()) {
+                        ILogicalOperator rootOp = rootOpRef.getValue();
+                        if (rootOp.getOperatorTag() == LogicalOperatorTag.AGGREGATE) {
+                            newAggOps.clear();
+                            if (rewriteAggregate((AggregateOperator) rootOp, newAggOps, context)) {
+                                applied = true;
+                                rootOpRef.setValue(newAggOps.get(0));
+                                for (int j = 1, ln = newAggOps.size(); j < ln; j++) {
+                                    nestedPlans.add(new ALogicalPlanImpl(new MutableObject<>(newAggOps.get(j))));
+                                }
+                            }
+                        }
+                    }
+                }
+                if (applied) {
+                    context.computeAndSetTypeEnvironmentForOperator(gbyOp);
+                    context.addToDontApplySet(this, gbyOp);
+                    return true;
+                }
+                return false;
+
+            default:
+                return false;
+        }
+    }
+
+    private ILogicalOperator join(List<? extends ILogicalOperator> ops, IOptimizationContext context)
+            throws AlgebricksException {
+        ILogicalOperator resultOp = ops.get(0);
+        for (int i = 1, n = ops.size(); i < n; i++) {
+            ILogicalOperator op = ops.get(i);
+            InnerJoinOperator joinOp = new InnerJoinOperator(new MutableObject<>(ConstantExpression.TRUE));
+            joinOp.setSourceLocation(resultOp.getSourceLocation());
+            joinOp.getInputs().add(new MutableObject<>(resultOp));
+            joinOp.getInputs().add(new MutableObject<>(op));
+            context.computeAndSetTypeEnvironmentForOperator(joinOp);
+            resultOp = joinOp;
+        }
+        return resultOp;
+    }
+
+    private boolean rewriteAggregate(AggregateOperator aggOp, List<AggregateOperator> outAggOps,
+            IOptimizationContext context) throws AlgebricksException {
+        aggGroups.clear();
+        bitSetAllocator.reset();
+
+        List<Mutable<ILogicalExpression>> aggExprs = aggOp.getExpressions();
+        int aggCount = aggExprs.size();
+        int distinctAggCount = 0;
+
+        for (int i = 0; i < aggCount; i++) {
+            ILogicalExpression aggExpr = aggExprs.get(i).getValue();
+            ILogicalExpression distinctValueExpr = null;
+            if (aggExpr.getExpressionTag() == LogicalExpressionTag.FUNCTION_CALL) {
+                AbstractFunctionCallExpression callExpr = (AbstractFunctionCallExpression) aggExpr;
+                FunctionIdentifier aggFn = callExpr.getFunctionIdentifier();
+                if (BuiltinFunctions.getAggregateFunctionForDistinct(aggFn) != null) {
+                    distinctValueExpr = callExpr.getArguments().get(0).getValue();
+                    distinctAggCount++;
+                }
+            }
+
+            BitSet argIndexes = aggGroups.get(distinctValueExpr);
+            if (argIndexes == null) {
+                argIndexes = bitSetAllocator.allocate(null);
+                argIndexes.clear();
+                aggGroups.put(distinctValueExpr, argIndexes);
+            }
+            argIndexes.set(i);
+        }
+
+        if (distinctAggCount == 0) {
+            return false;
+        }
+
+        for (Iterator<Map.Entry<ILogicalExpression, BitSet>> i = aggGroups.entrySet().iterator(); i.hasNext();) {
+            Map.Entry<ILogicalExpression, BitSet> me = i.next();
+            boolean isDistinct = me.getKey() != null;
+            BitSet argIndexes = me.getValue();
+
+            AggregateOperator newAggOp;
+            if (i.hasNext()) {
+                newAggOp = (AggregateOperator) OperatorManipulationUtil.deepCopyWithNewVars(aggOp, context).first;
+                newAggOp.getVariables().clear();
+                newAggOp.getVariables().addAll(aggOp.getVariables());
+            } else {
+                newAggOp = aggOp;
+            }
+
+            OperatorManipulationUtil.retainAssignVariablesAndExpressions(newAggOp.getVariables(),
+                    newAggOp.getExpressions(), argIndexes);
+
+            if (isDistinct) {
+                rewriteDistinctAggregate(newAggOp, context);
+            }
+
+            context.computeAndSetTypeEnvironmentForOperator(newAggOp);
+            context.addToDontApplySet(this, newAggOp);
+            outAggOps.add(newAggOp);
+        }
+
+        return true;
+    }
+
+    private void rewriteDistinctAggregate(AggregateOperator aggOp, IOptimizationContext context)
+            throws AlgebricksException {
+        ILogicalOperator inputOp = aggOp.getInputs().get(0).getValue();
+
+        // Introduce ASSIGN operator if distinct expression is not a variable
+        ILogicalExpression distinctExpr = ((AbstractFunctionCallExpression) aggOp.getExpressions().get(0).getValue())
+                .getArguments().get(0).getValue();
+        AssignOperator assignOp = null;
+        LogicalVariable distinctVar;
+        if (distinctExpr.getExpressionTag() == LogicalExpressionTag.VARIABLE) {
+            distinctVar = ((VariableReferenceExpression) distinctExpr).getVariableReference();
+        } else {
+            distinctVar = context.newVar();
+            assignOp = new AssignOperator(distinctVar, new MutableObject<>(distinctExpr));
+            assignOp.setSourceLocation(aggOp.getSourceLocation());
+            assignOp.getInputs().add(new MutableObject<>(inputOp));
+            assignOp.setExecutionMode(inputOp.getExecutionMode());
+            context.computeAndSetTypeEnvironmentForOperator(assignOp);
+            inputOp = assignOp;
+        }
+
+        // Introduce DISTINCT operator
+        VariableReferenceExpression distinctVarRef = new VariableReferenceExpression(distinctVar);
+        distinctVarRef.setSourceLocation(distinctExpr.getSourceLocation());
+
+        List<Mutable<ILogicalExpression>> distinctExprs = new ArrayList<>(1);
+        distinctExprs.add(new MutableObject<>(distinctVarRef));
+        DistinctOperator distinctOp = new DistinctOperator(distinctExprs);
+        distinctOp.setSourceLocation(aggOp.getSourceLocation());
+        distinctOp.getInputs().add(new MutableObject<>(inputOp));
+        distinctOp.setExecutionMode(inputOp.getExecutionMode());
+        context.computeAndSetTypeEnvironmentForOperator(distinctOp);
+
+        // Change function identifiers from agg-distinct(expr) to agg(expr),
+        // replace 'expr' with variable reference if ASSIGN was introduced
+        for (Mutable<ILogicalExpression> aggExprRef : aggOp.getExpressions()) {
+            AbstractFunctionCallExpression callExpr = (AbstractFunctionCallExpression) aggExprRef.getValue();
+            FunctionIdentifier regularAggForDistinct =
+                    BuiltinFunctions.getAggregateFunctionForDistinct(callExpr.getFunctionIdentifier());
+            if (regularAggForDistinct == null) {
+                throw new IllegalStateException(String.valueOf(callExpr.getFunctionIdentifier()));
+            }
+            callExpr.setFunctionInfo(BuiltinFunctions.getAsterixFunctionInfo(regularAggForDistinct));
+
+            if (assignOp != null) {
+                callExpr.getArguments().get(0).setValue(distinctVarRef.cloneExpression());
+            }
+        }
+
+        aggOp.getInputs().clear();
+        aggOp.getInputs().add(new MutableObject<>(distinctOp));
+        context.computeAndSetTypeEnvironmentForOperator(aggOp);
+    }
+
+    @Override
+    public boolean rewritePost(Mutable<ILogicalOperator> opRef, IOptimizationContext context) {
+        return false;
+    }
+}
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.10.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.10.sqlpp
new file mode 100644
index 0000000..b605f56
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.10.sqlpp
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Two distinct aggregates on the same field with group by */
+
+drop  dataverse test if exists;
+create  dataverse test;
+
+use test;
+
+create type t1 as {
+  id: bigint,
+  v1: bigint
+};
+
+create dataset d1(t1) primary key id;
+create dataset d2(t1) primary key id;
+create dataset d3(t1) primary key id;
+
+from d1 x, d2 y, range(1, 3) g
+group by g
+select
+  g,
+  count(distinct x.v1) as count_distinct_x,
+  sum(distinct x.v1) as sum_distinct_x
+order by g;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.11.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.11.sqlpp
new file mode 100644
index 0000000..e0f0547
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.11.sqlpp
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Two distinct aggregates on different fields with group by */
+
+drop  dataverse test if exists;
+create  dataverse test;
+
+use test;
+
+create type t1 as {
+  id: bigint,
+  v1: bigint
+};
+
+create dataset d1(t1) primary key id;
+create dataset d2(t1) primary key id;
+create dataset d3(t1) primary key id;
+
+from d1 x, d2 y, range(1, 3) g
+group by g
+select
+  g,
+  sum(distinct x.v1) as sum_distinct_x,
+  sum(distinct y.v1) as sum_distinct_y
+order by g;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.12.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.12.sqlpp
new file mode 100644
index 0000000..b45b719
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.12.sqlpp
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Two distinct aggregates and one regular on the same field with group by */
+
+drop  dataverse test if exists;
+create  dataverse test;
+
+use test;
+
+create type t1 as {
+  id: bigint,
+  v1: bigint
+};
+
+create dataset d1(t1) primary key id;
+create dataset d2(t1) primary key id;
+create dataset d3(t1) primary key id;
+
+from d1 x, d2 y, range(1, 3) g
+group by g
+select
+  g,
+  sum(x.v1) as sum_x,
+  sum(distinct x.v1) as sum_distinct_x,
+  sum(distinct x.v1) as sum_distinct_y
+order by g;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.13.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.13.sqlpp
new file mode 100644
index 0000000..c78cc59
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.13.sqlpp
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Two distinct aggregates and one regular on different fields with group by */
+
+drop  dataverse test if exists;
+create  dataverse test;
+
+use test;
+
+create type t1 as {
+  id: bigint,
+  v1: bigint
+};
+
+create dataset d1(t1) primary key id;
+create dataset d2(t1) primary key id;
+create dataset d3(t1) primary key id;
+
+from d1 x, d2 y, d3 z, range(1, 3) g
+group by g
+select
+  g,
+  sum(distinct x.v1) as sum_distinct_x,
+  sum(y.v1) as sum_y,
+  sum(distinct z.v1) as sum_distinct_z
+order by g;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.14.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.14.sqlpp
new file mode 100644
index 0000000..60129af
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.14.sqlpp
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Groups of distinct and regular aggregates on various fields with group by */
+
+drop  dataverse test if exists;
+create  dataverse test;
+
+use test;
+
+create type t1 as {
+  id: bigint,
+  v1: bigint
+};
+
+create dataset d1(t1) primary key id;
+create dataset d2(t1) primary key id;
+create dataset d3(t1) primary key id;
+
+from d1 x, d2 y, d3 z, range(1, 3) g
+group by g
+select
+  g,
+  sum(distinct x.v1) as sum_distinct_x,
+  sum(y.v1) as sum_y,
+  sum(distinct z.v1) as sum_distinct_z,
+  avg(distinct x.v1) as avg_distinct_x,
+  avg(distinct y.v1) as avg_distinct_y,
+  count(x.v1) as count_x,
+  count(distinct y.v1) as count_distinct_y,
+  avg(z.v1) as avg_z,
+  count(distinct z.v1) as count_distinct_z
+order by g;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.15.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.15.sqlpp
new file mode 100644
index 0000000..51713a4
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.15.sqlpp
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Groups of distinct and regular aggregates on various fields */
+/* Unpartitioned data */
+
+drop  dataverse test if exists;
+create  dataverse test;
+
+use test;
+
+create type t1 as {
+  id: bigint,
+  v1: bigint
+};
+
+create dataset d1(t1) primary key id;
+create dataset d2(t1) primary key id;
+create dataset d3(t1) primary key id;
+
+from range(1, 5) x, range(6, 10) y, range(11, 15) z
+select
+  sum(distinct x) as sum_distinct_x,
+  sum(y) as sum_y,
+  sum(distinct z) as sum_distinct_z,
+  avg(distinct x) as avg_distinct_x,
+  avg(distinct y) as avg_distinct_y,
+  count(x) as count_x,
+  count(distinct y) as count_distinct_y,
+  avg(z) as avg_z,
+  count(distinct z) as count_distinct_z;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.16.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.16.sqlpp
new file mode 100644
index 0000000..871ee6d
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.16.sqlpp
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Groups of distinct and regular aggregates on various fields with group by */
+/* Unpartitioned data */
+
+drop  dataverse test if exists;
+create  dataverse test;
+
+use test;
+
+create type t1 as {
+  id: bigint,
+  v1: bigint
+};
+
+create dataset d1(t1) primary key id;
+create dataset d2(t1) primary key id;
+create dataset d3(t1) primary key id;
+
+from range(1, 5) x, range(6, 10) y, range(11, 15) z, range(1, 3) g
+group by g
+select
+  g,
+  sum(distinct x) as sum_distinct_x,
+  sum(y) as sum_y,
+  sum(distinct z) as sum_distinct_z,
+  avg(distinct x) as avg_distinct_x,
+  avg(distinct y) as avg_distinct_y,
+  count(x) as count_x,
+  count(distinct y) as count_distinct_y,
+  avg(z) as avg_z,
+  count(distinct z) as count_distinct_z
+order by g;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.17.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.17.sqlpp
new file mode 100644
index 0000000..d71238b
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.17.sqlpp
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Groups of distinct and regular aggregates on various fields */
+/* Unpartitioned data */
+
+drop  dataverse test if exists;
+create  dataverse test;
+
+use test;
+
+create type t1 as {
+  id: bigint,
+  v1: bigint
+};
+
+create dataset d1(t1) primary key id;
+create dataset d2(t1) primary key id;
+
+select t.* from
+(
+
+  select
+    sum(distinct x.v1) as sum_distinct_x,
+    sum(distinct y.v1) as sum_distinct_y
+  from d1 x, d2 y
+  group by true
+
+union all
+
+  select
+    sum(distinct x.v1) as sum_distinct_x,
+    sum(distinct y.v1) as sum_distinct_y
+  from d1 x, d2 y
+  group by false
+
+) t;
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.3.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.3.sqlpp
new file mode 100644
index 0000000..2d71819
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.3.sqlpp
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Single distinct aggregate */
+
+drop  dataverse test if exists;
+create  dataverse test;
+
+use test;
+
+create type t1 as {
+  id: bigint,
+  v1: bigint
+};
+
+create dataset d1(t1) primary key id;
+create dataset d2(t1) primary key id;
+create dataset d3(t1) primary key id;
+
+from d1 x, d2 y
+select count(distinct x.v1) as count_distinct_x;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.4.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.4.sqlpp
new file mode 100644
index 0000000..65bb023
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.4.sqlpp
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Two distinct aggregates on the same field */
+
+drop  dataverse test if exists;
+create  dataverse test;
+
+use test;
+
+create type t1 as {
+  id: bigint,
+  v1: bigint
+};
+
+create dataset d1(t1) primary key id;
+create dataset d2(t1) primary key id;
+create dataset d3(t1) primary key id;
+
+from d1 x, d2 y
+select
+  count(distinct x.v1) as count_distinct_x,
+  sum(distinct x.v1) as sum_distinct_x;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.5.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.5.sqlpp
new file mode 100644
index 0000000..daee7e6
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.5.sqlpp
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Two distinct aggregates on different fields */
+
+drop  dataverse test if exists;
+create  dataverse test;
+
+use test;
+
+create type t1 as {
+  id: bigint,
+  v1: bigint
+};
+
+create dataset d1(t1) primary key id;
+create dataset d2(t1) primary key id;
+create dataset d3(t1) primary key id;
+
+from d1 x, d2 y
+select
+  sum(distinct x.v1) as sum_distinct_x,
+  sum(distinct y.v1) as sum_distinct_y;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.6.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.6.sqlpp
new file mode 100644
index 0000000..1f1629c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.6.sqlpp
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Two distinct aggregates and one regular on the same field */
+
+drop  dataverse test if exists;
+create  dataverse test;
+
+use test;
+
+create type t1 as {
+  id: bigint,
+  v1: bigint
+};
+
+create dataset d1(t1) primary key id;
+create dataset d2(t1) primary key id;
+create dataset d3(t1) primary key id;
+
+from d1 x, d2 y
+select
+  sum(x.v1) as sum_x,
+  sum(distinct x.v1) as sum_distinct_x,
+  sum(distinct x.v1) as sum_distinct_y;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.7.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.7.sqlpp
new file mode 100644
index 0000000..21c3099
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.7.sqlpp
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Two distinct aggregates and one regular on different fields */
+
+drop  dataverse test if exists;
+create  dataverse test;
+
+use test;
+
+create type t1 as {
+  id: bigint,
+  v1: bigint
+};
+
+create dataset d1(t1) primary key id;
+create dataset d2(t1) primary key id;
+create dataset d3(t1) primary key id;
+
+from d1 x, d2 y, d3 z
+select
+  sum(distinct x.v1) as sum_distinct_x,
+  sum(y.v1) as sum_y,
+  sum(distinct z.v1) as sum_distinct_z;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.8.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.8.sqlpp
new file mode 100644
index 0000000..fba356e
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.8.sqlpp
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Groups of distinct and regular aggregates on various fields */
+
+drop  dataverse test if exists;
+create  dataverse test;
+
+use test;
+
+create type t1 as {
+  id: bigint,
+  v1: bigint
+};
+
+create dataset d1(t1) primary key id;
+create dataset d2(t1) primary key id;
+create dataset d3(t1) primary key id;
+
+from d1 x, d2 y, d3 z
+select
+  sum(distinct x.v1) as sum_distinct_x,
+  sum(y.v1) as sum_y,
+  sum(distinct z.v1) as sum_distinct_z,
+  avg(distinct x.v1) as avg_distinct_x,
+  avg(distinct y.v1) as avg_distinct_y,
+  count(x.v1) as count_x,
+  count(distinct y.v1) as count_distinct_y,
+  avg(z.v1) as avg_z,
+  count(distinct z.v1) as count_distinct_z;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.9.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.9.sqlpp
new file mode 100644
index 0000000..9b76a99
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/aggregate-sql-sugar/distinct_mixed/distinct_mixed.9.sqlpp
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Single distinct aggregate with group by */
+
+drop  dataverse test if exists;
+create  dataverse test;
+
+use test;
+
+create type t1 as {
+  id: bigint,
+  v1: bigint
+};
+
+create dataset d1(t1) primary key id;
+create dataset d2(t1) primary key id;
+create dataset d3(t1) primary key id;
+
+from d1 x, d2 y, range(1, 3) g
+group by g
+select
+  g,
+  count(distinct x.v1) as count_distinct_x
+order by g;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.10.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.10.plan
new file mode 100644
index 0000000..6b66a53
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.10.plan
@@ -0,0 +1,35 @@
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    -- STREAM_PROJECT  |PARTITIONED|
+      -- ASSIGN  |PARTITIONED|
+        -- SORT_MERGE_EXCHANGE [$$g(ASC) ]  |PARTITIONED|
+          -- PRE_CLUSTERED_GROUP_BY[$$g]  |PARTITIONED|
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- MICRO_PRE_SORTED_DISTINCT_BY  |LOCAL|
+                        -- IN_MEMORY_STABLE_SORT [$$62(ASC)]  |LOCAL|
+                          -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+              -- STABLE_SORT [$$g(ASC)]  |PARTITIONED|
+                -- HASH_PARTITION_EXCHANGE [$$g]  |PARTITIONED|
+                  -- NESTED_LOOP  |PARTITIONED|
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      -- NESTED_LOOP  |PARTITIONED|
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          -- STREAM_PROJECT  |PARTITIONED|
+                            -- ASSIGN  |PARTITIONED|
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- DATASOURCE_SCAN  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                        -- BROADCAST_EXCHANGE  |PARTITIONED|
+                          -- STREAM_PROJECT  |PARTITIONED|
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              -- DATASOURCE_SCAN  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                    -- BROADCAST_EXCHANGE  |PARTITIONED|
+                      -- UNNEST  |UNPARTITIONED|
+                        -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.11.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.11.plan
new file mode 100644
index 0000000..dd3bb8f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.11.plan
@@ -0,0 +1,43 @@
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    -- STREAM_PROJECT  |PARTITIONED|
+      -- ASSIGN  |PARTITIONED|
+        -- SORT_MERGE_EXCHANGE [$$g(ASC) ]  |PARTITIONED|
+          -- PRE_CLUSTERED_GROUP_BY[$$g]  |PARTITIONED|
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- MICRO_PRE_SORTED_DISTINCT_BY  |LOCAL|
+                        -- IN_MEMORY_STABLE_SORT [$$62(ASC)]  |LOCAL|
+                          -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- MICRO_PRE_SORTED_DISTINCT_BY  |LOCAL|
+                        -- IN_MEMORY_STABLE_SORT [$$67(ASC)]  |LOCAL|
+                          -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+              -- STABLE_SORT [$$g(ASC)]  |PARTITIONED|
+                -- HASH_PARTITION_EXCHANGE [$$g]  |PARTITIONED|
+                  -- NESTED_LOOP  |PARTITIONED|
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      -- NESTED_LOOP  |PARTITIONED|
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          -- STREAM_PROJECT  |PARTITIONED|
+                            -- ASSIGN  |PARTITIONED|
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- DATASOURCE_SCAN  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                        -- BROADCAST_EXCHANGE  |PARTITIONED|
+                          -- STREAM_PROJECT  |PARTITIONED|
+                            -- ASSIGN  |PARTITIONED|
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- DATASOURCE_SCAN  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                    -- BROADCAST_EXCHANGE  |PARTITIONED|
+                      -- UNNEST  |UNPARTITIONED|
+                        -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.12.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.12.plan
new file mode 100644
index 0000000..1766fc3
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.12.plan
@@ -0,0 +1,39 @@
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    -- STREAM_PROJECT  |PARTITIONED|
+      -- ASSIGN  |PARTITIONED|
+        -- SORT_MERGE_EXCHANGE [$$g(ASC) ]  |PARTITIONED|
+          -- PRE_CLUSTERED_GROUP_BY[$$g]  |PARTITIONED|
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- MICRO_PRE_SORTED_DISTINCT_BY  |LOCAL|
+                        -- IN_MEMORY_STABLE_SORT [$$66(ASC)]  |LOCAL|
+                          -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+              -- STABLE_SORT [$$g(ASC)]  |PARTITIONED|
+                -- HASH_PARTITION_EXCHANGE [$$g]  |PARTITIONED|
+                  -- NESTED_LOOP  |PARTITIONED|
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      -- NESTED_LOOP  |PARTITIONED|
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          -- STREAM_PROJECT  |PARTITIONED|
+                            -- ASSIGN  |PARTITIONED|
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- DATASOURCE_SCAN  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                        -- BROADCAST_EXCHANGE  |PARTITIONED|
+                          -- STREAM_PROJECT  |PARTITIONED|
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              -- DATASOURCE_SCAN  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                    -- BROADCAST_EXCHANGE  |PARTITIONED|
+                      -- UNNEST  |UNPARTITIONED|
+                        -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.13.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.13.plan
new file mode 100644
index 0000000..98c4c4f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.13.plan
@@ -0,0 +1,57 @@
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    -- STREAM_PROJECT  |PARTITIONED|
+      -- ASSIGN  |PARTITIONED|
+        -- SORT_MERGE_EXCHANGE [$$g(ASC) ]  |PARTITIONED|
+          -- PRE_CLUSTERED_GROUP_BY[$$g]  |PARTITIONED|
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- MICRO_PRE_SORTED_DISTINCT_BY  |LOCAL|
+                        -- IN_MEMORY_STABLE_SORT [$$76(ASC)]  |LOCAL|
+                          -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- MICRO_PRE_SORTED_DISTINCT_BY  |LOCAL|
+                        -- IN_MEMORY_STABLE_SORT [$$86(ASC)]  |LOCAL|
+                          -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+              -- STABLE_SORT [$$g(ASC)]  |PARTITIONED|
+                -- HASH_PARTITION_EXCHANGE [$$g]  |PARTITIONED|
+                  -- NESTED_LOOP  |PARTITIONED|
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      -- NESTED_LOOP  |PARTITIONED|
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          -- NESTED_LOOP  |PARTITIONED|
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                -- ASSIGN  |PARTITIONED|
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- DATASOURCE_SCAN  |PARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                            -- BROADCAST_EXCHANGE  |PARTITIONED|
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                -- ASSIGN  |PARTITIONED|
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- DATASOURCE_SCAN  |PARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                        -- BROADCAST_EXCHANGE  |PARTITIONED|
+                          -- STREAM_PROJECT  |PARTITIONED|
+                            -- ASSIGN  |PARTITIONED|
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- DATASOURCE_SCAN  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                    -- BROADCAST_EXCHANGE  |PARTITIONED|
+                      -- UNNEST  |UNPARTITIONED|
+                        -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.14.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.14.plan
new file mode 100644
index 0000000..b563fef
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.14.plan
@@ -0,0 +1,63 @@
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    -- STREAM_PROJECT  |PARTITIONED|
+      -- ASSIGN  |PARTITIONED|
+        -- SORT_MERGE_EXCHANGE [$$g(ASC) ]  |PARTITIONED|
+          -- PRE_CLUSTERED_GROUP_BY[$$g]  |PARTITIONED|
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- MICRO_PRE_SORTED_DISTINCT_BY  |LOCAL|
+                        -- IN_MEMORY_STABLE_SORT [$$100(ASC)]  |LOCAL|
+                          -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- MICRO_PRE_SORTED_DISTINCT_BY  |LOCAL|
+                        -- IN_MEMORY_STABLE_SORT [$$110(ASC)]  |LOCAL|
+                          -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- MICRO_PRE_SORTED_DISTINCT_BY  |LOCAL|
+                        -- IN_MEMORY_STABLE_SORT [$$105(ASC)]  |LOCAL|
+                          -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+              -- STABLE_SORT [$$g(ASC)]  |PARTITIONED|
+                -- HASH_PARTITION_EXCHANGE [$$g]  |PARTITIONED|
+                  -- NESTED_LOOP  |PARTITIONED|
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      -- NESTED_LOOP  |PARTITIONED|
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          -- NESTED_LOOP  |PARTITIONED|
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                -- ASSIGN  |PARTITIONED|
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- DATASOURCE_SCAN  |PARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                            -- BROADCAST_EXCHANGE  |PARTITIONED|
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                -- ASSIGN  |PARTITIONED|
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- DATASOURCE_SCAN  |PARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                        -- BROADCAST_EXCHANGE  |PARTITIONED|
+                          -- STREAM_PROJECT  |PARTITIONED|
+                            -- ASSIGN  |PARTITIONED|
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- DATASOURCE_SCAN  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                    -- BROADCAST_EXCHANGE  |PARTITIONED|
+                      -- UNNEST  |UNPARTITIONED|
+                        -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.15.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.15.plan
new file mode 100644
index 0000000..5c97374
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.15.plan
@@ -0,0 +1,175 @@
+-- DISTRIBUTE_RESULT  |LOCAL|
+  -- ONE_TO_ONE_EXCHANGE  |LOCAL|
+    -- STREAM_PROJECT  |LOCAL|
+      -- ASSIGN  |LOCAL|
+        -- ONE_TO_ONE_EXCHANGE  |LOCAL|
+          -- NESTED_LOOP  |LOCAL|
+            -- ONE_TO_ONE_EXCHANGE  |LOCAL|
+              -- NESTED_LOOP  |LOCAL|
+                -- ONE_TO_ONE_EXCHANGE  |LOCAL|
+                  -- NESTED_LOOP  |LOCAL|
+                    -- ONE_TO_ONE_EXCHANGE  |LOCAL|
+                      -- AGGREGATE  |LOCAL|
+                        -- ONE_TO_ONE_EXCHANGE  |LOCAL|
+                          -- PRE_SORTED_DISTINCT_BY  |LOCAL|
+                            -- ONE_TO_ONE_EXCHANGE  |LOCAL|
+                              -- STABLE_SORT [$$133(ASC)]  |LOCAL|
+                                -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                  -- NESTED_LOOP  |UNPARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                      -- NESTED_LOOP  |UNPARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                          -- REPLICATE  |UNPARTITIONED|
+                                            -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                              -- REPLICATE  |UNPARTITIONED|
+                                                -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                                  -- UNNEST  |UNPARTITIONED|
+                                                    -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                          -- REPLICATE  |UNPARTITIONED|
+                                            -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                              -- STREAM_PROJECT  |UNPARTITIONED|
+                                                -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                                  -- REPLICATE  |UNPARTITIONED|
+                                                    -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                                      -- UNNEST  |UNPARTITIONED|
+                                                        -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                      -- REPLICATE  |UNPARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                          -- STREAM_PROJECT  |UNPARTITIONED|
+                                            -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                              -- REPLICATE  |UNPARTITIONED|
+                                                -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                                  -- UNNEST  |UNPARTITIONED|
+                                                    -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
+                    -- ONE_TO_ONE_EXCHANGE  |LOCAL|
+                      -- AGGREGATE  |LOCAL|
+                        -- AGGREGATE  |LOCAL|
+                          -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                            -- NESTED_LOOP  |UNPARTITIONED|
+                              -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                -- NESTED_LOOP  |UNPARTITIONED|
+                                  -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                    -- STREAM_PROJECT  |UNPARTITIONED|
+                                      -- ASSIGN  |UNPARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                          -- REPLICATE  |UNPARTITIONED|
+                                            -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                              -- REPLICATE  |UNPARTITIONED|
+                                                -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                                  -- UNNEST  |UNPARTITIONED|
+                                                    -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
+                                  -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                    -- REPLICATE  |UNPARTITIONED|
+                                      -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                        -- STREAM_PROJECT  |UNPARTITIONED|
+                                          -- ASSIGN  |UNPARTITIONED|
+                                            -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                              -- REPLICATE  |UNPARTITIONED|
+                                                -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                                  -- UNNEST  |UNPARTITIONED|
+                                                    -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
+                              -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                -- STREAM_PROJECT  |UNPARTITIONED|
+                                  -- ASSIGN  |UNPARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                      -- REPLICATE  |UNPARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                          -- STREAM_PROJECT  |UNPARTITIONED|
+                                            -- ASSIGN  |UNPARTITIONED|
+                                              -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                                -- REPLICATE  |UNPARTITIONED|
+                                                  -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                                    -- UNNEST  |UNPARTITIONED|
+                                                      -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
+                -- ONE_TO_ONE_EXCHANGE  |LOCAL|
+                  -- AGGREGATE  |LOCAL|
+                    -- ONE_TO_ONE_EXCHANGE  |LOCAL|
+                      -- PRE_SORTED_DISTINCT_BY  |LOCAL|
+                        -- ONE_TO_ONE_EXCHANGE  |LOCAL|
+                          -- STABLE_SORT [$$159(ASC)]  |LOCAL|
+                            -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                              -- NESTED_LOOP  |UNPARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                  -- NESTED_LOOP  |UNPARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                      -- REPLICATE  |UNPARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                          -- STREAM_PROJECT  |UNPARTITIONED|
+                                            -- ASSIGN  |UNPARTITIONED|
+                                              -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                                -- REPLICATE  |UNPARTITIONED|
+                                                  -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                                    -- UNNEST  |UNPARTITIONED|
+                                                      -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                      -- STREAM_PROJECT  |UNPARTITIONED|
+                                        -- ASSIGN  |UNPARTITIONED|
+                                          -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                            -- REPLICATE  |UNPARTITIONED|
+                                              -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                                -- STREAM_PROJECT  |UNPARTITIONED|
+                                                  -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                                    -- REPLICATE  |UNPARTITIONED|
+                                                      -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                                        -- UNNEST  |UNPARTITIONED|
+                                                          -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                  -- REPLICATE  |UNPARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                      -- STREAM_PROJECT  |UNPARTITIONED|
+                                        -- ASSIGN  |UNPARTITIONED|
+                                          -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                            -- REPLICATE  |UNPARTITIONED|
+                                              -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                                -- UNNEST  |UNPARTITIONED|
+                                                  -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
+            -- ONE_TO_ONE_EXCHANGE  |LOCAL|
+              -- AGGREGATE  |LOCAL|
+                -- ONE_TO_ONE_EXCHANGE  |LOCAL|
+                  -- PRE_SORTED_DISTINCT_BY  |LOCAL|
+                    -- ONE_TO_ONE_EXCHANGE  |LOCAL|
+                      -- STABLE_SORT [$$y(ASC)]  |LOCAL|
+                        -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                          -- NESTED_LOOP  |UNPARTITIONED|
+                            -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                              -- NESTED_LOOP  |UNPARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                  -- STREAM_PROJECT  |UNPARTITIONED|
+                                    -- ASSIGN  |UNPARTITIONED|
+                                      -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                        -- REPLICATE  |UNPARTITIONED|
+                                          -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                            -- STREAM_PROJECT  |UNPARTITIONED|
+                                              -- ASSIGN  |UNPARTITIONED|
+                                                -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                                  -- REPLICATE  |UNPARTITIONED|
+                                                    -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                                      -- UNNEST  |UNPARTITIONED|
+                                                        -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                  -- STREAM_PROJECT  |UNPARTITIONED|
+                                    -- ASSIGN  |UNPARTITIONED|
+                                      -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                        -- REPLICATE  |UNPARTITIONED|
+                                          -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                            -- STREAM_PROJECT  |UNPARTITIONED|
+                                              -- ASSIGN  |UNPARTITIONED|
+                                                -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                                  -- REPLICATE  |UNPARTITIONED|
+                                                    -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                                      -- UNNEST  |UNPARTITIONED|
+                                                        -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
+                            -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                              -- STREAM_PROJECT  |UNPARTITIONED|
+                                -- ASSIGN  |UNPARTITIONED|
+                                  -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                    -- REPLICATE  |UNPARTITIONED|
+                                      -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                        -- STREAM_PROJECT  |UNPARTITIONED|
+                                          -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                            -- REPLICATE  |UNPARTITIONED|
+                                              -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                                                -- UNNEST  |UNPARTITIONED|
+                                                  -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.16.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.16.plan
new file mode 100644
index 0000000..ab82bf0
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.16.plan
@@ -0,0 +1,49 @@
+-- DISTRIBUTE_RESULT  |LOCAL|
+  -- ONE_TO_ONE_EXCHANGE  |LOCAL|
+    -- STREAM_PROJECT  |LOCAL|
+      -- ASSIGN  |LOCAL|
+        -- ONE_TO_ONE_EXCHANGE  |LOCAL|
+          -- PRE_CLUSTERED_GROUP_BY[$$g]  |LOCAL|
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- MICRO_PRE_SORTED_DISTINCT_BY  |LOCAL|
+                        -- IN_MEMORY_STABLE_SORT [$$x(ASC)]  |LOCAL|
+                          -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- AGGREGATE  |LOCAL|
+                        -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- MICRO_PRE_SORTED_DISTINCT_BY  |LOCAL|
+                        -- IN_MEMORY_STABLE_SORT [$$z(ASC)]  |LOCAL|
+                          -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- MICRO_PRE_SORTED_DISTINCT_BY  |LOCAL|
+                        -- IN_MEMORY_STABLE_SORT [$$y(ASC)]  |LOCAL|
+                          -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+            -- ONE_TO_ONE_EXCHANGE  |LOCAL|
+              -- STABLE_SORT [$$g(ASC)]  |LOCAL|
+                -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                  -- NESTED_LOOP  |UNPARTITIONED|
+                    -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                      -- NESTED_LOOP  |UNPARTITIONED|
+                        -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                          -- NESTED_LOOP  |UNPARTITIONED|
+                            -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                              -- UNNEST  |UNPARTITIONED|
+                                -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
+                            -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                              -- UNNEST  |UNPARTITIONED|
+                                -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
+                        -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                          -- UNNEST  |UNPARTITIONED|
+                            -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
+                    -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                      -- UNNEST  |UNPARTITIONED|
+                        -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.17.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.17.plan
new file mode 100644
index 0000000..6249d46
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.17.plan
@@ -0,0 +1,94 @@
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    -- UNION_ALL  |PARTITIONED|
+      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+        -- STREAM_PROJECT  |PARTITIONED|
+          -- ASSIGN  |PARTITIONED|
+            -- STREAM_PROJECT  |PARTITIONED|
+              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                -- PRE_CLUSTERED_GROUP_BY[$$140]  |PARTITIONED|
+                        {
+                          -- AGGREGATE  |LOCAL|
+                            -- MICRO_PRE_SORTED_DISTINCT_BY  |LOCAL|
+                              -- IN_MEMORY_STABLE_SORT [$$106(ASC)]  |LOCAL|
+                                -- NESTED_TUPLE_SOURCE  |LOCAL|
+                        }
+                        {
+                          -- AGGREGATE  |LOCAL|
+                            -- MICRO_PRE_SORTED_DISTINCT_BY  |LOCAL|
+                              -- IN_MEMORY_STABLE_SORT [$$111(ASC)]  |LOCAL|
+                                -- NESTED_TUPLE_SOURCE  |LOCAL|
+                        }
+                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                    -- STABLE_SORT [$$140(ASC)]  |PARTITIONED|
+                      -- HASH_PARTITION_EXCHANGE [$$140]  |PARTITIONED|
+                        -- NESTED_LOOP  |PARTITIONED|
+                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                            -- STREAM_PROJECT  |PARTITIONED|
+                              -- ASSIGN  |PARTITIONED|
+                                -- STREAM_PROJECT  |PARTITIONED|
+                                  -- ASSIGN  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- REPLICATE  |PARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          -- STREAM_PROJECT  |PARTITIONED|
+                                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                              -- DATASOURCE_SCAN  |PARTITIONED|
+                                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                  -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                            -- STREAM_PROJECT  |PARTITIONED|
+                              -- ASSIGN  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- REPLICATE  |PARTITIONED|
+                                    -- BROADCAST_EXCHANGE  |PARTITIONED|
+                                      -- STREAM_PROJECT  |PARTITIONED|
+                                        -- ASSIGN  |PARTITIONED|
+                                          -- STREAM_PROJECT  |PARTITIONED|
+                                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                              -- DATASOURCE_SCAN  |PARTITIONED|
+                                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                  -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+        -- STREAM_PROJECT  |PARTITIONED|
+          -- ASSIGN  |PARTITIONED|
+            -- STREAM_PROJECT  |PARTITIONED|
+              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                -- PRE_CLUSTERED_GROUP_BY[$$141]  |PARTITIONED|
+                        {
+                          -- AGGREGATE  |LOCAL|
+                            -- MICRO_PRE_SORTED_DISTINCT_BY  |LOCAL|
+                              -- IN_MEMORY_STABLE_SORT [$$124(ASC)]  |LOCAL|
+                                -- NESTED_TUPLE_SOURCE  |LOCAL|
+                        }
+                        {
+                          -- AGGREGATE  |LOCAL|
+                            -- MICRO_PRE_SORTED_DISTINCT_BY  |LOCAL|
+                              -- IN_MEMORY_STABLE_SORT [$$129(ASC)]  |LOCAL|
+                                -- NESTED_TUPLE_SOURCE  |LOCAL|
+                        }
+                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                    -- STABLE_SORT [$$141(ASC)]  |PARTITIONED|
+                      -- HASH_PARTITION_EXCHANGE [$$141]  |PARTITIONED|
+                        -- NESTED_LOOP  |PARTITIONED|
+                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                            -- STREAM_PROJECT  |PARTITIONED|
+                              -- ASSIGN  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- REPLICATE  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- STREAM_PROJECT  |PARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          -- DATASOURCE_SCAN  |PARTITIONED|
+                                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                              -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                            -- REPLICATE  |PARTITIONED|
+                              -- BROADCAST_EXCHANGE  |PARTITIONED|
+                                -- STREAM_PROJECT  |PARTITIONED|
+                                  -- ASSIGN  |PARTITIONED|
+                                    -- STREAM_PROJECT  |PARTITIONED|
+                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                        -- DATASOURCE_SCAN  |PARTITIONED|
+                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                            -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.3.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.3.plan
new file mode 100644
index 0000000..aee235f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.3.plan
@@ -0,0 +1,25 @@
+-- DISTRIBUTE_RESULT  |UNPARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+    -- STREAM_PROJECT  |UNPARTITIONED|
+      -- ASSIGN  |UNPARTITIONED|
+        -- AGGREGATE  |UNPARTITIONED|
+          -- SORT_MERGE_EXCHANGE [$$39(ASC) ]  |PARTITIONED|
+            -- PRE_SORTED_DISTINCT_BY  |PARTITIONED|
+              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                -- STABLE_SORT [$$39(ASC)]  |PARTITIONED|
+                  -- HASH_PARTITION_EXCHANGE [$$39]  |PARTITIONED|
+                    -- NESTED_LOOP  |PARTITIONED|
+                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                        -- STREAM_PROJECT  |PARTITIONED|
+                          -- ASSIGN  |PARTITIONED|
+                            -- STREAM_PROJECT  |PARTITIONED|
+                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                -- DATASOURCE_SCAN  |PARTITIONED|
+                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                    -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                      -- BROADCAST_EXCHANGE  |PARTITIONED|
+                        -- STREAM_PROJECT  |PARTITIONED|
+                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                            -- DATASOURCE_SCAN  |PARTITIONED|
+                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.4.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.4.plan
new file mode 100644
index 0000000..1cfe01a
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.4.plan
@@ -0,0 +1,25 @@
+-- DISTRIBUTE_RESULT  |UNPARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+    -- STREAM_PROJECT  |UNPARTITIONED|
+      -- ASSIGN  |UNPARTITIONED|
+        -- AGGREGATE  |UNPARTITIONED|
+          -- SORT_MERGE_EXCHANGE [$$43(ASC) ]  |PARTITIONED|
+            -- PRE_SORTED_DISTINCT_BY  |PARTITIONED|
+              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                -- STABLE_SORT [$$43(ASC)]  |PARTITIONED|
+                  -- HASH_PARTITION_EXCHANGE [$$43]  |PARTITIONED|
+                    -- NESTED_LOOP  |PARTITIONED|
+                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                        -- STREAM_PROJECT  |PARTITIONED|
+                          -- ASSIGN  |PARTITIONED|
+                            -- STREAM_PROJECT  |PARTITIONED|
+                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                -- DATASOURCE_SCAN  |PARTITIONED|
+                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                    -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                      -- BROADCAST_EXCHANGE  |PARTITIONED|
+                        -- STREAM_PROJECT  |PARTITIONED|
+                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                            -- DATASOURCE_SCAN  |PARTITIONED|
+                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.5.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.5.plan
new file mode 100644
index 0000000..afa6c13
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.5.plan
@@ -0,0 +1,60 @@
+-- DISTRIBUTE_RESULT  |UNPARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+    -- STREAM_PROJECT  |UNPARTITIONED|
+      -- ASSIGN  |UNPARTITIONED|
+        -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+          -- NESTED_LOOP  |UNPARTITIONED|
+            -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+              -- AGGREGATE  |UNPARTITIONED|
+                -- SORT_MERGE_EXCHANGE [$$59(ASC) ]  |PARTITIONED|
+                  -- PRE_SORTED_DISTINCT_BY  |PARTITIONED|
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      -- STABLE_SORT [$$59(ASC)]  |PARTITIONED|
+                        -- HASH_PARTITION_EXCHANGE [$$59]  |PARTITIONED|
+                          -- NESTED_LOOP  |PARTITIONED|
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                -- ASSIGN  |PARTITIONED|
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- REPLICATE  |PARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          -- DATASOURCE_SCAN  |PARTITIONED|
+                                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                              -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                            -- BROADCAST_EXCHANGE  |PARTITIONED|
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                -- ASSIGN  |PARTITIONED|
+                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                    -- REPLICATE  |PARTITIONED|
+                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                        -- DATASOURCE_SCAN  |PARTITIONED|
+                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                            -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+            -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+              -- AGGREGATE  |UNPARTITIONED|
+                -- SORT_MERGE_EXCHANGE [$$48(ASC) ]  |PARTITIONED|
+                  -- PRE_SORTED_DISTINCT_BY  |PARTITIONED|
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      -- STABLE_SORT [$$48(ASC)]  |PARTITIONED|
+                        -- HASH_PARTITION_EXCHANGE [$$48]  |PARTITIONED|
+                          -- NESTED_LOOP  |PARTITIONED|
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                -- ASSIGN  |PARTITIONED|
+                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                    -- REPLICATE  |PARTITIONED|
+                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                        -- DATASOURCE_SCAN  |PARTITIONED|
+                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                            -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                            -- BROADCAST_EXCHANGE  |PARTITIONED|
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                -- ASSIGN  |PARTITIONED|
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- REPLICATE  |PARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          -- DATASOURCE_SCAN  |PARTITIONED|
+                                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                              -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.6.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.6.plan
new file mode 100644
index 0000000..8057837
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.6.plan
@@ -0,0 +1,62 @@
+-- DISTRIBUTE_RESULT  |UNPARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+    -- STREAM_PROJECT  |UNPARTITIONED|
+      -- ASSIGN  |UNPARTITIONED|
+        -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+          -- NESTED_LOOP  |UNPARTITIONED|
+            -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+              -- AGGREGATE  |UNPARTITIONED|
+                -- RANDOM_MERGE_EXCHANGE  |PARTITIONED|
+                  -- AGGREGATE  |PARTITIONED|
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      -- NESTED_LOOP  |PARTITIONED|
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          -- STREAM_PROJECT  |PARTITIONED|
+                            -- ASSIGN  |PARTITIONED|
+                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                -- REPLICATE  |PARTITIONED|
+                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                    -- STREAM_PROJECT  |PARTITIONED|
+                                      -- ASSIGN  |PARTITIONED|
+                                        -- STREAM_PROJECT  |PARTITIONED|
+                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                            -- DATASOURCE_SCAN  |PARTITIONED|
+                                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          -- STREAM_PROJECT  |PARTITIONED|
+                            -- ASSIGN  |PARTITIONED|
+                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                -- REPLICATE  |PARTITIONED|
+                                  -- BROADCAST_EXCHANGE  |PARTITIONED|
+                                    -- STREAM_PROJECT  |PARTITIONED|
+                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                        -- DATASOURCE_SCAN  |PARTITIONED|
+                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                            -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+            -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+              -- AGGREGATE  |UNPARTITIONED|
+                -- SORT_MERGE_EXCHANGE [$$47(ASC) ]  |PARTITIONED|
+                  -- PRE_SORTED_DISTINCT_BY  |PARTITIONED|
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      -- STABLE_SORT [$$47(ASC)]  |PARTITIONED|
+                        -- HASH_PARTITION_EXCHANGE [$$47]  |PARTITIONED|
+                          -- NESTED_LOOP  |PARTITIONED|
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              -- REPLICATE  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    -- ASSIGN  |PARTITIONED|
+                                      -- STREAM_PROJECT  |PARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          -- DATASOURCE_SCAN  |PARTITIONED|
+                                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                              -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              -- REPLICATE  |PARTITIONED|
+                                -- BROADCAST_EXCHANGE  |PARTITIONED|
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- DATASOURCE_SCAN  |PARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.7.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.7.plan
new file mode 100644
index 0000000..3fa0019
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.7.plan
@@ -0,0 +1,139 @@
+-- DISTRIBUTE_RESULT  |UNPARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+    -- STREAM_PROJECT  |UNPARTITIONED|
+      -- ASSIGN  |UNPARTITIONED|
+        -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+          -- NESTED_LOOP  |UNPARTITIONED|
+            -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+              -- NESTED_LOOP  |UNPARTITIONED|
+                -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                  -- AGGREGATE  |UNPARTITIONED|
+                    -- SORT_MERGE_EXCHANGE [$$82(ASC) ]  |PARTITIONED|
+                      -- PRE_SORTED_DISTINCT_BY  |PARTITIONED|
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          -- STABLE_SORT [$$82(ASC)]  |PARTITIONED|
+                            -- HASH_PARTITION_EXCHANGE [$$82]  |PARTITIONED|
+                              -- NESTED_LOOP  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- NESTED_LOOP  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- STREAM_PROJECT  |PARTITIONED|
+                                        -- ASSIGN  |PARTITIONED|
+                                          -- STREAM_PROJECT  |PARTITIONED|
+                                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                              -- REPLICATE  |PARTITIONED|
+                                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                  -- DATASOURCE_SCAN  |PARTITIONED|
+                                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                      -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- REPLICATE  |PARTITIONED|
+                                        -- BROADCAST_EXCHANGE  |PARTITIONED|
+                                          -- STREAM_PROJECT  |PARTITIONED|
+                                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                              -- REPLICATE  |PARTITIONED|
+                                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                  -- DATASOURCE_SCAN  |PARTITIONED|
+                                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                      -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- REPLICATE  |PARTITIONED|
+                                    -- BROADCAST_EXCHANGE  |PARTITIONED|
+                                      -- STREAM_PROJECT  |PARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          -- REPLICATE  |PARTITIONED|
+                                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                              -- DATASOURCE_SCAN  |PARTITIONED|
+                                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                  -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                  -- AGGREGATE  |UNPARTITIONED|
+                    -- RANDOM_MERGE_EXCHANGE  |PARTITIONED|
+                      -- AGGREGATE  |PARTITIONED|
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          -- NESTED_LOOP  |PARTITIONED|
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              -- NESTED_LOOP  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    -- ASSIGN  |PARTITIONED|
+                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                        -- REPLICATE  |PARTITIONED|
+                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                            -- STREAM_PROJECT  |PARTITIONED|
+                                              -- ASSIGN  |PARTITIONED|
+                                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                  -- REPLICATE  |PARTITIONED|
+                                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                      -- DATASOURCE_SCAN  |PARTITIONED|
+                                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                                -- BROADCAST_EXCHANGE  |PARTITIONED|
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    -- ASSIGN  |PARTITIONED|
+                                      -- STREAM_PROJECT  |PARTITIONED|
+                                        -- ASSIGN  |PARTITIONED|
+                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                            -- REPLICATE  |PARTITIONED|
+                                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                -- DATASOURCE_SCAN  |PARTITIONED|
+                                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                    -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                -- ASSIGN  |PARTITIONED|
+                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                    -- REPLICATE  |PARTITIONED|
+                                      -- BROADCAST_EXCHANGE  |PARTITIONED|
+                                        -- STREAM_PROJECT  |PARTITIONED|
+                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                            -- REPLICATE  |PARTITIONED|
+                                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                -- DATASOURCE_SCAN  |PARTITIONED|
+                                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                    -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+            -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+              -- AGGREGATE  |UNPARTITIONED|
+                -- SORT_MERGE_EXCHANGE [$$67(ASC) ]  |PARTITIONED|
+                  -- PRE_SORTED_DISTINCT_BY  |PARTITIONED|
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      -- STABLE_SORT [$$67(ASC)]  |PARTITIONED|
+                        -- HASH_PARTITION_EXCHANGE [$$67]  |PARTITIONED|
+                          -- NESTED_LOOP  |PARTITIONED|
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              -- NESTED_LOOP  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- REPLICATE  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- STREAM_PROJECT  |PARTITIONED|
+                                        -- ASSIGN  |PARTITIONED|
+                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                            -- REPLICATE  |PARTITIONED|
+                                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                -- DATASOURCE_SCAN  |PARTITIONED|
+                                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                    -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    -- ASSIGN  |PARTITIONED|
+                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                        -- REPLICATE  |PARTITIONED|
+                                          -- BROADCAST_EXCHANGE  |PARTITIONED|
+                                            -- STREAM_PROJECT  |PARTITIONED|
+                                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                -- REPLICATE  |PARTITIONED|
+                                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                    -- DATASOURCE_SCAN  |PARTITIONED|
+                                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                        -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                            -- BROADCAST_EXCHANGE  |PARTITIONED|
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                -- ASSIGN  |PARTITIONED|
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    -- ASSIGN  |PARTITIONED|
+                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                        -- REPLICATE  |PARTITIONED|
+                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                            -- DATASOURCE_SCAN  |PARTITIONED|
+                                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.8.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.8.plan
new file mode 100644
index 0000000..5f78826
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.8.plan
@@ -0,0 +1,204 @@
+-- DISTRIBUTE_RESULT  |UNPARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+    -- STREAM_PROJECT  |UNPARTITIONED|
+      -- ASSIGN  |UNPARTITIONED|
+        -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+          -- NESTED_LOOP  |UNPARTITIONED|
+            -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+              -- NESTED_LOOP  |UNPARTITIONED|
+                -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                  -- NESTED_LOOP  |UNPARTITIONED|
+                    -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                      -- AGGREGATE  |UNPARTITIONED|
+                        -- SORT_MERGE_EXCHANGE [$$148(ASC) ]  |PARTITIONED|
+                          -- PRE_SORTED_DISTINCT_BY  |PARTITIONED|
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              -- STABLE_SORT [$$148(ASC)]  |PARTITIONED|
+                                -- HASH_PARTITION_EXCHANGE [$$148]  |PARTITIONED|
+                                  -- NESTED_LOOP  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- NESTED_LOOP  |PARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          -- REPLICATE  |PARTITIONED|
+                                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                              -- STREAM_PROJECT  |PARTITIONED|
+                                                -- ASSIGN  |PARTITIONED|
+                                                  -- STREAM_PROJECT  |PARTITIONED|
+                                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                      -- REPLICATE  |PARTITIONED|
+                                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                          -- DATASOURCE_SCAN  |PARTITIONED|
+                                                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                              -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          -- REPLICATE  |PARTITIONED|
+                                            -- BROADCAST_EXCHANGE  |PARTITIONED|
+                                              -- STREAM_PROJECT  |PARTITIONED|
+                                                -- ASSIGN  |PARTITIONED|
+                                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                    -- REPLICATE  |PARTITIONED|
+                                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                        -- DATASOURCE_SCAN  |PARTITIONED|
+                                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                            -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- REPLICATE  |PARTITIONED|
+                                        -- BROADCAST_EXCHANGE  |PARTITIONED|
+                                          -- STREAM_PROJECT  |PARTITIONED|
+                                            -- ASSIGN  |PARTITIONED|
+                                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                -- REPLICATE  |PARTITIONED|
+                                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                    -- DATASOURCE_SCAN  |PARTITIONED|
+                                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                        -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                    -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                      -- AGGREGATE  |UNPARTITIONED|
+                        -- RANDOM_MERGE_EXCHANGE  |PARTITIONED|
+                          -- AGGREGATE  |PARTITIONED|
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              -- NESTED_LOOP  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- NESTED_LOOP  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- STREAM_PROJECT  |PARTITIONED|
+                                        -- ASSIGN  |PARTITIONED|
+                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                            -- REPLICATE  |PARTITIONED|
+                                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                -- STREAM_PROJECT  |PARTITIONED|
+                                                  -- ASSIGN  |PARTITIONED|
+                                                    -- STREAM_PROJECT  |PARTITIONED|
+                                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                        -- REPLICATE  |PARTITIONED|
+                                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                            -- DATASOURCE_SCAN  |PARTITIONED|
+                                                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                                -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- REPLICATE  |PARTITIONED|
+                                        -- BROADCAST_EXCHANGE  |PARTITIONED|
+                                          -- STREAM_PROJECT  |PARTITIONED|
+                                            -- ASSIGN  |PARTITIONED|
+                                              -- STREAM_PROJECT  |PARTITIONED|
+                                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                  -- REPLICATE  |PARTITIONED|
+                                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                      -- DATASOURCE_SCAN  |PARTITIONED|
+                                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    -- ASSIGN  |PARTITIONED|
+                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                        -- REPLICATE  |PARTITIONED|
+                                          -- BROADCAST_EXCHANGE  |PARTITIONED|
+                                            -- STREAM_PROJECT  |PARTITIONED|
+                                              -- ASSIGN  |PARTITIONED|
+                                                -- STREAM_PROJECT  |PARTITIONED|
+                                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                    -- REPLICATE  |PARTITIONED|
+                                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                        -- DATASOURCE_SCAN  |PARTITIONED|
+                                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                            -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+                  -- AGGREGATE  |UNPARTITIONED|
+                    -- SORT_MERGE_EXCHANGE [$$186(ASC) ]  |PARTITIONED|
+                      -- PRE_SORTED_DISTINCT_BY  |PARTITIONED|
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          -- STABLE_SORT [$$186(ASC)]  |PARTITIONED|
+                            -- HASH_PARTITION_EXCHANGE [$$186]  |PARTITIONED|
+                              -- NESTED_LOOP  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- NESTED_LOOP  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- REPLICATE  |PARTITIONED|
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          -- STREAM_PROJECT  |PARTITIONED|
+                                            -- ASSIGN  |PARTITIONED|
+                                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                -- REPLICATE  |PARTITIONED|
+                                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                    -- DATASOURCE_SCAN  |PARTITIONED|
+                                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                        -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- STREAM_PROJECT  |PARTITIONED|
+                                        -- ASSIGN  |PARTITIONED|
+                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                            -- REPLICATE  |PARTITIONED|
+                                              -- BROADCAST_EXCHANGE  |PARTITIONED|
+                                                -- STREAM_PROJECT  |PARTITIONED|
+                                                  -- ASSIGN  |PARTITIONED|
+                                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                      -- REPLICATE  |PARTITIONED|
+                                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                          -- DATASOURCE_SCAN  |PARTITIONED|
+                                                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                              -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- REPLICATE  |PARTITIONED|
+                                    -- BROADCAST_EXCHANGE  |PARTITIONED|
+                                      -- STREAM_PROJECT  |PARTITIONED|
+                                        -- ASSIGN  |PARTITIONED|
+                                          -- STREAM_PROJECT  |PARTITIONED|
+                                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                              -- REPLICATE  |PARTITIONED|
+                                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                  -- DATASOURCE_SCAN  |PARTITIONED|
+                                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                      -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+            -- ONE_TO_ONE_EXCHANGE  |UNPARTITIONED|
+              -- AGGREGATE  |UNPARTITIONED|
+                -- SORT_MERGE_EXCHANGE [$$86(ASC) ]  |PARTITIONED|
+                  -- PRE_SORTED_DISTINCT_BY  |PARTITIONED|
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      -- STABLE_SORT [$$86(ASC)]  |PARTITIONED|
+                        -- HASH_PARTITION_EXCHANGE [$$86]  |PARTITIONED|
+                          -- NESTED_LOOP  |PARTITIONED|
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              -- NESTED_LOOP  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    -- ASSIGN  |PARTITIONED|
+                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                        -- REPLICATE  |PARTITIONED|
+                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                            -- STREAM_PROJECT  |PARTITIONED|
+                                              -- ASSIGN  |PARTITIONED|
+                                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                  -- REPLICATE  |PARTITIONED|
+                                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                      -- DATASOURCE_SCAN  |PARTITIONED|
+                                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    -- ASSIGN  |PARTITIONED|
+                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                        -- REPLICATE  |PARTITIONED|
+                                          -- BROADCAST_EXCHANGE  |PARTITIONED|
+                                            -- STREAM_PROJECT  |PARTITIONED|
+                                              -- ASSIGN  |PARTITIONED|
+                                                -- STREAM_PROJECT  |PARTITIONED|
+                                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                    -- REPLICATE  |PARTITIONED|
+                                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                        -- DATASOURCE_SCAN  |PARTITIONED|
+                                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                            -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                -- ASSIGN  |PARTITIONED|
+                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                    -- REPLICATE  |PARTITIONED|
+                                      -- BROADCAST_EXCHANGE  |PARTITIONED|
+                                        -- STREAM_PROJECT  |PARTITIONED|
+                                          -- ASSIGN  |PARTITIONED|
+                                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                              -- REPLICATE  |PARTITIONED|
+                                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                  -- DATASOURCE_SCAN  |PARTITIONED|
+                                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                      -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.9.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.9.plan
new file mode 100644
index 0000000..6d8202e
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.9.plan
@@ -0,0 +1,35 @@
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    -- STREAM_PROJECT  |PARTITIONED|
+      -- ASSIGN  |PARTITIONED|
+        -- SORT_MERGE_EXCHANGE [$$g(ASC) ]  |PARTITIONED|
+          -- PRE_CLUSTERED_GROUP_BY[$$g]  |PARTITIONED|
+                  {
+                    -- AGGREGATE  |LOCAL|
+                      -- MICRO_PRE_SORTED_DISTINCT_BY  |LOCAL|
+                        -- IN_MEMORY_STABLE_SORT [$$58(ASC)]  |LOCAL|
+                          -- NESTED_TUPLE_SOURCE  |LOCAL|
+                  }
+            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+              -- STABLE_SORT [$$g(ASC)]  |PARTITIONED|
+                -- HASH_PARTITION_EXCHANGE [$$g]  |PARTITIONED|
+                  -- NESTED_LOOP  |PARTITIONED|
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      -- NESTED_LOOP  |PARTITIONED|
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          -- STREAM_PROJECT  |PARTITIONED|
+                            -- ASSIGN  |PARTITIONED|
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- DATASOURCE_SCAN  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                        -- BROADCAST_EXCHANGE  |PARTITIONED|
+                          -- STREAM_PROJECT  |PARTITIONED|
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              -- DATASOURCE_SCAN  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                    -- BROADCAST_EXCHANGE  |PARTITIONED|
+                      -- UNNEST  |UNPARTITIONED|
+                        -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/group-by/sugar-06-distinct.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/group-by/sugar-06-distinct.plan
index f3a6f3d..f4eea8a 100644
--- a/asterixdb/asterix-app/src/test/resources/optimizerts/results/group-by/sugar-06-distinct.plan
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/group-by/sugar-06-distinct.plan
@@ -3,21 +3,21 @@
     -- STREAM_LIMIT  |UNPARTITIONED|
       -- STREAM_PROJECT  |PARTITIONED|
         -- ASSIGN  |PARTITIONED|
-          -- SORT_MERGE_EXCHANGE [$$53(DESC) ]  |PARTITIONED|
+          -- SORT_MERGE_EXCHANGE [$$51(DESC) ]  |PARTITIONED|
             -- STREAM_LIMIT  |PARTITIONED|
               -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                -- STABLE_SORT [topK: 3] [$$53(DESC)]  |PARTITIONED|
+                -- STABLE_SORT [topK: 3] [$$51(DESC)]  |PARTITIONED|
                   -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                    -- PRE_CLUSTERED_GROUP_BY[$$51]  |PARTITIONED|
+                    -- PRE_CLUSTERED_GROUP_BY[$$49]  |PARTITIONED|
                             {
                               -- AGGREGATE  |LOCAL|
                                 -- MICRO_PRE_SORTED_DISTINCT_BY  |LOCAL|
-                                  -- IN_MEMORY_STABLE_SORT [$$46(ASC)]  |LOCAL|
+                                  -- IN_MEMORY_STABLE_SORT [$$45(ASC)]  |LOCAL|
                                     -- NESTED_TUPLE_SOURCE  |LOCAL|
                             }
                       -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
-                        -- STABLE_SORT [$$51(ASC)]  |PARTITIONED|
-                          -- HASH_PARTITION_EXCHANGE [$$51]  |PARTITIONED|
+                        -- STABLE_SORT [$$49(ASC)]  |PARTITIONED|
+                          -- HASH_PARTITION_EXCHANGE [$$49]  |PARTITIONED|
                             -- STREAM_PROJECT  |PARTITIONED|
                               -- ASSIGN  |PARTITIONED|
                                 -- STREAM_PROJECT  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.1.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.1.ddl.sqlpp
new file mode 100644
index 0000000..9cb9ba6
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.1.ddl.sqlpp
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+drop  dataverse test if exists;
+create  dataverse test;
+
+use test;
+
+create type t1 as {
+  id: bigint,
+  v1: bigint
+};
+
+create dataset d1(t1) primary key id;
+create dataset d2(t1) primary key id;
+create dataset d3(t1) primary key id;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.10.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.10.query.sqlpp
new file mode 100644
index 0000000..dac321d
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.10.query.sqlpp
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Two distinct aggregates on the same field with group by */
+
+use test;
+
+from d1 x, d2 y, range(1, 3) g
+group by g
+select
+  g,
+  count(distinct x.v1) as count_distinct_x,
+  sum(distinct x.v1) as sum_distinct_x
+order by g
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.11.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.11.query.sqlpp
new file mode 100644
index 0000000..acad050
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.11.query.sqlpp
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Two distinct aggregates on different fields with group by */
+
+use test;
+
+from d1 x, d2 y, range(1, 3) g
+group by g
+select
+  g,
+  sum(distinct x.v1) as sum_distinct_x,
+  sum(distinct y.v1) as sum_distinct_y
+order by g
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.12.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.12.query.sqlpp
new file mode 100644
index 0000000..1a09acd
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.12.query.sqlpp
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Two distinct aggregates and one regular on the same field with group by */
+
+use test;
+
+from d1 x, d2 y, range(1, 3) g
+group by g
+select
+  g,
+  sum(x.v1) as sum_x,
+  sum(distinct x.v1) as sum_distinct_x,
+  sum(distinct x.v1) as sum_distinct_y
+order by g
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.13.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.13.query.sqlpp
new file mode 100644
index 0000000..478ff99
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.13.query.sqlpp
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Two distinct aggregates and one regular on different fields with group by */
+
+use test;
+
+from d1 x, d2 y, d3 z, range(1, 3) g
+group by g
+select
+  g,
+  sum(distinct x.v1) as sum_distinct_x,
+  sum(y.v1) as sum_y,
+  sum(distinct z.v1) as sum_distinct_z
+order by g
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.14.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.14.query.sqlpp
new file mode 100644
index 0000000..1b5722b
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.14.query.sqlpp
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Groups of distinct and regular aggregates on various fields with group by */
+
+use test;
+
+from d1 x, d2 y, d3 z, range(1, 3) g
+group by g
+select
+  g,
+  sum(distinct x.v1) as sum_distinct_x,
+  sum(y.v1) as sum_y,
+  sum(distinct z.v1) as sum_distinct_z,
+  avg(distinct x.v1) as avg_distinct_x,
+  avg(distinct y.v1) as avg_distinct_y,
+  count(x.v1) as count_x,
+  count(distinct y.v1) as count_distinct_y,
+  avg(z.v1) as avg_z,
+  count(distinct z.v1) as count_distinct_z
+order by g
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.15.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.15.query.sqlpp
new file mode 100644
index 0000000..19200d0
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.15.query.sqlpp
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Groups of distinct and regular aggregates on various fields */
+/* Unpartitioned data */
+
+use test;
+
+from range(1, 5) x, range(6, 10) y, range(11, 15) z
+select
+  sum(distinct x) as sum_distinct_x,
+  sum(y) as sum_y,
+  sum(distinct z) as sum_distinct_z,
+  avg(distinct x) as avg_distinct_x,
+  avg(distinct y) as avg_distinct_y,
+  count(x) as count_x,
+  count(distinct y) as count_distinct_y,
+  avg(z) as avg_z,
+  count(distinct z) as count_distinct_z
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.16.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.16.query.sqlpp
new file mode 100644
index 0000000..faed1ca
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.16.query.sqlpp
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Groups of distinct and regular aggregates on various fields with group by */
+/* Unpartitioned data */
+
+use test;
+
+from range(1, 5) x, range(6, 10) y, range(11, 15) z, range(1, 3) g
+group by g
+select
+  g,
+  sum(distinct x) as sum_distinct_x,
+  sum(y) as sum_y,
+  sum(distinct z) as sum_distinct_z,
+  avg(distinct x) as avg_distinct_x,
+  avg(distinct y) as avg_distinct_y,
+  count(x) as count_x,
+  count(distinct y) as count_distinct_y,
+  avg(z) as avg_z,
+  count(distinct z) as count_distinct_z
+order by g
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.17.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.17.query.sqlpp
new file mode 100644
index 0000000..0113f0f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.17.query.sqlpp
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Single distinct with union all */
+
+use test;
+
+select t.* from
+(
+
+  select
+    sum(distinct x.v1) as sum_distinct_x,
+    sum(distinct y.v1) as sum_distinct_y
+  from d1 x, d2 y
+  group by true
+
+union all
+
+  select
+    sum(distinct x.v1) as sum_distinct_x,
+    sum(distinct y.v1) as sum_distinct_y
+  from d1 x, d2 y
+  group by false
+
+) t
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.2.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.2.update.sqlpp
new file mode 100644
index 0000000..cdabfff
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.2.update.sqlpp
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+use test;
+
+insert into d1 (
+  ( from range(1, 5) t select t id, t v1 )
+);
+
+insert into d2 (
+  ( from range(6, 10) t select t id, t v1 )
+);
+
+insert into d3 (
+  ( from range(11, 15) t select t id, t v1 )
+);
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.3.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.3.query.sqlpp
new file mode 100644
index 0000000..261344b
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.3.query.sqlpp
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Single distinct aggregate */
+
+use test;
+
+from d1 x, d2 y
+select count(distinct x.v1) as count_distinct_x
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.4.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.4.query.sqlpp
new file mode 100644
index 0000000..cc38ac2
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.4.query.sqlpp
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Two distinct aggregates on the same field */
+
+use test;
+
+from d1 x, d2 y
+select
+  count(distinct x.v1) as count_distinct_x,
+  sum(distinct x.v1) as sum_distinct_x
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.5.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.5.query.sqlpp
new file mode 100644
index 0000000..8dca282
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.5.query.sqlpp
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Two distinct aggregates on different fields */
+
+use test;
+
+from d1 x, d2 y
+select
+  sum(distinct x.v1) as sum_distinct_x,
+  sum(distinct y.v1) as sum_distinct_y
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.6.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.6.query.sqlpp
new file mode 100644
index 0000000..3ca9218
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.6.query.sqlpp
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Two distinct aggregates and one regular on the same field */
+
+use test;
+
+from d1 x, d2 y
+select
+  sum(x.v1) as sum_x,
+  sum(distinct x.v1) as sum_distinct_x,
+  sum(distinct x.v1) as sum_distinct_y
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.7.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.7.query.sqlpp
new file mode 100644
index 0000000..c543c81
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.7.query.sqlpp
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Two distinct aggregates and one regular on different fields */
+
+use test;
+
+from d1 x, d2 y, d3 z
+select
+  sum(distinct x.v1) as sum_distinct_x,
+  sum(y.v1) as sum_y,
+  sum(distinct z.v1) as sum_distinct_z
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.8.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.8.query.sqlpp
new file mode 100644
index 0000000..301ec79
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.8.query.sqlpp
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Groups of distinct and regular aggregates on various fields */
+
+use test;
+
+from d1 x, d2 y, d3 z
+select
+  sum(distinct x.v1) as sum_distinct_x,
+  sum(y.v1) as sum_y,
+  sum(distinct z.v1) as sum_distinct_z,
+  avg(distinct x.v1) as avg_distinct_x,
+  avg(distinct y.v1) as avg_distinct_y,
+  count(x.v1) as count_x,
+  count(distinct y.v1) as count_distinct_y,
+  avg(z.v1) as avg_z,
+  count(distinct z.v1) as count_distinct_z
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.9.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.9.query.sqlpp
new file mode 100644
index 0000000..a8cdc4c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/aggregate-sql-sugar/distinct_mixed/distinct_mixed.9.query.sqlpp
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* Single distinct aggregate with group by */
+
+use test;
+
+from d1 x, d2 y, range(1, 3) g
+group by g
+select
+  g,
+  count(distinct x.v1) as count_distinct_x
+order by g
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.10.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.10.adm
new file mode 100644
index 0000000..e264e6f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.10.adm
@@ -0,0 +1,3 @@
+{ "g": 1, "count_distinct_x": 5, "sum_distinct_x": 15 }
+{ "g": 2, "count_distinct_x": 5, "sum_distinct_x": 15 }
+{ "g": 3, "count_distinct_x": 5, "sum_distinct_x": 15 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.11.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.11.adm
new file mode 100644
index 0000000..903447e
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.11.adm
@@ -0,0 +1,3 @@
+{ "g": 1, "sum_distinct_x": 15, "sum_distinct_y": 40 }
+{ "g": 2, "sum_distinct_x": 15, "sum_distinct_y": 40 }
+{ "g": 3, "sum_distinct_x": 15, "sum_distinct_y": 40 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.12.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.12.adm
new file mode 100644
index 0000000..161bfee
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.12.adm
@@ -0,0 +1,3 @@
+{ "g": 1, "sum_x": 75, "sum_distinct_x": 15, "sum_distinct_y": 15 }
+{ "g": 2, "sum_x": 75, "sum_distinct_x": 15, "sum_distinct_y": 15 }
+{ "g": 3, "sum_x": 75, "sum_distinct_x": 15, "sum_distinct_y": 15 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.13.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.13.adm
new file mode 100644
index 0000000..d4450f1
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.13.adm
@@ -0,0 +1,3 @@
+{ "g": 1, "sum_distinct_x": 15, "sum_y": 1000, "sum_distinct_z": 65 }
+{ "g": 2, "sum_distinct_x": 15, "sum_y": 1000, "sum_distinct_z": 65 }
+{ "g": 3, "sum_distinct_x": 15, "sum_y": 1000, "sum_distinct_z": 65 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.14.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.14.adm
new file mode 100644
index 0000000..a07fff1
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.14.adm
@@ -0,0 +1,3 @@
+{ "g": 1, "sum_distinct_x": 15, "sum_y": 1000, "sum_distinct_z": 65, "avg_distinct_x": 3.0, "avg_distinct_y": 8.0, "count_x": 125, "count_distinct_y": 5, "avg_z": 13.0, "count_distinct_z": 5 }
+{ "g": 2, "sum_distinct_x": 15, "sum_y": 1000, "sum_distinct_z": 65, "avg_distinct_x": 3.0, "avg_distinct_y": 8.0, "count_x": 125, "count_distinct_y": 5, "avg_z": 13.0, "count_distinct_z": 5 }
+{ "g": 3, "sum_distinct_x": 15, "sum_y": 1000, "sum_distinct_z": 65, "avg_distinct_x": 3.0, "avg_distinct_y": 8.0, "count_x": 125, "count_distinct_y": 5, "avg_z": 13.0, "count_distinct_z": 5 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.15.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.15.adm
new file mode 100644
index 0000000..747c99e
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.15.adm
@@ -0,0 +1 @@
+{ "sum_distinct_x": 15, "sum_y": 1000, "sum_distinct_z": 65, "avg_distinct_x": 3.0, "avg_distinct_y": 8.0, "count_x": 125, "count_distinct_y": 5, "avg_z": 13.0, "count_distinct_z": 5 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.16.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.16.adm
new file mode 100644
index 0000000..a07fff1
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.16.adm
@@ -0,0 +1,3 @@
+{ "g": 1, "sum_distinct_x": 15, "sum_y": 1000, "sum_distinct_z": 65, "avg_distinct_x": 3.0, "avg_distinct_y": 8.0, "count_x": 125, "count_distinct_y": 5, "avg_z": 13.0, "count_distinct_z": 5 }
+{ "g": 2, "sum_distinct_x": 15, "sum_y": 1000, "sum_distinct_z": 65, "avg_distinct_x": 3.0, "avg_distinct_y": 8.0, "count_x": 125, "count_distinct_y": 5, "avg_z": 13.0, "count_distinct_z": 5 }
+{ "g": 3, "sum_distinct_x": 15, "sum_y": 1000, "sum_distinct_z": 65, "avg_distinct_x": 3.0, "avg_distinct_y": 8.0, "count_x": 125, "count_distinct_y": 5, "avg_z": 13.0, "count_distinct_z": 5 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.17.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.17.adm
new file mode 100644
index 0000000..a0ec1bd
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.17.adm
@@ -0,0 +1,2 @@
+{ "sum_distinct_x": 15, "sum_distinct_y": 40 }
+{ "sum_distinct_x": 15, "sum_distinct_y": 40 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.3.adm
new file mode 100644
index 0000000..e4de696
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.3.adm
@@ -0,0 +1 @@
+{ "count_distinct_x": 5 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.4.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.4.adm
new file mode 100644
index 0000000..1585f6f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.4.adm
@@ -0,0 +1 @@
+{ "count_distinct_x": 5, "sum_distinct_x": 15 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.5.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.5.adm
new file mode 100644
index 0000000..ba3c1d2
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.5.adm
@@ -0,0 +1 @@
+{ "sum_distinct_x": 15, "sum_distinct_y": 40 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.6.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.6.adm
new file mode 100644
index 0000000..0dc43af
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.6.adm
@@ -0,0 +1 @@
+{ "sum_x": 75, "sum_distinct_x": 15, "sum_distinct_y": 15 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.7.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.7.adm
new file mode 100644
index 0000000..bbd350e
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.7.adm
@@ -0,0 +1 @@
+{ "sum_distinct_x": 15, "sum_y": 1000, "sum_distinct_z": 65 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.8.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.8.adm
new file mode 100644
index 0000000..747c99e
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.8.adm
@@ -0,0 +1 @@
+{ "sum_distinct_x": 15, "sum_y": 1000, "sum_distinct_z": 65, "avg_distinct_x": 3.0, "avg_distinct_y": 8.0, "count_x": 125, "count_distinct_y": 5, "avg_z": 13.0, "count_distinct_z": 5 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.9.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.9.adm
new file mode 100644
index 0000000..b6b0b10
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql-sugar/distinct_mixed/distinct_mixed.9.adm
@@ -0,0 +1,3 @@
+{ "g": 1, "count_distinct_x": 5 }
+{ "g": 2, "count_distinct_x": 5 }
+{ "g": 3, "count_distinct_x": 5 }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql/skewness_distinct/skewness_distinct.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql/skewness_distinct/skewness_distinct.1.adm
index 83b615b..e2282dc 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql/skewness_distinct/skewness_distinct.1.adm
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/aggregate-sql/skewness_distinct/skewness_distinct.1.adm
@@ -1 +1 @@
-{ "t1": 0, "t2": null, "t3": null, "t4": null }
+{ "t1": 0, "t2": null, "t3": 0, "t4": null }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
index 49cb57a..aeb79e4 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -2621,6 +2621,11 @@
   </test-group>
   <test-group name="aggregate-sql-sugar">
     <test-case FilePath="aggregate-sql-sugar">
+      <compilation-unit name="distinct_mixed">
+        <output-dir compare="Text">distinct_mixed</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="aggregate-sql-sugar">
       <compilation-unit name="stddev">
         <output-dir compare="Text">stddev</output-dir>
       </compilation-unit>
diff --git a/asterixdb/asterix-geo/src/main/java/org/apache/asterix/geo/aggregates/ScalarSTUnionAggregateDescriptor.java b/asterixdb/asterix-geo/src/main/java/org/apache/asterix/geo/aggregates/ScalarSTUnionAggregateDescriptor.java
index 96b6478..553d948 100644
--- a/asterixdb/asterix-geo/src/main/java/org/apache/asterix/geo/aggregates/ScalarSTUnionAggregateDescriptor.java
+++ b/asterixdb/asterix-geo/src/main/java/org/apache/asterix/geo/aggregates/ScalarSTUnionAggregateDescriptor.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.geo.aggregates;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.runtime.aggregates.scalar.AbstractScalarAggregateDescriptor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -27,16 +26,13 @@
 public class ScalarSTUnionAggregateDescriptor extends AbstractScalarAggregateDescriptor {
 
     private static final long serialVersionUID = 1L;
+
     public final static FunctionIdentifier FID = BuiltinFunctions.SCALAR_ST_UNION_AGG;
 
-    public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
-        public IFunctionDescriptor createFunctionDescriptor() {
-            return new ScalarSTUnionAggregateDescriptor(STUnionAggregateDescriptor.FACTORY.createFunctionDescriptor());
-        }
-    };
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSTUnionAggregateDescriptor::new;
 
-    private ScalarSTUnionAggregateDescriptor(IFunctionDescriptor aggFuncDesc) {
-        super(aggFuncDesc);
+    private ScalarSTUnionAggregateDescriptor() {
+        super(STUnionAggregateDescriptor.FACTORY);
     }
 
     @Override
diff --git a/asterixdb/asterix-geo/src/main/java/org/apache/asterix/geo/aggregates/ScalarSTUnionDistinctAggregateDescriptor.java b/asterixdb/asterix-geo/src/main/java/org/apache/asterix/geo/aggregates/ScalarSTUnionDistinctAggregateDescriptor.java
new file mode 100644
index 0000000..1c84bfd
--- /dev/null
+++ b/asterixdb/asterix-geo/src/main/java/org/apache/asterix/geo/aggregates/ScalarSTUnionDistinctAggregateDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.geo.aggregates;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.aggregates.scalar.AbstractScalarDistinctAggregateDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class ScalarSTUnionDistinctAggregateDescriptor extends AbstractScalarDistinctAggregateDescriptor {
+
+    private static final long serialVersionUID = 1L;
+
+    public final static FunctionIdentifier FID = BuiltinFunctions.SCALAR_ST_UNION_AGG_DISTINCT;
+
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSTUnionDistinctAggregateDescriptor::new;
+
+    private ScalarSTUnionDistinctAggregateDescriptor() {
+        super(STUnionAggregateDescriptor.FACTORY);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-geo/src/main/java/org/apache/asterix/geo/evaluators/GeoFunctionRegistrant.java b/asterixdb/asterix-geo/src/main/java/org/apache/asterix/geo/evaluators/GeoFunctionRegistrant.java
index 1644b99..deef79a 100644
--- a/asterixdb/asterix-geo/src/main/java/org/apache/asterix/geo/evaluators/GeoFunctionRegistrant.java
+++ b/asterixdb/asterix-geo/src/main/java/org/apache/asterix/geo/evaluators/GeoFunctionRegistrant.java
@@ -20,6 +20,7 @@
 
 import org.apache.asterix.geo.aggregates.STUnionAggregateDescriptor;
 import org.apache.asterix.geo.aggregates.ScalarSTUnionAggregateDescriptor;
+import org.apache.asterix.geo.aggregates.ScalarSTUnionDistinctAggregateDescriptor;
 import org.apache.asterix.geo.evaluators.functions.ParseGeoJSONDescriptor;
 import org.apache.asterix.geo.evaluators.functions.STAreaDescriptor;
 import org.apache.asterix.geo.evaluators.functions.STAsBinaryDescriptor;
@@ -88,6 +89,7 @@
     public void register(IFunctionCollection fc) {
         //Geo functions
         fc.add(ScalarSTUnionAggregateDescriptor.FACTORY);
+        fc.add(ScalarSTUnionDistinctAggregateDescriptor.FACTORY);
         fc.add(STUnionAggregateDescriptor.FACTORY);
 
         //GeoJSON
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java
index 47c9ad1..28851fd 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java
@@ -64,7 +64,6 @@
 import org.apache.asterix.lang.sqlpp.rewrites.visitor.OperatorExpressionVisitor;
 import org.apache.asterix.lang.sqlpp.rewrites.visitor.SetOperationVisitor;
 import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppBuiltinFunctionRewriteVisitor;
-import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppDistinctAggregationSugarVisitor;
 import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppGroupByAggregationSugarVisitor;
 import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppGroupByVisitor;
 import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppInlineUdfsVisitor;
@@ -166,9 +165,6 @@
         // names could be case sensitive.
         rewriteFunctionNames();
 
-        // Rewrites distinct aggregates into regular aggregates
-        rewriteDistinctAggregations();
-
         // Sets the var counter of the query.
         topStatement.setVarCounter(context.getVarCounter().get());
     }
@@ -178,12 +174,6 @@
         rewriteTopExpr(visitor, null);
     }
 
-    protected void rewriteDistinctAggregations() throws CompilationException {
-        SqlppDistinctAggregationSugarVisitor distinctAggregationVisitor =
-                new SqlppDistinctAggregationSugarVisitor(context);
-        rewriteTopExpr(distinctAggregationVisitor, null);
-    }
-
     protected void rewriteListInputFunctions() throws CompilationException {
         SqlppListInputFunctionRewriteVisitor listInputFunctionVisitor = new SqlppListInputFunctionRewriteVisitor();
         rewriteTopExpr(listInputFunctionVisitor, null);
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppDistinctAggregationSugarVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppDistinctAggregationSugarVisitor.java
deleted file mode 100644
index 180f80c..0000000
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppDistinctAggregationSugarVisitor.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.asterix.lang.sqlpp.rewrites.visitor;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.asterix.common.exceptions.CompilationException;
-import org.apache.asterix.common.functions.FunctionSignature;
-import org.apache.asterix.lang.common.base.Expression;
-import org.apache.asterix.lang.common.base.ILangExpression;
-import org.apache.asterix.lang.common.expression.CallExpr;
-import org.apache.asterix.lang.common.expression.VariableExpr;
-import org.apache.asterix.lang.common.rewrites.LangRewritingContext;
-import org.apache.asterix.lang.common.util.FunctionUtil;
-import org.apache.asterix.lang.sqlpp.clause.FromClause;
-import org.apache.asterix.lang.sqlpp.clause.FromTerm;
-import org.apache.asterix.lang.sqlpp.clause.SelectBlock;
-import org.apache.asterix.lang.sqlpp.clause.SelectClause;
-import org.apache.asterix.lang.sqlpp.clause.SelectElement;
-import org.apache.asterix.lang.sqlpp.clause.SelectSetOperation;
-import org.apache.asterix.lang.sqlpp.expression.SelectExpression;
-import org.apache.asterix.lang.sqlpp.expression.WindowExpression;
-import org.apache.asterix.lang.sqlpp.struct.SetOperationInput;
-import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppSimpleExpressionVisitor;
-import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
-import org.apache.hyracks.algebricks.core.algebra.functions.IFunctionInfo;
-import org.apache.hyracks.api.exceptions.SourceLocation;
-
-/**
- * An AST pre-processor to rewrite distinct aggregates into regular aggregates as follows: <br/>
- * {@code agg-distinct(expr) -> agg((FROM expr AS i SELECT DISTINCT VALUE i))} <br/>
- * where {@code agg-distinct} is a distinct aggregate function, {@code agg} - a regular aggregate function
- */
-public class SqlppDistinctAggregationSugarVisitor extends AbstractSqlppSimpleExpressionVisitor {
-    protected final LangRewritingContext context;
-
-    public SqlppDistinctAggregationSugarVisitor(LangRewritingContext context) {
-        this.context = context;
-    }
-
-    @Override
-    public Expression visit(CallExpr callExpr, ILangExpression arg) throws CompilationException {
-        FunctionIdentifier newAggFn = getAggregateFunctionForDistinct(callExpr.getFunctionSignature());
-        if (newAggFn == null) {
-            return super.visit(callExpr, arg);
-        }
-        List<Expression> newExprList = rewriteArgumentList(callExpr.getExprList(), arg);
-        callExpr.setFunctionSignature(new FunctionSignature(newAggFn));
-        callExpr.setExprList(newExprList);
-        return callExpr;
-    }
-
-    @Override
-    public Expression visit(WindowExpression winExpr, ILangExpression arg) throws CompilationException {
-        FunctionIdentifier newAggFn = getAggregateFunctionForDistinct(winExpr.getFunctionSignature());
-        if (newAggFn == null) {
-            return super.visit(winExpr, arg);
-        }
-        List<Expression> newExprList = rewriteArgumentList(winExpr.getExprList(), arg);
-        winExpr.setFunctionSignature(new FunctionSignature(newAggFn));
-        winExpr.setExprList(newExprList);
-        return winExpr;
-    }
-
-    private FunctionIdentifier getAggregateFunctionForDistinct(FunctionSignature signature) {
-        IFunctionInfo finfo = FunctionUtil.getFunctionInfo(signature);
-        FunctionIdentifier aggFn =
-                finfo != null ? BuiltinFunctions.getAggregateFunction(finfo.getFunctionIdentifier()) : null;
-        return aggFn != null ? BuiltinFunctions.getAggregateFunctionForDistinct(aggFn) : null;
-    }
-
-    private List<Expression> rewriteArgumentList(List<Expression> exprList, ILangExpression arg)
-            throws CompilationException {
-        List<Expression> result = new ArrayList<>(exprList.size());
-        for (Expression expr : exprList) {
-            Expression newExpr = rewriteArgument(expr);
-            result.add(newExpr.accept(this, arg));
-        }
-        return result;
-    }
-
-    /**
-     * rewrites {@code expr -> FROM expr AS i SELECT DISTINCT VALUE i}
-     */
-    private Expression rewriteArgument(Expression argExpr) throws CompilationException {
-        SourceLocation sourceLoc = argExpr.getSourceLocation();
-        // From clause
-        VariableExpr fromBindingVar = new VariableExpr(context.newVariable());
-        fromBindingVar.setSourceLocation(sourceLoc);
-        FromTerm fromTerm = new FromTerm(argExpr, fromBindingVar, null, null);
-        fromTerm.setSourceLocation(sourceLoc);
-        FromClause fromClause = new FromClause(Collections.singletonList(fromTerm));
-        fromClause.setSourceLocation(sourceLoc);
-
-        // Select clause.
-        SelectElement selectElement = new SelectElement(fromBindingVar);
-        selectElement.setSourceLocation(sourceLoc);
-        SelectClause selectClause = new SelectClause(selectElement, null, true);
-        selectClause.setSourceLocation(sourceLoc);
-
-        // Construct the select expression.
-        SelectBlock selectBlock = new SelectBlock(selectClause, fromClause, null, null, null);
-        selectBlock.setSourceLocation(sourceLoc);
-        SelectSetOperation selectSetOperation = new SelectSetOperation(new SetOperationInput(selectBlock, null), null);
-        selectSetOperation.setSourceLocation(sourceLoc);
-        SelectExpression selectExpr = new SelectExpression(null, selectSetOperation, null, null, true);
-        selectExpr.setSourceLocation(sourceLoc);
-        return selectExpr;
-    }
-}
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
index 952cc86..382b1b2 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
@@ -169,8 +169,7 @@
     private static final Map<IFunctionInfo, IFunctionInfo> aggregateToSerializableAggregate = new HashMap<>();
     private static final Map<IFunctionInfo, Boolean> builtinUnnestingFunctions = new HashMap<>();
     private static final Map<IFunctionInfo, IFunctionInfo> scalarToAggregateFunctionMap = new HashMap<>();
-    private static final Map<IFunctionInfo, IFunctionInfo> distinctToRegularScalarAggregateFunctionMap =
-            new HashMap<>();
+    private static final Map<IFunctionInfo, IFunctionInfo> distinctToRegularAggregateFunctionMap = new HashMap<>();
     private static final Map<IFunctionInfo, IFunctionInfo> sqlToWindowFunctions = new HashMap<>();
     private static final Set<IFunctionInfo> windowFunctions = new HashSet<>();
 
@@ -1304,8 +1303,12 @@
             new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "st-length", 1);
     public static final FunctionIdentifier SCALAR_ST_UNION_AGG =
             new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "st-union", 1);
+    public static final FunctionIdentifier SCALAR_ST_UNION_AGG_DISTINCT =
+            new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "st-union-distinct", 1);
     public static final FunctionIdentifier ST_UNION_AGG =
             new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "st-union-agg", 1);
+    public static final FunctionIdentifier ST_UNION_AGG_DISTINCT =
+            new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "st-union-agg-distinct", 1);
     public static final FunctionIdentifier ST_GEOM_FROM_TEXT =
             new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "st-geom-from-text", 1);
     public static final FunctionIdentifier ST_GEOM_FROM_TEXT_SRID =
@@ -2120,6 +2123,7 @@
         addFunction(ST_INTERSECTION, AGeometryTypeComputer.INSTANCE, true);
         addFunction(ST_SYM_DIFFERENCE, AGeometryTypeComputer.INSTANCE, true);
         addFunction(SCALAR_ST_UNION_AGG, AGeometryTypeComputer.INSTANCE, true);
+        addFunction(SCALAR_ST_UNION_AGG_DISTINCT, AGeometryTypeComputer.INSTANCE, true);
         addPrivateFunction(ST_UNION_AGG, AGeometryTypeComputer.INSTANCE, true);
         addFunction(ST_POLYGONIZE, AGeometryTypeComputer.INSTANCE, true);
 
@@ -2319,7 +2323,7 @@
 
         // AVG DISTINCT
 
-        addDistinctAgg(AVG_DISTINCT, SCALAR_AVG);
+        addDistinctAgg(AVG_DISTINCT, AVG);
         addScalarAgg(AVG_DISTINCT, SCALAR_AVG_DISTINCT);
 
         // COUNT
@@ -2339,7 +2343,7 @@
 
         // COUNT DISTINCT
 
-        addDistinctAgg(COUNT_DISTINCT, SCALAR_COUNT);
+        addDistinctAgg(COUNT_DISTINCT, COUNT);
         addScalarAgg(COUNT_DISTINCT, SCALAR_COUNT_DISTINCT);
 
         // MAX
@@ -2355,7 +2359,7 @@
 
         // MAX DISTINCT
 
-        addDistinctAgg(MAX_DISTINCT, SCALAR_MAX);
+        addDistinctAgg(MAX_DISTINCT, MAX);
         addScalarAgg(MAX_DISTINCT, SCALAR_MAX_DISTINCT);
 
         // STDDEV_SAMP
@@ -2387,7 +2391,7 @@
 
         // STDDEV_SAMP DISTINCT
 
-        addDistinctAgg(STDDEV_SAMP_DISTINCT, SCALAR_STDDEV_SAMP);
+        addDistinctAgg(STDDEV_SAMP_DISTINCT, STDDEV_SAMP);
         addScalarAgg(STDDEV_SAMP_DISTINCT, SCALAR_STDDEV_SAMP_DISTINCT);
 
         // STDDEV_POP
@@ -2419,7 +2423,7 @@
 
         // STDDEV_POP DISTINCT
 
-        addDistinctAgg(STDDEV_POP_DISTINCT, SCALAR_STDDEV_POP);
+        addDistinctAgg(STDDEV_POP_DISTINCT, STDDEV_POP);
         addScalarAgg(STDDEV_POP_DISTINCT, SCALAR_STDDEV_POP_DISTINCT);
 
         // VAR_SAMP
@@ -2451,7 +2455,7 @@
 
         // VAR_SAMP DISTINCT
 
-        addDistinctAgg(VAR_SAMP_DISTINCT, SCALAR_VAR_SAMP);
+        addDistinctAgg(VAR_SAMP_DISTINCT, VAR_SAMP);
         addScalarAgg(VAR_SAMP_DISTINCT, SCALAR_VAR_SAMP_DISTINCT);
 
         // VAR_POP
@@ -2483,7 +2487,7 @@
 
         // VAR_POP DISTINCT
 
-        addDistinctAgg(VAR_POP_DISTINCT, SCALAR_VAR_POP);
+        addDistinctAgg(VAR_POP_DISTINCT, VAR_POP);
         addScalarAgg(VAR_POP_DISTINCT, SCALAR_VAR_POP_DISTINCT);
 
         // SKEWNESS
@@ -2515,7 +2519,7 @@
 
         // SKEWNESS DISTINCT
 
-        addDistinctAgg(SKEWNESS_DISTINCT, SCALAR_SKEWNESS);
+        addDistinctAgg(SKEWNESS_DISTINCT, SKEWNESS);
         addScalarAgg(SKEWNESS_DISTINCT, SCALAR_SKEWNESS_DISTINCT);
 
         // KURTOSIS
@@ -2547,7 +2551,7 @@
 
         // KURTOSIS DISTINCT
 
-        addDistinctAgg(KURTOSIS_DISTINCT, SCALAR_KURTOSIS);
+        addDistinctAgg(KURTOSIS_DISTINCT, KURTOSIS);
         addScalarAgg(KURTOSIS_DISTINCT, SCALAR_KURTOSIS_DISTINCT);
 
         // FIRST_ELEMENT
@@ -2587,7 +2591,7 @@
 
         // MIN DISTINCT
 
-        addDistinctAgg(MIN_DISTINCT, SCALAR_MIN);
+        addDistinctAgg(MIN_DISTINCT, MIN);
         addScalarAgg(MIN_DISTINCT, SCALAR_MIN_DISTINCT);
 
         // SUM
@@ -2614,7 +2618,7 @@
         addGlobalAgg(SERIAL_SUM, SERIAL_GLOBAL_SUM);
 
         // SUM Distinct
-        addDistinctAgg(SUM_DISTINCT, SCALAR_SUM);
+        addDistinctAgg(SUM_DISTINCT, SUM);
         addScalarAgg(SUM_DISTINCT, SCALAR_SUM_DISTINCT);
 
         // LISTIFY
@@ -2814,37 +2818,37 @@
 
         // SQL AVG DISTINCT
 
-        addDistinctAgg(SQL_AVG_DISTINCT, SCALAR_SQL_AVG);
+        addDistinctAgg(SQL_AVG_DISTINCT, SQL_AVG);
         addScalarAgg(SQL_AVG_DISTINCT, SCALAR_SQL_AVG_DISTINCT);
 
         // SQL STDDEV_SAMP DISTINCT
 
-        addDistinctAgg(SQL_STDDEV_SAMP_DISTINCT, SCALAR_SQL_STDDEV_SAMP);
+        addDistinctAgg(SQL_STDDEV_SAMP_DISTINCT, SQL_STDDEV_SAMP);
         addScalarAgg(SQL_STDDEV_SAMP_DISTINCT, SCALAR_SQL_STDDEV_SAMP_DISTINCT);
 
         // SQL STDDEV_POP DISTINCT
 
-        addDistinctAgg(SQL_STDDEV_POP_DISTINCT, SCALAR_SQL_STDDEV_POP);
+        addDistinctAgg(SQL_STDDEV_POP_DISTINCT, SQL_STDDEV_POP);
         addScalarAgg(SQL_STDDEV_POP_DISTINCT, SCALAR_SQL_STDDEV_POP_DISTINCT);
 
         // SQL VAR_SAMP DISTINCT
 
-        addDistinctAgg(SQL_VAR_SAMP_DISTINCT, SCALAR_SQL_VAR_SAMP);
+        addDistinctAgg(SQL_VAR_SAMP_DISTINCT, SQL_VAR_SAMP);
         addScalarAgg(SQL_VAR_SAMP_DISTINCT, SCALAR_SQL_VAR_SAMP_DISTINCT);
 
         // SQL VAR_POP DISTINCT
 
-        addDistinctAgg(SQL_VAR_POP_DISTINCT, SCALAR_SQL_VAR_POP);
+        addDistinctAgg(SQL_VAR_POP_DISTINCT, SQL_VAR_POP);
         addScalarAgg(SQL_VAR_POP_DISTINCT, SCALAR_SQL_VAR_POP_DISTINCT);
 
         // SQL SKEWNESS DISTINCT
 
-        addDistinctAgg(SQL_SKEWNESS_DISTINCT, SCALAR_SQL_SKEWNESS);
+        addDistinctAgg(SQL_SKEWNESS_DISTINCT, SQL_SKEWNESS);
         addScalarAgg(SQL_SKEWNESS_DISTINCT, SCALAR_SQL_SKEWNESS_DISTINCT);
 
         // SQL KURTOSIS DISTINCT
 
-        addDistinctAgg(SQL_KURTOSIS_DISTINCT, SCALAR_SQL_KURTOSIS);
+        addDistinctAgg(SQL_KURTOSIS_DISTINCT, SQL_KURTOSIS);
         addScalarAgg(SQL_KURTOSIS_DISTINCT, SCALAR_SQL_KURTOSIS_DISTINCT);
 
         // SQL COUNT
@@ -2864,7 +2868,7 @@
 
         // SQL COUNT DISTINCT
 
-        addDistinctAgg(SQL_COUNT_DISTINCT, SCALAR_SQL_COUNT);
+        addDistinctAgg(SQL_COUNT_DISTINCT, SQL_COUNT);
         addScalarAgg(SQL_COUNT_DISTINCT, SCALAR_SQL_COUNT_DISTINCT);
 
         // SQL MAX
@@ -2880,7 +2884,7 @@
 
         // SQL MAX DISTINCT
 
-        addDistinctAgg(SQL_MAX_DISTINCT, SCALAR_SQL_MAX);
+        addDistinctAgg(SQL_MAX_DISTINCT, SQL_MAX);
         addScalarAgg(SQL_MAX_DISTINCT, SCALAR_SQL_MAX_DISTINCT);
 
         // SQL MIN
@@ -2895,7 +2899,7 @@
 
         // SQL MIN DISTINCT
 
-        addDistinctAgg(SQL_MIN_DISTINCT, SCALAR_SQL_MIN);
+        addDistinctAgg(SQL_MIN_DISTINCT, SQL_MIN);
         addScalarAgg(SQL_MIN_DISTINCT, SCALAR_SQL_MIN_DISTINCT);
 
         // SQL SUM
@@ -2922,7 +2926,7 @@
         addGlobalAgg(SERIAL_SQL_SUM, SERIAL_GLOBAL_SQL_SUM);
 
         // SQL SUM DISTINCT
-        addDistinctAgg(SQL_SUM_DISTINCT, SCALAR_SQL_SUM);
+        addDistinctAgg(SQL_SUM_DISTINCT, SQL_SUM);
         addScalarAgg(SQL_SUM_DISTINCT, SCALAR_SQL_SUM_DISTINCT);
 
         // SPATIAL AGGREGATES
@@ -2931,6 +2935,9 @@
         addLocalAgg(ST_UNION_AGG, ST_UNION_AGG);
         addIntermediateAgg(ST_UNION_AGG, ST_UNION_AGG);
         addGlobalAgg(ST_UNION_AGG, ST_UNION_AGG);
+        addScalarAgg(ST_UNION_AGG, SCALAR_ST_UNION_AGG);
+        addDistinctAgg(ST_UNION_AGG_DISTINCT, ST_UNION_AGG);
+        addScalarAgg(ST_UNION_AGG_DISTINCT, SCALAR_ST_UNION_AGG_DISTINCT);
     }
 
     interface BuiltinFunctionProperty {
@@ -3088,7 +3095,7 @@
 
     public static FunctionIdentifier getAggregateFunctionForDistinct(FunctionIdentifier distinctVersionOfAggregate) {
         IFunctionInfo finfo =
-                distinctToRegularScalarAggregateFunctionMap.get(getAsterixFunctionInfo(distinctVersionOfAggregate));
+                distinctToRegularAggregateFunctionMap.get(getAsterixFunctionInfo(distinctVersionOfAggregate));
         return finfo == null ? null : finfo.getFunctionIdentifier();
     }
 
@@ -3157,9 +3164,8 @@
         scalarToAggregateFunctionMap.put(getAsterixFunctionInfo(scalarfi), getAsterixFunctionInfo(fi));
     }
 
-    public static void addDistinctAgg(FunctionIdentifier distinctfi, FunctionIdentifier regularscalarfi) {
-        distinctToRegularScalarAggregateFunctionMap.put(getAsterixFunctionInfo(distinctfi),
-                getAsterixFunctionInfo(regularscalarfi));
+    public static void addDistinctAgg(FunctionIdentifier distinctfi, FunctionIdentifier fi) {
+        distinctToRegularAggregateFunctionMap.put(getAsterixFunctionInfo(distinctfi), getAsterixFunctionInfo(fi));
     }
 
     public static void addWindowFunction(FunctionIdentifier sqlfi, FunctionIdentifier winfi,
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/AbstractScalarAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/AbstractScalarAggregateDescriptor.java
index 7538e43..de376c6 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/AbstractScalarAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/AbstractScalarAggregateDescriptor.java
@@ -18,11 +18,12 @@
  */
 package org.apache.asterix.runtime.aggregates.scalar;
 
-import org.apache.asterix.om.functions.IFunctionDescriptor;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.runtime.aggregates.base.AbstractAggregateFunctionDynamicDescriptor;
 import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor;
 import org.apache.asterix.runtime.unnestingfunctions.std.ScanCollectionDescriptor.ScanCollectionUnnestingFunctionFactory;
 import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.runtime.base.IAggregateEvaluator;
 import org.apache.hyracks.algebricks.runtime.base.IAggregateEvaluatorFactory;
 import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator;
 import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
@@ -35,8 +36,8 @@
 
     protected final AbstractAggregateFunctionDynamicDescriptor aggFuncDesc;
 
-    protected AbstractScalarAggregateDescriptor(IFunctionDescriptor aggFuncDesc) {
-        this.aggFuncDesc = (AbstractAggregateFunctionDynamicDescriptor) aggFuncDesc;
+    public AbstractScalarAggregateDescriptor(IFunctionDescriptorFactory aggFuncDescFactory) {
+        this.aggFuncDesc = (AbstractAggregateFunctionDynamicDescriptor) aggFuncDescFactory.createFunctionDescriptor();
     }
 
     @Override
@@ -57,9 +58,15 @@
                 // Use ScanCollection to iterate over list items.
                 ScanCollectionUnnestingFunctionFactory scanCollectionFactory =
                         new ScanCollectionUnnestingFunctionFactory(args[0], sourceLoc);
-                return new GenericScalarAggregateFunction(aggFuncFactory.createAggregateEvaluator(ctx),
+                return createScalarAggregateEvaluator(aggFuncFactory.createAggregateEvaluator(ctx),
                         scanCollectionFactory, ctx);
             }
         };
     }
+
+    protected IScalarEvaluator createScalarAggregateEvaluator(IAggregateEvaluator aggEval,
+            ScanCollectionUnnestingFunctionFactory scanCollectionFactory, IHyracksTaskContext ctx)
+            throws HyracksDataException {
+        return new GenericScalarAggregateFunction(aggEval, scanCollectionFactory, ctx, sourceLoc);
+    }
 }
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/AbstractScalarDistinctAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/AbstractScalarDistinctAggregateDescriptor.java
new file mode 100644
index 0000000..2876d40
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/AbstractScalarDistinctAggregateDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.scalar;
+
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.unnestingfunctions.std.ScanCollectionDescriptor;
+import org.apache.hyracks.algebricks.runtime.base.IAggregateEvaluator;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator;
+import org.apache.hyracks.api.context.IHyracksTaskContext;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+
+public abstract class AbstractScalarDistinctAggregateDescriptor extends AbstractScalarAggregateDescriptor {
+
+    private static final long serialVersionUID = 1L;
+
+    public AbstractScalarDistinctAggregateDescriptor(IFunctionDescriptorFactory aggFuncDescFactory) {
+        super(aggFuncDescFactory);
+    }
+
+    @Override
+    protected IScalarEvaluator createScalarAggregateEvaluator(IAggregateEvaluator aggEval,
+            ScanCollectionDescriptor.ScanCollectionUnnestingFunctionFactory scanCollectionFactory,
+            IHyracksTaskContext ctx) throws HyracksDataException {
+        return new GenericScalarDistinctAggregateFunction(aggEval, scanCollectionFactory, ctx, sourceLoc);
+    }
+}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/GenericScalarAggregateFunction.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/GenericScalarAggregateFunction.java
index 9cec06d..7de3ab6 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/GenericScalarAggregateFunction.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/GenericScalarAggregateFunction.java
@@ -25,36 +25,54 @@
 import org.apache.hyracks.algebricks.runtime.base.IUnnestingEvaluatorFactory;
 import org.apache.hyracks.api.context.IHyracksTaskContext;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.api.exceptions.SourceLocation;
 import org.apache.hyracks.data.std.api.IPointable;
 import org.apache.hyracks.data.std.primitive.VoidPointable;
 import org.apache.hyracks.dataflow.common.data.accessors.IFrameTupleReference;
 
 /**
  * Implements scalar aggregates by iterating over a collection with the ScanCollection unnesting function,
- * and applying the corresponding ICopyAggregateFunction to each collection-item.
+ * and applying the corresponding aggregate function to each collection-item.
  */
 public class GenericScalarAggregateFunction implements IScalarEvaluator {
 
-    private final IPointable listItemOut = new VoidPointable();
     private final IAggregateEvaluator aggFunc;
+
     private final IUnnestingEvaluator scanCollection;
 
+    private final IPointable listItemOut = new VoidPointable();
+
     private final SingleFieldFrameTupleReference itemTuple = new SingleFieldFrameTupleReference();
 
+    protected final SourceLocation sourceLoc;
+
     public GenericScalarAggregateFunction(IAggregateEvaluator aggFunc, IUnnestingEvaluatorFactory scanCollectionFactory,
-            IHyracksTaskContext context) throws HyracksDataException {
+            IHyracksTaskContext context, SourceLocation sourceLoc) throws HyracksDataException {
         this.aggFunc = aggFunc;
         this.scanCollection = scanCollectionFactory.createUnnestingEvaluator(context);
+        this.sourceLoc = sourceLoc;
     }
 
     @Override
     public void evaluate(IFrameTupleReference tuple, IPointable result) throws HyracksDataException {
         scanCollection.init(tuple);
-        aggFunc.init();
+        aggInit();
         while (scanCollection.step(listItemOut)) {
-            itemTuple.reset(listItemOut.getByteArray(), listItemOut.getStartOffset(), listItemOut.getLength());
-            aggFunc.step(itemTuple);
+            aggStep(listItemOut);
         }
+        aggFinish(result);
+    }
+
+    protected void aggInit() throws HyracksDataException {
+        aggFunc.init();
+    }
+
+    protected void aggStep(IPointable item) throws HyracksDataException {
+        itemTuple.reset(item.getByteArray(), item.getStartOffset(), item.getLength());
+        aggFunc.step(itemTuple);
+    }
+
+    protected void aggFinish(IPointable result) throws HyracksDataException {
         aggFunc.finish(result);
     }
 }
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/GenericScalarDistinctAggregateFunction.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/GenericScalarDistinctAggregateFunction.java
new file mode 100644
index 0000000..2752730
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/GenericScalarDistinctAggregateFunction.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.scalar;
+
+import java.util.List;
+
+import org.apache.asterix.builders.AbvsBuilderFactory;
+import org.apache.asterix.builders.ArrayListFactory;
+import org.apache.asterix.om.types.ATypeTag;
+import org.apache.asterix.om.util.container.IObjectPool;
+import org.apache.asterix.om.util.container.ListObjectPool;
+import org.apache.asterix.runtime.aggregates.utils.PointableHashSet;
+import org.apache.hyracks.algebricks.runtime.base.IAggregateEvaluator;
+import org.apache.hyracks.algebricks.runtime.base.IUnnestingEvaluatorFactory;
+import org.apache.hyracks.api.context.IHyracksTaskContext;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.api.exceptions.SourceLocation;
+import org.apache.hyracks.data.std.api.IMutableValueStorage;
+import org.apache.hyracks.data.std.api.IPointable;
+import org.apache.hyracks.data.std.util.ArrayBackedValueStorage;
+
+/**
+ * Implements scalar distinct aggregates by iterating over a collection with the ScanCollection unnesting function,
+ * and applying the corresponding aggregate function to each distinct collection-item.
+ */
+public class GenericScalarDistinctAggregateFunction extends GenericScalarAggregateFunction {
+
+    private final IObjectPool<IMutableValueStorage, ATypeTag> storageAllocator;
+
+    private final IObjectPool<List<IPointable>, ATypeTag> arrayListAllocator;
+
+    private final PointableHashSet itemSet;
+
+    public GenericScalarDistinctAggregateFunction(IAggregateEvaluator aggFunc,
+            IUnnestingEvaluatorFactory scanCollectionFactory, IHyracksTaskContext context, SourceLocation sourceLoc)
+            throws HyracksDataException {
+        super(aggFunc, scanCollectionFactory, context, sourceLoc);
+        storageAllocator = new ListObjectPool<>(new AbvsBuilderFactory());
+        arrayListAllocator = new ListObjectPool<>(new ArrayListFactory<>());
+        itemSet = new PointableHashSet(arrayListAllocator, sourceLoc) {
+            @Override
+            protected IPointable makeStoredItem(IPointable item) throws HyracksDataException {
+                ArrayBackedValueStorage abvs = (ArrayBackedValueStorage) storageAllocator.allocate(null);
+                abvs.assign(item);
+                return abvs;
+            }
+        };
+    }
+
+    @Override
+    protected void aggInit() throws HyracksDataException {
+        super.aggInit();
+
+        storageAllocator.reset();
+        arrayListAllocator.reset();
+        itemSet.clear();
+    }
+
+    @Override
+    protected void aggStep(IPointable item) throws HyracksDataException {
+        if (itemSet.add(item)) {
+            super.aggStep(item);
+        }
+    }
+}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarAvgAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarAvgAggregateDescriptor.java
index 9fb994a..07b5ec3 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarAvgAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarAvgAggregateDescriptor.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.runtime.aggregates.scalar;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.runtime.aggregates.std.AvgAggregateDescriptor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -28,17 +27,12 @@
 
     private static final long serialVersionUID = 1L;
 
-    public final static FunctionIdentifier FID = BuiltinFunctions.SCALAR_AVG;
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_AVG;
 
-    public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
-        @Override
-        public IFunctionDescriptor createFunctionDescriptor() {
-            return new ScalarAvgAggregateDescriptor(AvgAggregateDescriptor.FACTORY.createFunctionDescriptor());
-        }
-    };
+    public static final IFunctionDescriptorFactory FACTORY = ScalarAvgAggregateDescriptor::new;
 
-    private ScalarAvgAggregateDescriptor(IFunctionDescriptor aggFuncDesc) {
-        super(aggFuncDesc);
+    private ScalarAvgAggregateDescriptor() {
+        super(AvgAggregateDescriptor.FACTORY);
     }
 
     @Override
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarAvgDistinctAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarAvgDistinctAggregateDescriptor.java
new file mode 100644
index 0000000..5e5bfb0
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarAvgDistinctAggregateDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.scalar;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.aggregates.std.AvgAggregateDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class ScalarAvgDistinctAggregateDescriptor extends AbstractScalarDistinctAggregateDescriptor {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_AVG_DISTINCT;
+
+    public static final IFunctionDescriptorFactory FACTORY = ScalarAvgDistinctAggregateDescriptor::new;
+
+    private ScalarAvgDistinctAggregateDescriptor() {
+        super(AvgAggregateDescriptor.FACTORY);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarCountAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarCountAggregateDescriptor.java
index 6aba99f..e96da20 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarCountAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarCountAggregateDescriptor.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.runtime.aggregates.scalar;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.runtime.aggregates.std.CountAggregateDescriptor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -28,17 +27,12 @@
 
     private static final long serialVersionUID = 1L;
 
-    public final static FunctionIdentifier FID = BuiltinFunctions.SCALAR_COUNT;
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_COUNT;
 
-    public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
-        @Override
-        public IFunctionDescriptor createFunctionDescriptor() {
-            return new ScalarCountAggregateDescriptor(CountAggregateDescriptor.FACTORY.createFunctionDescriptor());
-        }
-    };
+    public static final IFunctionDescriptorFactory FACTORY = ScalarCountAggregateDescriptor::new;
 
-    private ScalarCountAggregateDescriptor(IFunctionDescriptor aggFuncDesc) {
-        super(aggFuncDesc);
+    private ScalarCountAggregateDescriptor() {
+        super(CountAggregateDescriptor.FACTORY);
     }
 
     @Override
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarCountDistinctAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarCountDistinctAggregateDescriptor.java
new file mode 100644
index 0000000..a2b2ad1
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarCountDistinctAggregateDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.scalar;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.aggregates.std.CountAggregateDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class ScalarCountDistinctAggregateDescriptor extends AbstractScalarDistinctAggregateDescriptor {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_COUNT_DISTINCT;
+
+    public static final IFunctionDescriptorFactory FACTORY = ScalarCountDistinctAggregateDescriptor::new;
+
+    private ScalarCountDistinctAggregateDescriptor() {
+        super(CountAggregateDescriptor.FACTORY);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarKurtosisAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarKurtosisAggregateDescriptor.java
index 943c74d..117414d 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarKurtosisAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarKurtosisAggregateDescriptor.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.runtime.aggregates.scalar;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.runtime.aggregates.std.KurtosisAggregateDescriptor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -28,18 +27,12 @@
 
     private static final long serialVersionUID = 1L;
 
-    public final static FunctionIdentifier FID = BuiltinFunctions.SCALAR_KURTOSIS;
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_KURTOSIS;
 
-    public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
-        @Override
-        public IFunctionDescriptor createFunctionDescriptor() {
-            return new ScalarKurtosisAggregateDescriptor(
-                    KurtosisAggregateDescriptor.FACTORY.createFunctionDescriptor());
-        }
-    };
+    public static final IFunctionDescriptorFactory FACTORY = ScalarKurtosisAggregateDescriptor::new;
 
-    private ScalarKurtosisAggregateDescriptor(IFunctionDescriptor aggFuncDesc) {
-        super(aggFuncDesc);
+    private ScalarKurtosisAggregateDescriptor() {
+        super(KurtosisAggregateDescriptor.FACTORY);
     }
 
     @Override
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarKurtosisDistinctAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarKurtosisDistinctAggregateDescriptor.java
new file mode 100644
index 0000000..db6f0e8
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarKurtosisDistinctAggregateDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.scalar;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.aggregates.std.KurtosisAggregateDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class ScalarKurtosisDistinctAggregateDescriptor extends AbstractScalarDistinctAggregateDescriptor {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_KURTOSIS_DISTINCT;
+
+    public static final IFunctionDescriptorFactory FACTORY = ScalarKurtosisDistinctAggregateDescriptor::new;
+
+    private ScalarKurtosisDistinctAggregateDescriptor() {
+        super(KurtosisAggregateDescriptor.FACTORY);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarMaxAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarMaxAggregateDescriptor.java
index f18fd32..961890a 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarMaxAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarMaxAggregateDescriptor.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.runtime.aggregates.scalar;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.runtime.aggregates.std.MaxAggregateDescriptor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -28,17 +27,12 @@
 
     private static final long serialVersionUID = 1L;
 
-    public final static FunctionIdentifier FID = BuiltinFunctions.SCALAR_MAX;
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_MAX;
 
-    public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
-        @Override
-        public IFunctionDescriptor createFunctionDescriptor() {
-            return new ScalarMaxAggregateDescriptor(MaxAggregateDescriptor.FACTORY.createFunctionDescriptor());
-        }
-    };
+    public static final IFunctionDescriptorFactory FACTORY = ScalarMaxAggregateDescriptor::new;
 
-    private ScalarMaxAggregateDescriptor(IFunctionDescriptor aggFuncDesc) {
-        super(aggFuncDesc);
+    private ScalarMaxAggregateDescriptor() {
+        super(MaxAggregateDescriptor.FACTORY);
     }
 
     @Override
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarMaxDistinctAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarMaxDistinctAggregateDescriptor.java
new file mode 100644
index 0000000..932851c
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarMaxDistinctAggregateDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.scalar;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.aggregates.std.MaxAggregateDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class ScalarMaxDistinctAggregateDescriptor extends AbstractScalarDistinctAggregateDescriptor {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_MAX_DISTINCT;
+
+    public static final IFunctionDescriptorFactory FACTORY = ScalarMaxDistinctAggregateDescriptor::new;
+
+    private ScalarMaxDistinctAggregateDescriptor() {
+        super(MaxAggregateDescriptor.FACTORY);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarMinAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarMinAggregateDescriptor.java
index 17d008c..706dfcd 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarMinAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarMinAggregateDescriptor.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.runtime.aggregates.scalar;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.runtime.aggregates.std.MinAggregateDescriptor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -28,17 +27,12 @@
 
     private static final long serialVersionUID = 1L;
 
-    public final static FunctionIdentifier FID = BuiltinFunctions.SCALAR_MIN;
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_MIN;
 
-    public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
-        @Override
-        public IFunctionDescriptor createFunctionDescriptor() {
-            return new ScalarMinAggregateDescriptor(MinAggregateDescriptor.FACTORY.createFunctionDescriptor());
-        }
-    };
+    public static final IFunctionDescriptorFactory FACTORY = ScalarMinAggregateDescriptor::new;
 
-    private ScalarMinAggregateDescriptor(IFunctionDescriptor aggFuncDesc) {
-        super(aggFuncDesc);
+    private ScalarMinAggregateDescriptor() {
+        super(MinAggregateDescriptor.FACTORY);
     }
 
     @Override
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarMinDistinctAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarMinDistinctAggregateDescriptor.java
new file mode 100644
index 0000000..8bfd545
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarMinDistinctAggregateDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.scalar;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.aggregates.std.MinAggregateDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class ScalarMinDistinctAggregateDescriptor extends AbstractScalarDistinctAggregateDescriptor {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_MIN_DISTINCT;
+
+    public static final IFunctionDescriptorFactory FACTORY = ScalarMinDistinctAggregateDescriptor::new;
+
+    private ScalarMinDistinctAggregateDescriptor() {
+        super(MinAggregateDescriptor.FACTORY);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSkewnessAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSkewnessAggregateDescriptor.java
index 1fad8e4..79e38d1 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSkewnessAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSkewnessAggregateDescriptor.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.runtime.aggregates.scalar;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.runtime.aggregates.std.SkewnessAggregateDescriptor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -28,18 +27,12 @@
 
     private static final long serialVersionUID = 1L;
 
-    public final static FunctionIdentifier FID = BuiltinFunctions.SCALAR_SKEWNESS;
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SKEWNESS;
 
-    public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
-        @Override
-        public IFunctionDescriptor createFunctionDescriptor() {
-            return new ScalarSkewnessAggregateDescriptor(
-                    SkewnessAggregateDescriptor.FACTORY.createFunctionDescriptor());
-        }
-    };
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSkewnessAggregateDescriptor::new;
 
-    private ScalarSkewnessAggregateDescriptor(IFunctionDescriptor aggFuncDesc) {
-        super(aggFuncDesc);
+    private ScalarSkewnessAggregateDescriptor() {
+        super(SkewnessAggregateDescriptor.FACTORY);
     }
 
     @Override
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSkewnessDistinctAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSkewnessDistinctAggregateDescriptor.java
new file mode 100644
index 0000000..84da166
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSkewnessDistinctAggregateDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.scalar;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.aggregates.std.SkewnessAggregateDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class ScalarSkewnessDistinctAggregateDescriptor extends AbstractScalarDistinctAggregateDescriptor {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SKEWNESS_DISTINCT;
+
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSkewnessDistinctAggregateDescriptor::new;
+
+    private ScalarSkewnessDistinctAggregateDescriptor() {
+        super(SkewnessAggregateDescriptor.FACTORY);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlAvgAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlAvgAggregateDescriptor.java
index ecc10d2..bfd83a6 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlAvgAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlAvgAggregateDescriptor.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.runtime.aggregates.scalar;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.runtime.aggregates.std.SqlAvgAggregateDescriptor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -28,17 +27,12 @@
 
     private static final long serialVersionUID = 1L;
 
-    public final static FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_AVG;
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_AVG;
 
-    public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
-        @Override
-        public IFunctionDescriptor createFunctionDescriptor() {
-            return new ScalarSqlAvgAggregateDescriptor(SqlAvgAggregateDescriptor.FACTORY.createFunctionDescriptor());
-        }
-    };
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSqlAvgAggregateDescriptor::new;
 
-    private ScalarSqlAvgAggregateDescriptor(IFunctionDescriptor aggFuncDesc) {
-        super(aggFuncDesc);
+    private ScalarSqlAvgAggregateDescriptor() {
+        super(SqlAvgAggregateDescriptor.FACTORY);
     }
 
     @Override
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlAvgDistinctAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlAvgDistinctAggregateDescriptor.java
new file mode 100644
index 0000000..abba17d
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlAvgDistinctAggregateDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.scalar;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.aggregates.std.SqlAvgAggregateDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class ScalarSqlAvgDistinctAggregateDescriptor extends AbstractScalarDistinctAggregateDescriptor {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_AVG_DISTINCT;
+
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSqlAvgDistinctAggregateDescriptor::new;
+
+    private ScalarSqlAvgDistinctAggregateDescriptor() {
+        super(SqlAvgAggregateDescriptor.FACTORY);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlCountAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlCountAggregateDescriptor.java
index 029956a..bff06ce 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlCountAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlCountAggregateDescriptor.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.runtime.aggregates.scalar;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.runtime.aggregates.std.SqlCountAggregateDescriptor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -28,18 +27,12 @@
 
     private static final long serialVersionUID = 1L;
 
-    public final static FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_COUNT;
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_COUNT;
 
-    public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
-        @Override
-        public IFunctionDescriptor createFunctionDescriptor() {
-            return new ScalarSqlCountAggregateDescriptor(
-                    SqlCountAggregateDescriptor.FACTORY.createFunctionDescriptor());
-        }
-    };
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSqlCountAggregateDescriptor::new;
 
-    private ScalarSqlCountAggregateDescriptor(IFunctionDescriptor aggFuncDesc) {
-        super(aggFuncDesc);
+    private ScalarSqlCountAggregateDescriptor() {
+        super(SqlCountAggregateDescriptor.FACTORY);
     }
 
     @Override
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlCountDistinctAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlCountDistinctAggregateDescriptor.java
new file mode 100644
index 0000000..5945c79
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlCountDistinctAggregateDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.scalar;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.aggregates.std.SqlCountAggregateDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class ScalarSqlCountDistinctAggregateDescriptor extends AbstractScalarDistinctAggregateDescriptor {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_COUNT_DISTINCT;
+
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSqlCountDistinctAggregateDescriptor::new;
+
+    private ScalarSqlCountDistinctAggregateDescriptor() {
+        super(SqlCountAggregateDescriptor.FACTORY);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlKurtosisAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlKurtosisAggregateDescriptor.java
index 472b479..d83928e 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlKurtosisAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlKurtosisAggregateDescriptor.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.runtime.aggregates.scalar;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.runtime.aggregates.std.SqlKurtosisAggregateDescriptor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -28,18 +27,12 @@
 
     private static final long serialVersionUID = 1L;
 
-    public final static FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_KURTOSIS;
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_KURTOSIS;
 
-    public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
-        @Override
-        public IFunctionDescriptor createFunctionDescriptor() {
-            return new ScalarSqlKurtosisAggregateDescriptor(
-                    SqlKurtosisAggregateDescriptor.FACTORY.createFunctionDescriptor());
-        }
-    };
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSqlKurtosisAggregateDescriptor::new;
 
-    private ScalarSqlKurtosisAggregateDescriptor(IFunctionDescriptor aggFuncDesc) {
-        super(aggFuncDesc);
+    private ScalarSqlKurtosisAggregateDescriptor() {
+        super(SqlKurtosisAggregateDescriptor.FACTORY);
     }
 
     @Override
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlKurtosisDistinctAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlKurtosisDistinctAggregateDescriptor.java
new file mode 100644
index 0000000..de95222
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlKurtosisDistinctAggregateDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.scalar;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.aggregates.std.SqlKurtosisAggregateDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class ScalarSqlKurtosisDistinctAggregateDescriptor extends AbstractScalarDistinctAggregateDescriptor {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_KURTOSIS_DISTINCT;
+
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSqlKurtosisDistinctAggregateDescriptor::new;
+
+    private ScalarSqlKurtosisDistinctAggregateDescriptor() {
+        super(SqlKurtosisAggregateDescriptor.FACTORY);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlMaxAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlMaxAggregateDescriptor.java
index fb616b4..9e47104 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlMaxAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlMaxAggregateDescriptor.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.runtime.aggregates.scalar;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.runtime.aggregates.std.SqlMaxAggregateDescriptor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -28,17 +27,12 @@
 
     private static final long serialVersionUID = 1L;
 
-    public final static FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_MAX;
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_MAX;
 
-    public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
-        @Override
-        public IFunctionDescriptor createFunctionDescriptor() {
-            return new ScalarSqlMaxAggregateDescriptor(SqlMaxAggregateDescriptor.FACTORY.createFunctionDescriptor());
-        }
-    };
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSqlMaxAggregateDescriptor::new;
 
-    public ScalarSqlMaxAggregateDescriptor(IFunctionDescriptor aggFuncDesc) {
-        super(aggFuncDesc);
+    public ScalarSqlMaxAggregateDescriptor() {
+        super(SqlMaxAggregateDescriptor.FACTORY);
     }
 
     @Override
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlMaxDistinctAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlMaxDistinctAggregateDescriptor.java
new file mode 100644
index 0000000..02f740b
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlMaxDistinctAggregateDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.scalar;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.aggregates.std.SqlMaxAggregateDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class ScalarSqlMaxDistinctAggregateDescriptor extends AbstractScalarDistinctAggregateDescriptor {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_MAX_DISTINCT;
+
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSqlMaxDistinctAggregateDescriptor::new;
+
+    public ScalarSqlMaxDistinctAggregateDescriptor() {
+        super(SqlMaxAggregateDescriptor.FACTORY);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlMinAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlMinAggregateDescriptor.java
index 123b955..2d4267d 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlMinAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlMinAggregateDescriptor.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.runtime.aggregates.scalar;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.runtime.aggregates.std.SqlMinAggregateDescriptor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -28,17 +27,12 @@
 
     private static final long serialVersionUID = 1L;
 
-    public final static FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_MIN;
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_MIN;
 
-    public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
-        @Override
-        public IFunctionDescriptor createFunctionDescriptor() {
-            return new ScalarSqlMinAggregateDescriptor(SqlMinAggregateDescriptor.FACTORY.createFunctionDescriptor());
-        }
-    };
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSqlMinAggregateDescriptor::new;
 
-    private ScalarSqlMinAggregateDescriptor(IFunctionDescriptor aggFuncDesc) {
-        super(aggFuncDesc);
+    private ScalarSqlMinAggregateDescriptor() {
+        super(SqlMinAggregateDescriptor.FACTORY);
     }
 
     @Override
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlMinDistinctAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlMinDistinctAggregateDescriptor.java
new file mode 100644
index 0000000..843e4d0
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlMinDistinctAggregateDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.scalar;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.aggregates.std.SqlMinAggregateDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class ScalarSqlMinDistinctAggregateDescriptor extends AbstractScalarDistinctAggregateDescriptor {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_MIN_DISTINCT;
+
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSqlMinDistinctAggregateDescriptor::new;
+
+    private ScalarSqlMinDistinctAggregateDescriptor() {
+        super(SqlMinAggregateDescriptor.FACTORY);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlSkewnessAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlSkewnessAggregateDescriptor.java
index d52f597..c7a22e5 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlSkewnessAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlSkewnessAggregateDescriptor.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.runtime.aggregates.scalar;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.runtime.aggregates.std.SqlSkewnessAggregateDescriptor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -28,18 +27,12 @@
 
     private static final long serialVersionUID = 1L;
 
-    public final static FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_SKEWNESS;
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_SKEWNESS;
 
-    public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
-        @Override
-        public IFunctionDescriptor createFunctionDescriptor() {
-            return new ScalarSqlSkewnessAggregateDescriptor(
-                    SqlSkewnessAggregateDescriptor.FACTORY.createFunctionDescriptor());
-        }
-    };
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSqlSkewnessAggregateDescriptor::new;
 
-    private ScalarSqlSkewnessAggregateDescriptor(IFunctionDescriptor aggFuncDesc) {
-        super(aggFuncDesc);
+    private ScalarSqlSkewnessAggregateDescriptor() {
+        super(SqlSkewnessAggregateDescriptor.FACTORY);
     }
 
     @Override
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlSkewnessDistinctAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlSkewnessDistinctAggregateDescriptor.java
new file mode 100644
index 0000000..20ada89
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlSkewnessDistinctAggregateDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.scalar;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.aggregates.std.SqlSkewnessAggregateDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class ScalarSqlSkewnessDistinctAggregateDescriptor extends AbstractScalarAggregateDescriptor {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_SKEWNESS_DISTINCT;
+
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSqlSkewnessDistinctAggregateDescriptor::new;
+
+    private ScalarSqlSkewnessDistinctAggregateDescriptor() {
+        super(SqlSkewnessAggregateDescriptor.FACTORY);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlStddevAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlStddevAggregateDescriptor.java
index f52032d..514a4fc 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlStddevAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlStddevAggregateDescriptor.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.runtime.aggregates.scalar;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.runtime.aggregates.std.SqlStddevAggregateDescriptor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -28,18 +27,12 @@
 
     private static final long serialVersionUID = 1L;
 
-    public final static FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_STDDEV_SAMP;
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_STDDEV_SAMP;
 
-    public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
-        @Override
-        public IFunctionDescriptor createFunctionDescriptor() {
-            return new ScalarSqlStddevAggregateDescriptor(
-                    SqlStddevAggregateDescriptor.FACTORY.createFunctionDescriptor());
-        }
-    };
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSqlStddevAggregateDescriptor::new;
 
-    private ScalarSqlStddevAggregateDescriptor(IFunctionDescriptor aggFuncDesc) {
-        super(aggFuncDesc);
+    private ScalarSqlStddevAggregateDescriptor() {
+        super(SqlStddevAggregateDescriptor.FACTORY);
     }
 
     @Override
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlStddevDistinctAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlStddevDistinctAggregateDescriptor.java
new file mode 100644
index 0000000..1aa8120
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlStddevDistinctAggregateDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.scalar;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.aggregates.std.SqlStddevAggregateDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class ScalarSqlStddevDistinctAggregateDescriptor extends AbstractScalarDistinctAggregateDescriptor {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_STDDEV_SAMP_DISTINCT;
+
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSqlStddevDistinctAggregateDescriptor::new;
+
+    private ScalarSqlStddevDistinctAggregateDescriptor() {
+        super(SqlStddevAggregateDescriptor.FACTORY);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlStddevPopAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlStddevPopAggregateDescriptor.java
index b880222..9fd2506 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlStddevPopAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlStddevPopAggregateDescriptor.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.runtime.aggregates.scalar;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.runtime.aggregates.std.SqlStddevPopAggregateDescriptor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -28,18 +27,12 @@
 
     private static final long serialVersionUID = 1L;
 
-    public final static FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_STDDEV_POP;
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_STDDEV_POP;
 
-    public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
-        @Override
-        public IFunctionDescriptor createFunctionDescriptor() {
-            return new ScalarSqlStddevPopAggregateDescriptor(
-                    SqlStddevPopAggregateDescriptor.FACTORY.createFunctionDescriptor());
-        }
-    };
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSqlStddevPopAggregateDescriptor::new;
 
-    private ScalarSqlStddevPopAggregateDescriptor(IFunctionDescriptor aggFuncDesc) {
-        super(aggFuncDesc);
+    private ScalarSqlStddevPopAggregateDescriptor() {
+        super(SqlStddevPopAggregateDescriptor.FACTORY);
     }
 
     @Override
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlStddevPopDistinctAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlStddevPopDistinctAggregateDescriptor.java
new file mode 100644
index 0000000..d0c6bde
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlStddevPopDistinctAggregateDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.scalar;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.aggregates.std.SqlStddevPopAggregateDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class ScalarSqlStddevPopDistinctAggregateDescriptor extends AbstractScalarDistinctAggregateDescriptor {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_STDDEV_POP_DISTINCT;
+
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSqlStddevPopDistinctAggregateDescriptor::new;
+
+    private ScalarSqlStddevPopDistinctAggregateDescriptor() {
+        super(SqlStddevPopAggregateDescriptor.FACTORY);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlSumAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlSumAggregateDescriptor.java
index e1fcd1a..8334f1c 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlSumAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlSumAggregateDescriptor.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.runtime.aggregates.scalar;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.runtime.aggregates.std.SqlSumAggregateDescriptor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -28,15 +27,16 @@
 
     private static final long serialVersionUID = 1L;
 
-    public static final IFunctionDescriptorFactory FACTORY =
-            () -> new ScalarSqlSumAggregateDescriptor(SqlSumAggregateDescriptor.FACTORY.createFunctionDescriptor());
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_SUM;
 
-    private ScalarSqlSumAggregateDescriptor(IFunctionDescriptor aggFuncDesc) {
-        super(aggFuncDesc);
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSqlSumAggregateDescriptor::new;
+
+    private ScalarSqlSumAggregateDescriptor() {
+        super(SqlSumAggregateDescriptor.FACTORY);
     }
 
     @Override
     public FunctionIdentifier getIdentifier() {
-        return BuiltinFunctions.SCALAR_SQL_SUM;
+        return FID;
     }
 }
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlSumDistinctAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlSumDistinctAggregateDescriptor.java
new file mode 100644
index 0000000..853edbb
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlSumDistinctAggregateDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.scalar;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.aggregates.std.SqlSumAggregateDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class ScalarSqlSumDistinctAggregateDescriptor extends AbstractScalarDistinctAggregateDescriptor {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_SUM_DISTINCT;
+
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSqlSumDistinctAggregateDescriptor::new;
+
+    private ScalarSqlSumDistinctAggregateDescriptor() {
+        super(SqlSumAggregateDescriptor.FACTORY);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlVarAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlVarAggregateDescriptor.java
index 983e178..b1cbd10 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlVarAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlVarAggregateDescriptor.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.runtime.aggregates.scalar;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.runtime.aggregates.std.SqlVarAggregateDescriptor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -28,17 +27,12 @@
 
     private static final long serialVersionUID = 1L;
 
-    public final static FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_VAR_SAMP;
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_VAR_SAMP;
 
-    public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
-        @Override
-        public IFunctionDescriptor createFunctionDescriptor() {
-            return new ScalarSqlVarAggregateDescriptor(SqlVarAggregateDescriptor.FACTORY.createFunctionDescriptor());
-        }
-    };
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSqlVarAggregateDescriptor::new;
 
-    private ScalarSqlVarAggregateDescriptor(IFunctionDescriptor aggFuncDesc) {
-        super(aggFuncDesc);
+    private ScalarSqlVarAggregateDescriptor() {
+        super(SqlVarAggregateDescriptor.FACTORY);
     }
 
     @Override
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlVarDistinctAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlVarDistinctAggregateDescriptor.java
new file mode 100644
index 0000000..253dba9
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlVarDistinctAggregateDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.scalar;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.aggregates.std.SqlVarAggregateDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class ScalarSqlVarDistinctAggregateDescriptor extends AbstractScalarDistinctAggregateDescriptor {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_VAR_SAMP_DISTINCT;
+
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSqlVarDistinctAggregateDescriptor::new;
+
+    private ScalarSqlVarDistinctAggregateDescriptor() {
+        super(SqlVarAggregateDescriptor.FACTORY);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlVarPopAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlVarPopAggregateDescriptor.java
index 072a9bd..c09adb0 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlVarPopAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlVarPopAggregateDescriptor.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.runtime.aggregates.scalar;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.runtime.aggregates.std.SqlVarPopAggregateDescriptor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -28,18 +27,12 @@
 
     private static final long serialVersionUID = 1L;
 
-    public final static FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_VAR_POP;
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_VAR_POP;
 
-    public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
-        @Override
-        public IFunctionDescriptor createFunctionDescriptor() {
-            return new ScalarSqlVarPopAggregateDescriptor(
-                    SqlVarPopAggregateDescriptor.FACTORY.createFunctionDescriptor());
-        }
-    };
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSqlVarPopAggregateDescriptor::new;
 
-    private ScalarSqlVarPopAggregateDescriptor(IFunctionDescriptor aggFuncDesc) {
-        super(aggFuncDesc);
+    private ScalarSqlVarPopAggregateDescriptor() {
+        super(SqlVarPopAggregateDescriptor.FACTORY);
     }
 
     @Override
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlVarPopDistinctAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlVarPopDistinctAggregateDescriptor.java
new file mode 100644
index 0000000..4a761ba
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSqlVarPopDistinctAggregateDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.scalar;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.aggregates.std.SqlVarPopAggregateDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class ScalarSqlVarPopDistinctAggregateDescriptor extends AbstractScalarDistinctAggregateDescriptor {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SQL_VAR_POP_DISTINCT;
+
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSqlVarPopDistinctAggregateDescriptor::new;
+
+    private ScalarSqlVarPopDistinctAggregateDescriptor() {
+        super(SqlVarPopAggregateDescriptor.FACTORY);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarStddevAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarStddevAggregateDescriptor.java
index 23b1255..a4cd97f 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarStddevAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarStddevAggregateDescriptor.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.runtime.aggregates.scalar;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.runtime.aggregates.std.StddevAggregateDescriptor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -28,17 +27,12 @@
 
     private static final long serialVersionUID = 1L;
 
-    public final static FunctionIdentifier FID = BuiltinFunctions.SCALAR_STDDEV_SAMP;
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_STDDEV_SAMP;
 
-    public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
-        @Override
-        public IFunctionDescriptor createFunctionDescriptor() {
-            return new ScalarStddevAggregateDescriptor(StddevAggregateDescriptor.FACTORY.createFunctionDescriptor());
-        }
-    };
+    public static final IFunctionDescriptorFactory FACTORY = ScalarStddevAggregateDescriptor::new;
 
-    private ScalarStddevAggregateDescriptor(IFunctionDescriptor aggFuncDesc) {
-        super(aggFuncDesc);
+    private ScalarStddevAggregateDescriptor() {
+        super(StddevAggregateDescriptor.FACTORY);
     }
 
     @Override
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarStddevDistinctAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarStddevDistinctAggregateDescriptor.java
new file mode 100644
index 0000000..50141f3
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarStddevDistinctAggregateDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.scalar;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.aggregates.std.StddevAggregateDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class ScalarStddevDistinctAggregateDescriptor extends AbstractScalarDistinctAggregateDescriptor {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_STDDEV_SAMP_DISTINCT;
+
+    public static final IFunctionDescriptorFactory FACTORY = ScalarStddevDistinctAggregateDescriptor::new;
+
+    private ScalarStddevDistinctAggregateDescriptor() {
+        super(StddevAggregateDescriptor.FACTORY);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarStddevPopAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarStddevPopAggregateDescriptor.java
index 2a566a8..3710ba4 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarStddevPopAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarStddevPopAggregateDescriptor.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.runtime.aggregates.scalar;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.runtime.aggregates.std.StddevPopAggregateDescriptor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -28,18 +27,12 @@
 
     private static final long serialVersionUID = 1L;
 
-    public final static FunctionIdentifier FID = BuiltinFunctions.SCALAR_STDDEV_POP;
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_STDDEV_POP;
 
-    public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
-        @Override
-        public IFunctionDescriptor createFunctionDescriptor() {
-            return new ScalarStddevPopAggregateDescriptor(
-                    StddevPopAggregateDescriptor.FACTORY.createFunctionDescriptor());
-        }
-    };
+    public static final IFunctionDescriptorFactory FACTORY = ScalarStddevPopAggregateDescriptor::new;
 
-    private ScalarStddevPopAggregateDescriptor(IFunctionDescriptor aggFuncDesc) {
-        super(aggFuncDesc);
+    private ScalarStddevPopAggregateDescriptor() {
+        super(StddevPopAggregateDescriptor.FACTORY);
     }
 
     @Override
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarStddevPopDistinctAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarStddevPopDistinctAggregateDescriptor.java
new file mode 100644
index 0000000..f64fdd7
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarStddevPopDistinctAggregateDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.scalar;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.aggregates.std.StddevPopAggregateDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class ScalarStddevPopDistinctAggregateDescriptor extends AbstractScalarDistinctAggregateDescriptor {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_STDDEV_POP_DISTINCT;
+
+    public static final IFunctionDescriptorFactory FACTORY = ScalarStddevPopDistinctAggregateDescriptor::new;
+
+    private ScalarStddevPopDistinctAggregateDescriptor() {
+        super(StddevPopAggregateDescriptor.FACTORY);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSumAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSumAggregateDescriptor.java
index eb1d38b..3e64cd6 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSumAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSumAggregateDescriptor.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.runtime.aggregates.scalar;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.runtime.aggregates.std.SumAggregateDescriptor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -28,15 +27,16 @@
 
     private static final long serialVersionUID = 1L;
 
-    public static final IFunctionDescriptorFactory FACTORY =
-            () -> new ScalarSumAggregateDescriptor(SumAggregateDescriptor.FACTORY.createFunctionDescriptor());
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SUM;
 
-    private ScalarSumAggregateDescriptor(IFunctionDescriptor aggFuncDesc) {
-        super(aggFuncDesc);
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSumAggregateDescriptor::new;
+
+    private ScalarSumAggregateDescriptor() {
+        super(SumAggregateDescriptor.FACTORY);
     }
 
     @Override
     public FunctionIdentifier getIdentifier() {
-        return BuiltinFunctions.SCALAR_SUM;
+        return FID;
     }
 }
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSumDistinctAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSumDistinctAggregateDescriptor.java
new file mode 100644
index 0000000..cbe4033
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarSumDistinctAggregateDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.scalar;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.aggregates.std.SumAggregateDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class ScalarSumDistinctAggregateDescriptor extends AbstractScalarDistinctAggregateDescriptor {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_SUM_DISTINCT;
+
+    public static final IFunctionDescriptorFactory FACTORY = ScalarSumDistinctAggregateDescriptor::new;
+
+    private ScalarSumDistinctAggregateDescriptor() {
+        super(SumAggregateDescriptor.FACTORY);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarVarAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarVarAggregateDescriptor.java
index 2f600c2..a25f3dc 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarVarAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarVarAggregateDescriptor.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.runtime.aggregates.scalar;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.runtime.aggregates.std.VarAggregateDescriptor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -28,17 +27,12 @@
 
     private static final long serialVersionUID = 1L;
 
-    public final static FunctionIdentifier FID = BuiltinFunctions.SCALAR_VAR_SAMP;
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_VAR_SAMP;
 
-    public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
-        @Override
-        public IFunctionDescriptor createFunctionDescriptor() {
-            return new ScalarVarAggregateDescriptor(VarAggregateDescriptor.FACTORY.createFunctionDescriptor());
-        }
-    };
+    public static final IFunctionDescriptorFactory FACTORY = ScalarVarAggregateDescriptor::new;
 
-    private ScalarVarAggregateDescriptor(IFunctionDescriptor aggFuncDesc) {
-        super(aggFuncDesc);
+    private ScalarVarAggregateDescriptor() {
+        super(VarAggregateDescriptor.FACTORY);
     }
 
     @Override
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarVarDistinctAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarVarDistinctAggregateDescriptor.java
new file mode 100644
index 0000000..39256bb
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarVarDistinctAggregateDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.scalar;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.aggregates.std.VarAggregateDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class ScalarVarDistinctAggregateDescriptor extends AbstractScalarDistinctAggregateDescriptor {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_VAR_SAMP_DISTINCT;
+
+    public static final IFunctionDescriptorFactory FACTORY = ScalarVarDistinctAggregateDescriptor::new;
+
+    private ScalarVarDistinctAggregateDescriptor() {
+        super(VarAggregateDescriptor.FACTORY);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarVarPopAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarVarPopAggregateDescriptor.java
index d843a70..defd316 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarVarPopAggregateDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarVarPopAggregateDescriptor.java
@@ -19,7 +19,6 @@
 package org.apache.asterix.runtime.aggregates.scalar;
 
 import org.apache.asterix.om.functions.BuiltinFunctions;
-import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.runtime.aggregates.std.VarPopAggregateDescriptor;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
@@ -28,17 +27,12 @@
 
     private static final long serialVersionUID = 1L;
 
-    public final static FunctionIdentifier FID = BuiltinFunctions.SCALAR_VAR_POP;
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_VAR_POP;
 
-    public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
-        @Override
-        public IFunctionDescriptor createFunctionDescriptor() {
-            return new ScalarVarPopAggregateDescriptor(VarPopAggregateDescriptor.FACTORY.createFunctionDescriptor());
-        }
-    };
+    public static final IFunctionDescriptorFactory FACTORY = ScalarVarPopAggregateDescriptor::new;
 
-    private ScalarVarPopAggregateDescriptor(IFunctionDescriptor aggFuncDesc) {
-        super(aggFuncDesc);
+    private ScalarVarPopAggregateDescriptor() {
+        super(VarPopAggregateDescriptor.FACTORY);
     }
 
     @Override
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarVarPopDistinctAggregateDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarVarPopDistinctAggregateDescriptor.java
new file mode 100644
index 0000000..2cc0dab
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/scalar/ScalarVarPopDistinctAggregateDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.scalar;
+
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.aggregates.std.VarPopAggregateDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class ScalarVarPopDistinctAggregateDescriptor extends AbstractScalarDistinctAggregateDescriptor {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final FunctionIdentifier FID = BuiltinFunctions.SCALAR_VAR_POP_DISTINCT;
+
+    public static final IFunctionDescriptorFactory FACTORY = ScalarVarPopDistinctAggregateDescriptor::new;
+
+    private ScalarVarPopDistinctAggregateDescriptor() {
+        super(VarPopAggregateDescriptor.FACTORY);
+    }
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/utils/PointableHashSet.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/utils/PointableHashSet.java
new file mode 100644
index 0000000..3d6fccb
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/aggregates/utils/PointableHashSet.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.aggregates.utils;
+
+import java.util.List;
+
+import org.apache.asterix.common.exceptions.ErrorCode;
+import org.apache.asterix.common.exceptions.RuntimeDataException;
+import org.apache.asterix.formats.nontagged.BinaryComparatorFactoryProvider;
+import org.apache.asterix.formats.nontagged.BinaryHashFunctionFactoryProvider;
+import org.apache.asterix.om.types.ATypeTag;
+import org.apache.asterix.om.types.EnumDeserializer;
+import org.apache.asterix.om.util.container.IObjectPool;
+import org.apache.asterix.runtime.evaluators.functions.PointableHelper;
+import org.apache.hyracks.api.dataflow.value.IBinaryComparator;
+import org.apache.hyracks.api.dataflow.value.IBinaryHashFunction;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.api.exceptions.SourceLocation;
+import org.apache.hyracks.data.std.api.IPointable;
+
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+
+/**
+ * A hash set containing Pointables.
+ * Currently only {@link #add(IPointable)} operation as implemented.
+ */
+public class PointableHashSet {
+
+    private final SourceLocation sourceLoc;
+
+    private final IObjectPool<List<IPointable>, ATypeTag> listAllocator;
+
+    private final IBinaryComparator comparator;
+
+    private final IBinaryHashFunction hashFunction;
+
+    private final Int2ObjectMap<List<IPointable>> hashes;
+
+    public PointableHashSet(IObjectPool<List<IPointable>, ATypeTag> listAllocator, SourceLocation sourceLoc) {
+        this.listAllocator = listAllocator;
+        this.sourceLoc = sourceLoc;
+        comparator = BinaryComparatorFactoryProvider.INSTANCE.getBinaryComparatorFactory(null, null, true)
+                .createBinaryComparator();
+        hashFunction = BinaryHashFunctionFactoryProvider.INSTANCE.getBinaryHashFunctionFactory(null)
+                .createBinaryHashFunction();
+        hashes = new Int2ObjectOpenHashMap<>();
+    }
+
+    public void clear() {
+        hashes.clear();
+    }
+
+    /**
+     * Adds an instance to this set.
+     * Returns {@code true} if the set did not already contain this item, {@code false} otherwise.
+     */
+    public boolean add(IPointable item) throws HyracksDataException {
+        if (EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(item.getByteArray()[item.getStartOffset()])
+                .isDerivedType()) {
+            throw new RuntimeDataException(ErrorCode.CANNOT_COMPARE_COMPLEX, sourceLoc);
+        }
+
+        // look up if it already exists
+        int hash = hashFunction.hash(item.getByteArray(), item.getStartOffset(), item.getLength());
+        List<IPointable> sameHashes = hashes.get(hash);
+        if (sameHashes == null) {
+            // new item
+            sameHashes = listAllocator.allocate(null);
+            sameHashes.clear();
+            sameHashes.add(makeStoredItem(item));
+            hashes.put(hash, sameHashes);
+            return true;
+        } else if (PointableHelper.findItem(item, sameHashes, comparator) == null) {
+            // new item, it could happen that two hashes are the same but they are for different items
+            sameHashes.add(makeStoredItem(item));
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    protected IPointable makeStoredItem(IPointable item) throws HyracksDataException {
+        return item;
+    }
+}
\ No newline at end of file
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ArrayDistinctDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ArrayDistinctDescriptor.java
index 1fc89ae..35b9aac 100755
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ArrayDistinctDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ArrayDistinctDescriptor.java
@@ -18,41 +18,29 @@
  */
 package org.apache.asterix.runtime.evaluators.functions;
 
-import static org.apache.asterix.om.types.EnumDeserializer.ATYPETAGDESERIALIZER;
-
 import java.io.IOException;
-import java.util.List;
 
 import org.apache.asterix.builders.IAsterixListBuilder;
-import org.apache.asterix.common.exceptions.ErrorCode;
-import org.apache.asterix.common.exceptions.RuntimeDataException;
-import org.apache.asterix.formats.nontagged.BinaryComparatorFactoryProvider;
-import org.apache.asterix.formats.nontagged.BinaryHashFunctionFactoryProvider;
 import org.apache.asterix.om.functions.BuiltinFunctions;
 import org.apache.asterix.om.functions.IFunctionDescriptor;
 import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
 import org.apache.asterix.om.functions.IFunctionTypeInferer;
 import org.apache.asterix.om.types.ATypeTag;
 import org.apache.asterix.om.types.IAType;
+import org.apache.asterix.runtime.aggregates.utils.PointableHashSet;
 import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor;
 import org.apache.asterix.runtime.evaluators.common.ListAccessor;
 import org.apache.asterix.runtime.functions.FunctionTypeInferers;
-import org.apache.asterix.runtime.utils.ArrayFunctionsUtil;
 import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
 import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator;
 import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
 import org.apache.hyracks.api.context.IHyracksTaskContext;
-import org.apache.hyracks.api.dataflow.value.IBinaryComparator;
-import org.apache.hyracks.api.dataflow.value.IBinaryHashFunction;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.api.exceptions.SourceLocation;
 import org.apache.hyracks.data.std.api.IPointable;
 import org.apache.hyracks.data.std.util.ArrayBackedValueStorage;
 
-import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
-import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
-
 /**
  * <pre>
  * array_distinct(list) returns a new list with distinct items of the input list. The returned list has the same type as
@@ -112,60 +100,36 @@
     }
 
     public class ArrayDistinctFunction extends AbstractArrayProcessEval {
-        private final SourceLocation sourceLoc;
-        private final IBinaryHashFunction binaryHashFunction;
-        private final Int2ObjectMap<List<IPointable>> hashes;
-        private final IBinaryComparator comp;
+        private final PointableHashSet itemSet;
         private IPointable item;
         private ArrayBackedValueStorage storage;
 
         public ArrayDistinctFunction(IScalarEvaluatorFactory[] args, IHyracksTaskContext ctx, SourceLocation sourceLoc)
                 throws HyracksDataException {
             super(args, ctx, inputListType);
-            this.sourceLoc = sourceLoc;
-            hashes = new Int2ObjectOpenHashMap<>();
-            comp = BinaryComparatorFactoryProvider.INSTANCE.getBinaryComparatorFactory(null, null, true)
-                    .createBinaryComparator();
-            binaryHashFunction = BinaryHashFunctionFactoryProvider.INSTANCE.getBinaryHashFunctionFactory(null)
-                    .createBinaryHashFunction();
+            itemSet = new PointableHashSet(arrayListAllocator, sourceLoc);
         }
 
         @Override
         protected void processList(ListAccessor listAccessor, IAsterixListBuilder listBuilder) throws IOException {
-            int hash;
-            boolean itemInStorage;
-            boolean nullMissingWasAdded = false;
-            List<IPointable> sameHashes;
-            hashes.clear();
+            itemSet.clear();
             item = pointableAllocator.allocateEmpty();
             storage = (ArrayBackedValueStorage) storageAllocator.allocate(null);
+            boolean nullMissingWasAdded = false;
             for (int i = 0; i < listAccessor.size(); i++) {
                 // get the item and compute its hash
-                itemInStorage = listAccessor.getOrWriteItem(i, item, storage);
-                if (ATYPETAGDESERIALIZER.deserialize(item.getByteArray()[item.getStartOffset()]).isDerivedType()) {
-                    throw new RuntimeDataException(ErrorCode.CANNOT_COMPARE_COMPLEX, sourceLoc);
-                }
+                boolean itemInStorage = listAccessor.getOrWriteItem(i, item, storage);
                 if (isNullOrMissing(item)) {
                     if (!nullMissingWasAdded) {
                         listBuilder.addItem(item);
                         nullMissingWasAdded = true;
                     }
-                } else {
-                    // look up if it already exists
-                    hash = binaryHashFunction.hash(item.getByteArray(), item.getStartOffset(), item.getLength());
-                    hashes.get(hash);
-                    sameHashes = hashes.get(hash);
-                    if (sameHashes == null) {
-                        // new item
-                        sameHashes = arrayListAllocator.allocate(null);
-                        sameHashes.clear();
-                        addItem(item, listBuilder, itemInStorage, sameHashes);
-                        hashes.put(hash, sameHashes);
-                        item = pointableAllocator.allocateEmpty();
-                    } else if (ArrayFunctionsUtil.findItem(item, sameHashes, comp) == null) {
-                        // new item, it could happen that two hashes are the same but they are for different items
-                        addItem(item, listBuilder, itemInStorage, sameHashes);
-                        item = pointableAllocator.allocateEmpty();
+                } else if (itemSet.add(item)) {
+                    listBuilder.addItem(item);
+                    item = pointableAllocator.allocateEmpty();
+                    if (itemInStorage) {
+                        // create new storage since the added item is using it now
+                        storage = (ArrayBackedValueStorage) storageAllocator.allocate(null);
                     }
                 }
             }
@@ -175,15 +139,5 @@
             byte tag = item.getByteArray()[item.getStartOffset()];
             return tag == ATypeTag.SERIALIZED_NULL_TYPE_TAG || tag == ATypeTag.SERIALIZED_MISSING_TYPE_TAG;
         }
-
-        private void addItem(IPointable item, IAsterixListBuilder listBuilder, boolean itemInStorage,
-                List<IPointable> sameHashes) throws HyracksDataException {
-            sameHashes.add(item);
-            listBuilder.addItem(item);
-            if (itemInStorage) {
-                // create new storage since the added item is using it now
-                storage = (ArrayBackedValueStorage) storageAllocator.allocate(null);
-            }
-        }
     }
 }
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ArrayIntersectDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ArrayIntersectDescriptor.java
index 6b03899..ca98572 100755
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ArrayIntersectDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ArrayIntersectDescriptor.java
@@ -49,7 +49,6 @@
 import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor;
 import org.apache.asterix.runtime.evaluators.common.ListAccessor;
 import org.apache.asterix.runtime.functions.FunctionTypeInferers;
-import org.apache.asterix.runtime.utils.ArrayFunctionsUtil;
 import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
 import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator;
@@ -363,7 +362,7 @@
                 newHashes.add(valueListIndex);
                 hashes.put(hash, newHashes);
                 return true;
-            } else if (ArrayFunctionsUtil.findItem(item, sameHashes, comp) == null) {
+            } else if (PointableHelper.findItem(item, sameHashes, comp) == null) {
                 ValueListIndex valueListIndex = valueListIndexAllocator.allocate(null);
                 valueListIndex.set(item, -1);
                 sameHashes.add(valueListIndex);
@@ -388,7 +387,7 @@
 
         private void incrementIfExists(List<ValueListIndex> sameHashes, IPointable item, int listIndex,
                 IAsterixListBuilder listBuilder) throws HyracksDataException {
-            ValueListIndex sameValue = ArrayFunctionsUtil.findItem(item, sameHashes, comp);
+            ValueListIndex sameValue = PointableHelper.findItem(item, sameHashes, comp);
             if (sameValue != null && listIndex - sameValue.listIndex == 1) {
                 // found the item, its stamp is OK (stamp saves the index of the last list that has seen this item)
                 // increment stamp of this item
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ArraySymDiffEval.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ArraySymDiffEval.java
index 9be6ff2..8737c11 100755
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ArraySymDiffEval.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ArraySymDiffEval.java
@@ -29,7 +29,6 @@
 import org.apache.asterix.om.util.container.IObjectFactory;
 import org.apache.asterix.om.util.container.IObjectPool;
 import org.apache.asterix.om.util.container.ListObjectPool;
-import org.apache.asterix.runtime.utils.ArrayFunctionsUtil;
 import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
 import org.apache.hyracks.api.context.IHyracksTaskContext;
 import org.apache.hyracks.api.dataflow.value.IBinaryComparator;
@@ -150,7 +149,7 @@
             return true;
         } else {
             // potentially, item already exists
-            ValueCounter itemListIdxCounter = ArrayFunctionsUtil.findItem(item, sameHashes, comp);
+            ValueCounter itemListIdxCounter = PointableHelper.findItem(item, sameHashes, comp);
             if (itemListIdxCounter == null) {
                 // new item having the same hash as a different item
                 addItem(item, listIndex, sameHashes);
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ArrayUnionDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ArrayUnionDescriptor.java
index 9a7a021..d61e636 100755
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ArrayUnionDescriptor.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/ArrayUnionDescriptor.java
@@ -34,7 +34,6 @@
 import org.apache.asterix.om.util.container.ListObjectPool;
 import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor;
 import org.apache.asterix.runtime.functions.FunctionTypeInferers;
-import org.apache.asterix.runtime.utils.ArrayFunctionsUtil;
 import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
 import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator;
@@ -148,7 +147,7 @@
                 addItem(listBuilder, item, sameHashes);
                 hashes.put(hash, sameHashes);
                 return true;
-            } else if (ArrayFunctionsUtil.findItem(item, sameHashes, comp) == null) {
+            } else if (PointableHelper.findItem(item, sameHashes, comp) == null) {
                 // new item, it could happen that two hashes are the same but they are for different items
                 addItem(listBuilder, item, sameHashes);
                 return true;
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/PointableHelper.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/PointableHelper.java
index 068f239..2914f63 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/PointableHelper.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/PointableHelper.java
@@ -20,6 +20,7 @@
 
 import java.io.DataOutput;
 import java.io.IOException;
+import java.util.Collection;
 
 import org.apache.asterix.om.pointables.base.IVisitablePointable;
 import org.apache.asterix.om.types.ATypeTag;
@@ -118,6 +119,17 @@
         return EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(bytes[s]);
     }
 
+    public static <T extends IValueReference> T findItem(IValueReference item, Collection<T> list,
+            IBinaryComparator comparator) throws HyracksDataException {
+        for (T listItem : list) {
+            if (comparator.compare(item.getByteArray(), item.getStartOffset(), item.getLength(),
+                    listItem.getByteArray(), listItem.getStartOffset(), listItem.getLength()) == 0) {
+                return listItem;
+            }
+        }
+        return null;
+    }
+
     /**
      * @param str
      *            The input string
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java
index 32f72ff..8c884ca 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java
@@ -33,27 +33,49 @@
 import org.apache.asterix.runtime.aggregates.collections.ListifyAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.collections.LocalFirstElementAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.scalar.ScalarAvgAggregateDescriptor;
+import org.apache.asterix.runtime.aggregates.scalar.ScalarAvgDistinctAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.scalar.ScalarCountAggregateDescriptor;
+import org.apache.asterix.runtime.aggregates.scalar.ScalarCountDistinctAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.scalar.ScalarKurtosisAggregateDescriptor;
+import org.apache.asterix.runtime.aggregates.scalar.ScalarKurtosisDistinctAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.scalar.ScalarMaxAggregateDescriptor;
+import org.apache.asterix.runtime.aggregates.scalar.ScalarMaxDistinctAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.scalar.ScalarMinAggregateDescriptor;
+import org.apache.asterix.runtime.aggregates.scalar.ScalarMinDistinctAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.scalar.ScalarSkewnessAggregateDescriptor;
+import org.apache.asterix.runtime.aggregates.scalar.ScalarSkewnessDistinctAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.scalar.ScalarSqlAvgAggregateDescriptor;
+import org.apache.asterix.runtime.aggregates.scalar.ScalarSqlAvgDistinctAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.scalar.ScalarSqlCountAggregateDescriptor;
+import org.apache.asterix.runtime.aggregates.scalar.ScalarSqlCountDistinctAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.scalar.ScalarSqlKurtosisAggregateDescriptor;
+import org.apache.asterix.runtime.aggregates.scalar.ScalarSqlKurtosisDistinctAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.scalar.ScalarSqlMaxAggregateDescriptor;
+import org.apache.asterix.runtime.aggregates.scalar.ScalarSqlMaxDistinctAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.scalar.ScalarSqlMinAggregateDescriptor;
+import org.apache.asterix.runtime.aggregates.scalar.ScalarSqlMinDistinctAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.scalar.ScalarSqlSkewnessAggregateDescriptor;
+import org.apache.asterix.runtime.aggregates.scalar.ScalarSqlSkewnessDistinctAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.scalar.ScalarSqlStddevAggregateDescriptor;
+import org.apache.asterix.runtime.aggregates.scalar.ScalarSqlStddevDistinctAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.scalar.ScalarSqlStddevPopAggregateDescriptor;
+import org.apache.asterix.runtime.aggregates.scalar.ScalarSqlStddevPopDistinctAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.scalar.ScalarSqlSumAggregateDescriptor;
+import org.apache.asterix.runtime.aggregates.scalar.ScalarSqlSumDistinctAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.scalar.ScalarSqlVarAggregateDescriptor;
+import org.apache.asterix.runtime.aggregates.scalar.ScalarSqlVarDistinctAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.scalar.ScalarSqlVarPopAggregateDescriptor;
+import org.apache.asterix.runtime.aggregates.scalar.ScalarSqlVarPopDistinctAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.scalar.ScalarStddevAggregateDescriptor;
+import org.apache.asterix.runtime.aggregates.scalar.ScalarStddevDistinctAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.scalar.ScalarStddevPopAggregateDescriptor;
+import org.apache.asterix.runtime.aggregates.scalar.ScalarStddevPopDistinctAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.scalar.ScalarSumAggregateDescriptor;
+import org.apache.asterix.runtime.aggregates.scalar.ScalarSumDistinctAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.scalar.ScalarVarAggregateDescriptor;
+import org.apache.asterix.runtime.aggregates.scalar.ScalarVarDistinctAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.scalar.ScalarVarPopAggregateDescriptor;
+import org.apache.asterix.runtime.aggregates.scalar.ScalarVarPopDistinctAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.serializable.std.SerializableAvgAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.serializable.std.SerializableCountAggregateDescriptor;
 import org.apache.asterix.runtime.aggregates.serializable.std.SerializableGlobalAvgAggregateDescriptor;
@@ -599,6 +621,8 @@
         fc.add(LocalSkewnessAggregateDescriptor.FACTORY);
         fc.add(IntermediateSkewnessAggregateDescriptor.FACTORY);
         fc.add(GlobalSkewnessAggregateDescriptor.FACTORY);
+        fc.add(EmptyStreamAggregateDescriptor.FACTORY);
+        fc.add(NonEmptyStreamAggregateDescriptor.FACTORY);
 
         // serializable aggregates
         fc.add(SerializableCountAggregateDescriptor.FACTORY);
@@ -637,18 +661,27 @@
 
         // scalar aggregates
         fc.add(ScalarCountAggregateDescriptor.FACTORY);
+        fc.add(ScalarCountDistinctAggregateDescriptor.FACTORY);
         fc.add(ScalarAvgAggregateDescriptor.FACTORY);
+        fc.add(ScalarAvgDistinctAggregateDescriptor.FACTORY);
         fc.add(ScalarSumAggregateDescriptor.FACTORY);
+        fc.add(ScalarSumDistinctAggregateDescriptor.FACTORY);
         fc.add(ScalarMaxAggregateDescriptor.FACTORY);
+        fc.add(ScalarMaxDistinctAggregateDescriptor.FACTORY);
         fc.add(ScalarMinAggregateDescriptor.FACTORY);
-        fc.add(EmptyStreamAggregateDescriptor.FACTORY);
-        fc.add(NonEmptyStreamAggregateDescriptor.FACTORY);
+        fc.add(ScalarMinDistinctAggregateDescriptor.FACTORY);
         fc.add(ScalarStddevAggregateDescriptor.FACTORY);
+        fc.add(ScalarStddevDistinctAggregateDescriptor.FACTORY);
         fc.add(ScalarStddevPopAggregateDescriptor.FACTORY);
+        fc.add(ScalarStddevPopDistinctAggregateDescriptor.FACTORY);
         fc.add(ScalarVarAggregateDescriptor.FACTORY);
+        fc.add(ScalarVarDistinctAggregateDescriptor.FACTORY);
         fc.add(ScalarVarPopAggregateDescriptor.FACTORY);
+        fc.add(ScalarVarPopDistinctAggregateDescriptor.FACTORY);
         fc.add(ScalarKurtosisAggregateDescriptor.FACTORY);
+        fc.add(ScalarKurtosisDistinctAggregateDescriptor.FACTORY);
         fc.add(ScalarSkewnessAggregateDescriptor.FACTORY);
+        fc.add(ScalarSkewnessDistinctAggregateDescriptor.FACTORY);
 
         // SQL aggregates
         fc.add(SqlCountAggregateDescriptor.FACTORY);
@@ -726,16 +759,27 @@
 
         // SQL scalar aggregates
         fc.add(ScalarSqlCountAggregateDescriptor.FACTORY);
+        fc.add(ScalarSqlCountDistinctAggregateDescriptor.FACTORY);
         fc.add(ScalarSqlAvgAggregateDescriptor.FACTORY);
+        fc.add(ScalarSqlAvgDistinctAggregateDescriptor.FACTORY);
         fc.add(ScalarSqlSumAggregateDescriptor.FACTORY);
+        fc.add(ScalarSqlSumDistinctAggregateDescriptor.FACTORY);
         fc.add(ScalarSqlMaxAggregateDescriptor.FACTORY);
+        fc.add(ScalarSqlMaxDistinctAggregateDescriptor.FACTORY);
         fc.add(ScalarSqlMinAggregateDescriptor.FACTORY);
+        fc.add(ScalarSqlMinDistinctAggregateDescriptor.FACTORY);
         fc.add(ScalarSqlStddevAggregateDescriptor.FACTORY);
+        fc.add(ScalarSqlStddevDistinctAggregateDescriptor.FACTORY);
         fc.add(ScalarSqlStddevPopAggregateDescriptor.FACTORY);
+        fc.add(ScalarSqlStddevPopDistinctAggregateDescriptor.FACTORY);
         fc.add(ScalarSqlVarAggregateDescriptor.FACTORY);
+        fc.add(ScalarSqlVarDistinctAggregateDescriptor.FACTORY);
         fc.add(ScalarSqlVarPopAggregateDescriptor.FACTORY);
+        fc.add(ScalarSqlVarPopDistinctAggregateDescriptor.FACTORY);
         fc.add(ScalarSqlKurtosisAggregateDescriptor.FACTORY);
+        fc.add(ScalarSqlKurtosisDistinctAggregateDescriptor.FACTORY);
         fc.add(ScalarSqlSkewnessAggregateDescriptor.FACTORY);
+        fc.add(ScalarSqlSkewnessDistinctAggregateDescriptor.FACTORY);
 
         // window functions
         fc.add(DenseRankRunningAggregateDescriptor.FACTORY);
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/utils/ArrayFunctionsUtil.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/utils/ArrayFunctionsUtil.java
deleted file mode 100644
index 14f102c..0000000
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/utils/ArrayFunctionsUtil.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.asterix.runtime.utils;
-
-import java.util.List;
-
-import org.apache.hyracks.api.dataflow.value.IBinaryComparator;
-import org.apache.hyracks.api.exceptions.HyracksDataException;
-import org.apache.hyracks.data.std.api.IValueReference;
-
-public class ArrayFunctionsUtil {
-
-    private ArrayFunctionsUtil() {
-    }
-
-    public static <T extends IValueReference> T findItem(IValueReference item, List<T> sameHashes,
-            IBinaryComparator comp) throws HyracksDataException {
-        T sameItem;
-        for (int k = 0; k < sameHashes.size(); k++) {
-            sameItem = sameHashes.get(k);
-            if (comp.compare(item.getByteArray(), item.getStartOffset(), item.getLength(), sameItem.getByteArray(),
-                    sameItem.getStartOffset(), sameItem.getLength()) == 0) {
-                return sameItem;
-            }
-        }
-        return null;
-    }
-}
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/util/OperatorManipulationUtil.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/util/OperatorManipulationUtil.java
index 967ad6f..c98489b 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/util/OperatorManipulationUtil.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/util/OperatorManipulationUtil.java
@@ -20,6 +20,7 @@
 
 import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.BitSet;
 import java.util.Collections;
 import java.util.Deque;
 import java.util.List;
@@ -429,4 +430,25 @@
         }
         return null;
     }
+
+    /**
+     * Retains variables and expressions in provided lists as specified by given bitset
+     */
+    public static void retainAssignVariablesAndExpressions(List<LogicalVariable> assignVarList,
+            List<Mutable<ILogicalExpression>> assignExprList, BitSet retainIndexes) {
+        int targetIdx = 0;
+        for (int sourceIdx = retainIndexes.nextSetBit(0); sourceIdx >= 0; sourceIdx =
+                retainIndexes.nextSetBit(sourceIdx + 1)) {
+            if (targetIdx != sourceIdx) {
+                assignVarList.set(targetIdx, assignVarList.get(sourceIdx));
+                assignExprList.set(targetIdx, assignExprList.get(sourceIdx));
+            }
+            targetIdx++;
+        }
+
+        for (int i = assignVarList.size() - 1; i >= targetIdx; i--) {
+            assignVarList.remove(i);
+            assignExprList.remove(i);
+        }
+    }
 }
diff --git a/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/ExtractCommonOperatorsRule.java b/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/ExtractCommonOperatorsRule.java
index fa11f73..166114a 100644
--- a/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/ExtractCommonOperatorsRule.java
+++ b/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/ExtractCommonOperatorsRule.java
@@ -462,7 +462,10 @@
                     equivalentClass.add(candidates.get(i));
                     for (int j = i - 1; j >= 0; j--) {
                         ILogicalOperator peer = candidates.get(j).getValue();
-                        if (IsomorphismUtilities.isOperatorIsomorphic(candidate, peer)) {
+                        boolean isomorphic = candidate.getInputs().size() > 1
+                                ? IsomorphismUtilities.isOperatorIsomorphicPlanSegment(candidate, peer)
+                                : IsomorphismUtilities.isOperatorIsomorphic(candidate, peer);
+                        if (isomorphic) {
                             reserved[i] = true;
                             reserved[j] = true;
                             equivalentClass.add(candidates.get(j));