This is an automated email from the ASF dual-hosted git repository.

Jackie-Jiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git


The following commit(s) were added to refs/heads/master by this push:
     new 96fe7141147 Merge final results of array, raw sketch and other 
aggregation functions (#20098)
96fe7141147 is described below

commit 96fe714114751e5655b674c9e2fb8411d35dd4c2
Author: Xiaotian (Jackie) Jiang <[email protected]>
AuthorDate: Fri Oct 30 16:15:53 2026 -0700

    Merge final results of array, raw sketch and other aggregation functions 
(#20098)
---
 .../aggregation/function/AggregationFunction.java  |  11 +-
 .../function/AnyValueAggregationFunction.java      |   5 +
 ...ValueIntegerTupleSketchAggregationFunction.java |   6 +
 ...stinctCountRawCPCSketchAggregationFunction.java |  17 ++
 .../DistinctCountRawHLLAggregationFunction.java    |  15 +-
 ...DistinctCountRawHLLPlusAggregationFunction.java |  15 +-
 ...inctCountRawThetaSketchAggregationFunction.java |  17 ++
 .../DistinctCountRawULLAggregationFunction.java    |  15 +-
 .../FrequentLongsSketchAggregationFunction.java    |  10 ++
 .../FrequentStringsSketchAggregationFunction.java  |  10 ++
 .../function/HistogramAggregationFunction.java     |   9 ++
 .../function/IdSetAggregationFunction.java         |   9 ++
 .../IntegerTupleSketchAggregationFunction.java     |  11 ++
 .../PercentileRawEstAggregationFunction.java       |  16 +-
 .../PercentileRawKLLAggregationFunction.java       |  14 ++
 .../PercentileRawKLLMVAggregationFunction.java     |  14 ++
 .../PercentileRawTDigestAggregationFunction.java   |  15 +-
 .../function/StUnionAggregationFunction.java       |   7 +
 .../function/SumPrecisionAggregationFunction.java  |  24 +--
 .../function/array/BaseArrayAggFunction.java       |  20 +++
 .../function/array/ListAggFunction.java            |  15 +-
 .../array/SumArrayDoubleAggregationFunction.java   |  10 ++
 .../array/SumArrayLongAggregationFunction.java     |  10 ++
 .../funnel/FunnelCountAggregationFunction.java     |   5 +-
 ...FunnelStepDurationStatsAggregationFunction.java |   5 -
 .../function/AggregationFunctionFactoryTest.java   |   6 +
 .../function/AnyValueAggregationFunctionTest.java  |   6 +
 ...tinctCountCPCSketchAggregationFunctionTest.java |  65 +++++---
 .../DistinctCountULLAggregationFunctionTest.java   |  15 ++
 .../aggregation/function/FinalResultMergeTest.java | 176 +++++++++++++++++++++
 .../IntegerTupleSketchAggregationFunctionTest.java |  65 ++++++--
 .../aggregation/function/ListAggFunctionTest.java  |  34 ++++
 .../function/StUnionAggregationFunctionTest.java   |  14 ++
 .../SumPrecisionAggregationFunctionTest.java       |  19 +++
 .../function/funnel/FunnelFinalResultTest.java     |  16 ++
 .../apache/pinot/queries/ArrayAggQueriesTest.java  |  27 ++--
 .../DistinctCountThetaSketchQueriesTest.java       |   7 +
 ...erSegmentAggregationSingleValueQueriesTest.java |  16 ++
 38 files changed, 699 insertions(+), 72 deletions(-)

diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/AggregationFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/AggregationFunction.java
index 86955a830ed..32d516d2e41 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/AggregationFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/AggregationFunction.java
@@ -322,9 +322,16 @@ public interface AggregationFunction<IntermediateResult, 
FinalResult> {
   @Nullable
   FinalResult extractFinalResult(@Nullable IntermediateResult 
intermediateResult);
 
-  /// Merges two final results, neither of which is `null`. This can be used 
to optimized certain functions (e.g.
+  /// Merges two final results, neither of which is `null`. This can be used 
to optimize certain functions (e.g.
   /// DISTINCT_COUNT) when data is partitioned on each server, where we may 
directly request servers to return final
-  /// result and merge them on broker. The result may be `finalResult1` 
itself, modified in place.
+  /// result and merge them on broker. Final results are only merged across 
partitions of the aggregated values, so no
+  /// value is aggregated into both final results.
+  ///
+  /// Final results are merged in the form they are transported in, the value 
their column stores: a `STRING` final
+  /// result is a string even where [#extractFinalResult] returns an object 
stored as its string, e.g. the serialized
+  /// sketch of a raw sketch function. The merged result takes the same form. 
Like other column values, a final result
+  /// can be shared, e.g. when the multi-stage engine broadcasts a block to 
several stages, so the merge must not modify
+  /// either final result. It may return one of them unchanged.
   ///
   /// Call [AggregationFunctionUtils#mergeFinalResult] instead of calling this 
directly, on the same terms as [#merge].
   default FinalResult mergeFinalResult(FinalResult finalResult1, FinalResult 
finalResult2) {
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/AnyValueAggregationFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/AnyValueAggregationFunction.java
index 73f9097b291..256dc6274e8 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/AnyValueAggregationFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/AnyValueAggregationFunction.java
@@ -173,6 +173,11 @@ public class AnyValueAggregationFunction extends 
BaseSingleInputAggregationFunct
     return left;
   }
 
+  @Override
+  public Object mergeFinalResult(Object finalResult1, Object finalResult2) {
+    return finalResult1;
+  }
+
   @Override
   public void aggregate(int length, AggregationResultHolder holder,
                         Map<ExpressionContext, BlockValSet> blockValSetMap) {
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/AvgValueIntegerTupleSketchAggregationFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/AvgValueIntegerTupleSketchAggregationFunction.java
index f279a157aae..1d34b244d27 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/AvgValueIntegerTupleSketchAggregationFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/AvgValueIntegerTupleSketchAggregationFunction.java
@@ -73,4 +73,10 @@ public class AvgValueIntegerTupleSketchAggregationFunction
     double estimate = retainedTotal / result.getRetainedEntries();
     return Math.round(estimate);
   }
+
+  /// Averages cannot be merged.
+  @Override
+  public Comparable mergeFinalResult(Comparable finalResult1, Comparable 
finalResult2) {
+    throw new UnsupportedOperationException("Cannot merge final results for 
function: " + getType());
+  }
 }
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/DistinctCountRawCPCSketchAggregationFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/DistinctCountRawCPCSketchAggregationFunction.java
index bf3e8c94725..819bbc49987 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/DistinctCountRawCPCSketchAggregationFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/DistinctCountRawCPCSketchAggregationFunction.java
@@ -18,8 +18,11 @@
  */
 package org.apache.pinot.core.query.aggregation.function;
 
+import java.util.Base64;
 import java.util.List;
 import javax.annotation.Nullable;
+import org.apache.datasketches.cpc.CpcSketch;
+import org.apache.datasketches.cpc.CpcUnion;
 import org.apache.pinot.common.request.context.ExpressionContext;
 import org.apache.pinot.common.utils.DataSchema.ColumnDataType;
 import org.apache.pinot.segment.local.customobject.CpcSketchAccumulator;
@@ -56,4 +59,18 @@ public class DistinctCountRawCPCSketchAggregationFunction 
extends DistinctCountC
     intermediateResult.setThreshold(_accumulatorThreshold);
     return new SerializedCPCSketch(intermediateResult.getResult());
   }
+
+  /// Merges two final results, which are serialized sketches in string form, 
into the string form of the merged
+  /// sketch.
+  @Override
+  public String mergeFinalResult(Comparable finalResult1, Comparable 
finalResult2) {
+    CpcUnion union = new CpcUnion(_lgNominalEntries);
+    union.update(deserializeFinalResult(finalResult1));
+    union.update(deserializeFinalResult(finalResult2));
+    return new SerializedCPCSketch(union.getResult()).toString();
+  }
+
+  private static CpcSketch deserializeFinalResult(Comparable finalResult) {
+    return CpcSketch.heapify(Base64.getDecoder().decode((String) finalResult));
+  }
 }
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/DistinctCountRawHLLAggregationFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/DistinctCountRawHLLAggregationFunction.java
index 330dbb966c6..20ed32efcef 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/DistinctCountRawHLLAggregationFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/DistinctCountRawHLLAggregationFunction.java
@@ -26,14 +26,16 @@ import org.apache.pinot.common.CustomObject;
 import org.apache.pinot.common.request.context.ExpressionContext;
 import org.apache.pinot.common.utils.DataSchema.ColumnDataType;
 import org.apache.pinot.core.common.BlockValSet;
+import org.apache.pinot.core.common.ObjectSerDeUtils;
 import org.apache.pinot.core.query.aggregation.AggregationResultHolder;
 import org.apache.pinot.core.query.aggregation.groupby.GroupByResultHolder;
 import org.apache.pinot.segment.local.customobject.SerializedHLL;
 import org.apache.pinot.segment.spi.AggregationFunctionType;
+import org.apache.pinot.spi.utils.BytesUtils;
 
 
 public class DistinctCountRawHLLAggregationFunction
-    extends BaseSingleInputAggregationFunction<HyperLogLog, SerializedHLL> {
+    extends BaseSingleInputAggregationFunction<HyperLogLog, Comparable<?>> {
   private final DistinctCountHLLAggregationFunction 
_distinctCountHLLAggregationFunction;
 
   public DistinctCountRawHLLAggregationFunction(List<ExpressionContext> 
arguments, boolean nullHandlingEnabled) {
@@ -128,4 +130,15 @@ public class DistinctCountRawHLLAggregationFunction
     }
     return new SerializedHLL(intermediateResult);
   }
+
+  /// Merges two final results, which are serialized HLLs in string form, into 
the string form of the merged HLL.
+  @Override
+  public String mergeFinalResult(Comparable<?> finalResult1, Comparable<?> 
finalResult2) {
+    HyperLogLog merged = merge(deserializeFinalResult(finalResult1), 
deserializeFinalResult(finalResult2));
+    return new SerializedHLL(merged).toString();
+  }
+
+  private static HyperLogLog deserializeFinalResult(Comparable<?> finalResult) 
{
+    return 
ObjectSerDeUtils.HYPER_LOG_LOG_SER_DE.deserialize(BytesUtils.toBytes((String) 
finalResult));
+  }
 }
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/DistinctCountRawHLLPlusAggregationFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/DistinctCountRawHLLPlusAggregationFunction.java
index 75ebb62c5dc..17d9b012cd6 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/DistinctCountRawHLLPlusAggregationFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/DistinctCountRawHLLPlusAggregationFunction.java
@@ -26,14 +26,16 @@ import org.apache.pinot.common.CustomObject;
 import org.apache.pinot.common.request.context.ExpressionContext;
 import org.apache.pinot.common.utils.DataSchema.ColumnDataType;
 import org.apache.pinot.core.common.BlockValSet;
+import org.apache.pinot.core.common.ObjectSerDeUtils;
 import org.apache.pinot.core.query.aggregation.AggregationResultHolder;
 import org.apache.pinot.core.query.aggregation.groupby.GroupByResultHolder;
 import org.apache.pinot.segment.local.customobject.SerializedHLLPlus;
 import org.apache.pinot.segment.spi.AggregationFunctionType;
+import org.apache.pinot.spi.utils.BytesUtils;
 
 
 public class DistinctCountRawHLLPlusAggregationFunction
-    extends BaseSingleInputAggregationFunction<HyperLogLogPlus, 
SerializedHLLPlus> {
+    extends BaseSingleInputAggregationFunction<HyperLogLogPlus, Comparable<?>> 
{
   private final DistinctCountHLLPlusAggregationFunction 
_distinctCountHLLPlusAggregationFunction;
 
   public DistinctCountRawHLLPlusAggregationFunction(List<ExpressionContext> 
arguments, boolean nullHandlingEnabled) {
@@ -129,4 +131,15 @@ public class DistinctCountRawHLLPlusAggregationFunction
     }
     return new SerializedHLLPlus(intermediateResult);
   }
+
+  /// Merges two final results, which are serialized HLL+s in string form, 
into the string form of the merged HLL+.
+  @Override
+  public String mergeFinalResult(Comparable<?> finalResult1, Comparable<?> 
finalResult2) {
+    HyperLogLogPlus merged = merge(deserializeFinalResult(finalResult1), 
deserializeFinalResult(finalResult2));
+    return new SerializedHLLPlus(merged).toString();
+  }
+
+  private static HyperLogLogPlus deserializeFinalResult(Comparable<?> 
finalResult) {
+    return 
ObjectSerDeUtils.HYPER_LOG_LOG_PLUS_SER_DE.deserialize(BytesUtils.toBytes((String)
 finalResult));
+  }
 }
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/DistinctCountRawThetaSketchAggregationFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/DistinctCountRawThetaSketchAggregationFunction.java
index 20c93225836..f12861f0317 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/DistinctCountRawThetaSketchAggregationFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/DistinctCountRawThetaSketchAggregationFunction.java
@@ -18,11 +18,13 @@
  */
 package org.apache.pinot.core.query.aggregation.function;
 
+import java.lang.foreign.MemorySegment;
 import java.util.ArrayList;
 import java.util.Base64;
 import java.util.List;
 import javax.annotation.Nullable;
 import org.apache.datasketches.theta.ThetaSketch;
+import org.apache.datasketches.theta.ThetaUnion;
 import org.apache.pinot.common.request.context.ExpressionContext;
 import org.apache.pinot.common.utils.DataSchema.ColumnDataType;
 import org.apache.pinot.segment.local.customobject.ThetaSketchAccumulator;
@@ -67,4 +69,19 @@ public class DistinctCountRawThetaSketchAggregationFunction 
extends DistinctCoun
     ThetaSketch sketch = evaluatePostAggregationExpression(mergedSketches);
     return Base64.getEncoder().encodeToString(sketch.toByteArray());
   }
+
+  /// Merges two final results, which are serialized sketches in string form, 
into the string form of their union.
+  /// Final results are only merged across partitions of the aggregated 
values, so the union of the sketches that the
+  /// post-aggregation expression returns for each partition is the sketch it 
returns for all of them.
+  @Override
+  public String mergeFinalResult(Comparable finalResult1, Comparable 
finalResult2) {
+    ThetaUnion union = _setOperationBuilder.buildUnion();
+    union.union(deserializeFinalResult(finalResult1));
+    union.union(deserializeFinalResult(finalResult2));
+    return Base64.getEncoder().encodeToString(union.getResult().toByteArray());
+  }
+
+  private static ThetaSketch deserializeFinalResult(Comparable finalResult) {
+    return 
ThetaSketch.wrap(MemorySegment.ofArray(Base64.getDecoder().decode((String) 
finalResult)));
+  }
 }
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/DistinctCountRawULLAggregationFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/DistinctCountRawULLAggregationFunction.java
index e2104d6fdae..e2f5b14ccee 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/DistinctCountRawULLAggregationFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/DistinctCountRawULLAggregationFunction.java
@@ -19,6 +19,7 @@
 package org.apache.pinot.core.query.aggregation.function;
 
 import com.dynatrace.hash4j.distinctcount.UltraLogLog;
+import java.util.Base64;
 import java.util.List;
 import javax.annotation.Nullable;
 import org.apache.pinot.common.request.context.ExpressionContext;
@@ -41,12 +42,24 @@ public class DistinctCountRawULLAggregationFunction extends 
DistinctCountULLAggr
     return new SerializedULL(intermediateResult);
   }
 
+  @Override
   public AggregationFunctionType getType() {
-    return AggregationFunctionType.DISTINCTCOUNTULL;
+    return AggregationFunctionType.DISTINCTCOUNTRAWULL;
   }
 
   @Override
   public ColumnDataType getFinalResultColumnType() {
     return ColumnDataType.STRING;
   }
+
+  /// Merges two final results, which are serialized ULLs in string form, into 
the string form of the merged ULL.
+  @Override
+  public String mergeFinalResult(Comparable finalResult1, Comparable 
finalResult2) {
+    UltraLogLog merged = merge(deserializeFinalResult(finalResult1), 
deserializeFinalResult(finalResult2));
+    return new SerializedULL(merged).toString();
+  }
+
+  private static UltraLogLog deserializeFinalResult(Comparable finalResult) {
+    return UltraLogLog.wrap(Base64.getDecoder().decode((String) finalResult));
+  }
 }
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/FrequentLongsSketchAggregationFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/FrequentLongsSketchAggregationFunction.java
index 15cee829ebf..48253a23b74 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/FrequentLongsSketchAggregationFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/FrequentLongsSketchAggregationFunction.java
@@ -21,6 +21,7 @@ package org.apache.pinot.core.query.aggregation.function;
 
 import com.google.common.base.Preconditions;
 import java.lang.foreign.MemorySegment;
+import java.util.Base64;
 import java.util.List;
 import java.util.Map;
 import javax.annotation.Nullable;
@@ -303,4 +304,13 @@ public class FrequentLongsSketchAggregationFunction
     // no disabled-mode identity to preserve here.
     return sketch != null ? new SerializedFrequentLongsSketch(sketch) : null;
   }
+
+  /// Merges two final results, which are serialized sketches in string form, 
into the string form of the merged
+  /// sketch.
+  @Override
+  public String mergeFinalResult(Comparable<?> finalResult1, Comparable<?> 
finalResult2) {
+    FrequentLongsSketch sketch1 = 
deserializeSketch(Base64.getDecoder().decode((String) finalResult1));
+    FrequentLongsSketch sketch2 = 
deserializeSketch(Base64.getDecoder().decode((String) finalResult2));
+    return new SerializedFrequentLongsSketch(merge(sketch1, 
sketch2)).toString();
+  }
 }
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/FrequentStringsSketchAggregationFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/FrequentStringsSketchAggregationFunction.java
index 27fa032fe98..af0ad716a79 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/FrequentStringsSketchAggregationFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/FrequentStringsSketchAggregationFunction.java
@@ -21,6 +21,7 @@ package org.apache.pinot.core.query.aggregation.function;
 
 import com.google.common.base.Preconditions;
 import java.lang.foreign.MemorySegment;
+import java.util.Base64;
 import java.util.List;
 import java.util.Map;
 import javax.annotation.Nullable;
@@ -296,4 +297,13 @@ public class FrequentStringsSketchAggregationFunction
     // no disabled-mode identity to preserve here.
     return sketch != null ? new SerializedFrequentStringsSketch(sketch) : null;
   }
+
+  /// Merges two final results, which are serialized sketches in string form, 
into the string form of the merged
+  /// sketch.
+  @Override
+  public String mergeFinalResult(Comparable<?> finalResult1, Comparable<?> 
finalResult2) {
+    FrequentItemsSketch<String> sketch1 = 
deserializeSketch(Base64.getDecoder().decode((String) finalResult1));
+    FrequentItemsSketch<String> sketch2 = 
deserializeSketch(Base64.getDecoder().decode((String) finalResult2));
+    return new SerializedFrequentStringsSketch(merge(sketch1, 
sketch2)).toString();
+  }
 }
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/HistogramAggregationFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/HistogramAggregationFunction.java
index 64c48f522bd..2e39377a92a 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/HistogramAggregationFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/HistogramAggregationFunction.java
@@ -258,6 +258,15 @@ public class HistogramAggregationFunction extends 
BaseSingleInputAggregationFunc
     return ArrayListUtils.toDoubleArray(doubleArrayList);
   }
 
+  @Override
+  public double[] mergeFinalResult(double[] finalResult1, double[] 
finalResult2) {
+    double[] mergedResult = finalResult1.clone();
+    for (int i = 0; i < mergedResult.length; i++) {
+      mergedResult[i] += finalResult2[i];
+    }
+    return mergedResult;
+  }
+
   @Override
   public void aggregate(int length, AggregationResultHolder 
aggregationResultHolder,
       Map<ExpressionContext, BlockValSet> blockValSetMap) {
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/IdSetAggregationFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/IdSetAggregationFunction.java
index 7c1ef748058..0c616d2a2d2 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/IdSetAggregationFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/IdSetAggregationFunction.java
@@ -658,6 +658,15 @@ public class IdSetAggregationFunction extends 
BaseSingleInputAggregationFunction
     }
   }
 
+  @Override
+  public String mergeFinalResult(String finalResult1, String finalResult2) {
+    try {
+      return merge(IdSets.fromBase64String(finalResult1), 
IdSets.fromBase64String(finalResult2)).toBase64String();
+    } catch (IOException e) {
+      throw new RuntimeException("Caught exception while merging IdSets", e);
+    }
+  }
+
   /// Returns the IdSet from the result holder or creates a new one if it does 
not exist.
   private IdSet getIdSet(AggregationResultHolder aggregationResultHolder, 
DataType valueType) {
     IdSet idSet = aggregationResultHolder.getResult();
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/IntegerTupleSketchAggregationFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/IntegerTupleSketchAggregationFunction.java
index c7366e8227b..c53f19076f0 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/IntegerTupleSketchAggregationFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/IntegerTupleSketchAggregationFunction.java
@@ -26,6 +26,7 @@ import java.util.Map;
 import javax.annotation.Nullable;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.datasketches.tuple.TupleSketch;
+import org.apache.datasketches.tuple.TupleUnion;
 import org.apache.datasketches.tuple.aninteger.IntegerSummary;
 import org.apache.datasketches.tuple.aninteger.IntegerSummaryDeserializer;
 import org.apache.datasketches.tuple.aninteger.IntegerSummarySetOperations;
@@ -279,6 +280,16 @@ public class IntegerTupleSketchAggregationFunction
     return 
Base64.getEncoder().encodeToString(accumulator.getResult().toByteArray());
   }
 
+  /// Merges two final results, which are serialized sketches in string form, 
into the string form of the merged
+  /// sketch.
+  @Override
+  public Comparable mergeFinalResult(Comparable finalResult1, Comparable 
finalResult2) {
+    TupleUnion<IntegerSummary> union = new TupleUnion<>(_nominalEntries, 
_setOps);
+    union.union(deserializeSketch(Base64.getDecoder().decode((String) 
finalResult1)));
+    union.union(deserializeSketch(Base64.getDecoder().decode((String) 
finalResult2)));
+    return Base64.getEncoder().encodeToString(union.getResult().toByteArray());
+  }
+
   @Override
   public boolean canUseStarTree(Map<String, Object> functionParameters) {
     Object nominalEntriesParam = 
functionParameters.get(Constants.THETA_TUPLE_SKETCH_NOMINAL_ENTRIES);
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/PercentileRawEstAggregationFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/PercentileRawEstAggregationFunction.java
index 263b8d90ff4..b4842c8d61b 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/PercentileRawEstAggregationFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/PercentileRawEstAggregationFunction.java
@@ -24,17 +24,19 @@ import org.apache.pinot.common.CustomObject;
 import org.apache.pinot.common.request.context.ExpressionContext;
 import org.apache.pinot.common.utils.DataSchema.ColumnDataType;
 import org.apache.pinot.core.common.BlockValSet;
+import org.apache.pinot.core.common.ObjectSerDeUtils;
 import org.apache.pinot.core.query.aggregation.AggregationResultHolder;
 import org.apache.pinot.core.query.aggregation.groupby.GroupByResultHolder;
 import org.apache.pinot.segment.local.customobject.QuantileDigest;
 import org.apache.pinot.segment.local.customobject.SerializedQuantileDigest;
 import org.apache.pinot.segment.spi.AggregationFunctionType;
+import org.apache.pinot.spi.utils.BytesUtils;
 
 
 /// The `PercentileRawEstAggregationFunction` returns the serialized 
`QuantileDigest` data structure of the
 /// `PercentileEstAggregationFunction`.
 public class PercentileRawEstAggregationFunction
-    extends BaseSingleInputAggregationFunction<QuantileDigest, 
SerializedQuantileDigest> {
+    extends BaseSingleInputAggregationFunction<QuantileDigest, Comparable<?>> {
   private final PercentileEstAggregationFunction 
_percentileEstAggregationFunction;
 
   public PercentileRawEstAggregationFunction(ExpressionContext 
expressionContext, double percentile,
@@ -146,4 +148,16 @@ public class PercentileRawEstAggregationFunction
     }
     return new SerializedQuantileDigest(intermediateResult, 
_percentileEstAggregationFunction._percentile);
   }
+
+  /// Merges two final results, which are serialized digests in string form, 
into the string form of the merged
+  /// digest.
+  @Override
+  public String mergeFinalResult(Comparable<?> finalResult1, Comparable<?> 
finalResult2) {
+    QuantileDigest merged = merge(deserializeFinalResult(finalResult1), 
deserializeFinalResult(finalResult2));
+    return new SerializedQuantileDigest(merged, 
_percentileEstAggregationFunction._percentile).toString();
+  }
+
+  private static QuantileDigest deserializeFinalResult(Comparable<?> 
finalResult) {
+    return 
ObjectSerDeUtils.QUANTILE_DIGEST_SER_DE.deserialize(BytesUtils.toBytes((String) 
finalResult));
+  }
 }
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/PercentileRawKLLAggregationFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/PercentileRawKLLAggregationFunction.java
index e4e2df10bfb..ab3af559594 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/PercentileRawKLLAggregationFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/PercentileRawKLLAggregationFunction.java
@@ -18,11 +18,13 @@
  */
 package org.apache.pinot.core.query.aggregation.function;
 
+import java.util.Base64;
 import java.util.List;
 import javax.annotation.Nullable;
 import org.apache.datasketches.kll.KllDoublesSketch;
 import org.apache.pinot.common.request.context.ExpressionContext;
 import org.apache.pinot.common.utils.DataSchema.ColumnDataType;
+import org.apache.pinot.core.common.ObjectSerDeUtils;
 import org.apache.pinot.segment.local.customobject.SerializedKLL;
 import org.apache.pinot.segment.spi.AggregationFunctionType;
 
@@ -59,4 +61,16 @@ public class PercentileRawKLLAggregationFunction extends 
PercentileKLLAggregatio
     }
     return new SerializedKLL(sketch, _percentile);
   }
+
+  /// Merges two final results, which are serialized sketches in string form, 
into the string form of the merged
+  /// sketch.
+  @Override
+  public String mergeFinalResult(Comparable<?> finalResult1, Comparable<?> 
finalResult2) {
+    KllDoublesSketch merged = merge(deserializeFinalResult(finalResult1), 
deserializeFinalResult(finalResult2));
+    return new SerializedKLL(merged, _percentile).toString();
+  }
+
+  private static KllDoublesSketch deserializeFinalResult(Comparable<?> 
finalResult) {
+    return 
ObjectSerDeUtils.KLL_SKETCH_SER_DE.deserialize(Base64.getDecoder().decode((String)
 finalResult));
+  }
 }
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/PercentileRawKLLMVAggregationFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/PercentileRawKLLMVAggregationFunction.java
index 7979374c533..e40857dc193 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/PercentileRawKLLMVAggregationFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/PercentileRawKLLMVAggregationFunction.java
@@ -18,11 +18,13 @@
  */
 package org.apache.pinot.core.query.aggregation.function;
 
+import java.util.Base64;
 import java.util.List;
 import javax.annotation.Nullable;
 import org.apache.datasketches.kll.KllDoublesSketch;
 import org.apache.pinot.common.request.context.ExpressionContext;
 import org.apache.pinot.common.utils.DataSchema.ColumnDataType;
+import org.apache.pinot.core.common.ObjectSerDeUtils;
 import org.apache.pinot.segment.local.customobject.SerializedKLL;
 import org.apache.pinot.segment.spi.AggregationFunctionType;
 
@@ -59,4 +61,16 @@ public class PercentileRawKLLMVAggregationFunction extends 
PercentileKLLMVAggreg
     }
     return new SerializedKLL(sketch, _percentile);
   }
+
+  /// Merges two final results, which are serialized sketches in string form, 
into the string form of the merged
+  /// sketch.
+  @Override
+  public String mergeFinalResult(Comparable<?> finalResult1, Comparable<?> 
finalResult2) {
+    KllDoublesSketch merged = merge(deserializeFinalResult(finalResult1), 
deserializeFinalResult(finalResult2));
+    return new SerializedKLL(merged, _percentile).toString();
+  }
+
+  private static KllDoublesSketch deserializeFinalResult(Comparable<?> 
finalResult) {
+    return 
ObjectSerDeUtils.KLL_SKETCH_SER_DE.deserialize(Base64.getDecoder().decode((String)
 finalResult));
+  }
 }
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/PercentileRawTDigestAggregationFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/PercentileRawTDigestAggregationFunction.java
index 5e4240f33a3..54fdff1339f 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/PercentileRawTDigestAggregationFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/PercentileRawTDigestAggregationFunction.java
@@ -31,12 +31,13 @@ import 
org.apache.pinot.segment.local.customobject.tdigest.SerializedTDigest;
 import org.apache.pinot.segment.local.customobject.tdigest.TDigest;
 import org.apache.pinot.segment.local.customobject.tdigest.TDigestAccumulator;
 import org.apache.pinot.segment.spi.AggregationFunctionType;
+import org.apache.pinot.spi.utils.BytesUtils;
 
 
 /// The `PercentileRawTDigestAggregationFunction` returns the serialized 
`TDigest` data structure of the
 /// `PercentileEstAggregationFunction`.
 public class PercentileRawTDigestAggregationFunction
-    extends BaseSingleInputAggregationFunction<TDigest, SerializedTDigest> {
+    extends BaseSingleInputAggregationFunction<TDigest, Comparable<?>> {
   private final PercentileTDigestAggregationFunction 
_percentileTDigestAggregationFunction;
 
   public PercentileRawTDigestAggregationFunction(ExpressionContext 
expressionContext, int percentile,
@@ -162,4 +163,16 @@ public class PercentileRawTDigestAggregationFunction
     }
     return new SerializedTDigest(intermediateResult, 
_percentileTDigestAggregationFunction._percentile);
   }
+
+  /// Merges two final results, which are serialized digests in string form, 
into the string form of the merged
+  /// digest.
+  @Override
+  public String mergeFinalResult(Comparable<?> finalResult1, Comparable<?> 
finalResult2) {
+    TDigest merged = merge(deserializeFinalResult(finalResult1), 
deserializeFinalResult(finalResult2));
+    return new SerializedTDigest(merged, 
_percentileTDigestAggregationFunction._percentile).toString();
+  }
+
+  private static TDigest deserializeFinalResult(Comparable<?> finalResult) {
+    return 
ObjectSerDeUtils.TDIGEST_SER_DE.deserialize(BytesUtils.toBytes((String) 
finalResult));
+  }
 }
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/StUnionAggregationFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/StUnionAggregationFunction.java
index d46de5cffbe..3113d178c9b 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/StUnionAggregationFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/StUnionAggregationFunction.java
@@ -233,6 +233,13 @@ public class StUnionAggregationFunction extends 
BaseSingleInputAggregationFuncti
     return new ByteArray(GeometrySerializer.serialize(geometry));
   }
 
+  @Override
+  public ByteArray mergeFinalResult(ByteArray finalResult1, ByteArray 
finalResult2) {
+    Geometry geometry1 = 
GeometrySerializer.deserialize(finalResult1.getBytes());
+    Geometry geometry2 = 
GeometrySerializer.deserialize(finalResult2.getBytes());
+    return new ByteArray(GeometrySerializer.serialize(merge(geometry1, 
geometry2)));
+  }
+
   /// Returns the union of the supplied geometries.
   ///
   /// When either operand is a `GeometryCollection`, 
[Geometry#union(Geometry)] can produce invalid
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/SumPrecisionAggregationFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/SumPrecisionAggregationFunction.java
index 64f2064b1eb..4306f032107 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/SumPrecisionAggregationFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/SumPrecisionAggregationFunction.java
@@ -370,19 +370,25 @@ public class SumPrecisionAggregationFunction extends 
BaseSingleInputAggregationF
   @Nullable
   @Override
   public BigDecimal extractFinalResult(@Nullable BigDecimal 
intermediateResult) {
-    if (intermediateResult == null) {
-      return null;
-    }
-    if (_precision == null) {
-      return intermediateResult;
-    }
-    BigDecimal result = intermediateResult.round(new MathContext(_precision, 
RoundingMode.HALF_EVEN));
-    return _scale == null ? result : result.setScale(_scale, 
RoundingMode.HALF_EVEN);
+    return intermediateResult != null ? round(intermediateResult) : null;
   }
 
+  /// Merges two final results and rounds the sum to the precision and scale 
of the function. The final results are
+  /// already rounded, so the merged result can differ from the rounded exact 
sum: with precision 1 and scale 0,
+  /// partition sums `0.5` and `0.5` have final results `0` and `0`, which 
merge to `0`, while their exact sum `1.0`
+  /// rounds to `1`.
   @Override
   public BigDecimal mergeFinalResult(BigDecimal finalResult1, BigDecimal 
finalResult2) {
-    return merge(finalResult1, finalResult2);
+    return round(merge(finalResult1, finalResult2));
+  }
+
+  /// Rounds the sum to the precision and scale of the function, if specified.
+  private BigDecimal round(BigDecimal sum) {
+    if (_precision == null) {
+      return sum;
+    }
+    BigDecimal result = sum.round(new MathContext(_precision, 
RoundingMode.HALF_EVEN));
+    return _scale == null ? result : result.setScale(_scale, 
RoundingMode.HALF_EVEN);
   }
 
   public BigDecimal getDefaultResult(GroupByResultHolder groupByResultHolder, 
int groupKey) {
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/array/BaseArrayAggFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/array/BaseArrayAggFunction.java
index 7abc7c9f0da..ad4cde3b825 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/array/BaseArrayAggFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/array/BaseArrayAggFunction.java
@@ -18,6 +18,7 @@
  */
 package org.apache.pinot.core.query.aggregation.function.array;
 
+import java.lang.reflect.Array;
 import java.nio.ByteBuffer;
 import java.util.Map;
 import javax.annotation.Nullable;
@@ -100,4 +101,23 @@ public abstract class BaseArrayAggFunction<I, F> extends 
BaseSingleInputAggregat
   public I extractGroupByResult(GroupByResultHolder groupByResultHolder, int 
groupKey) {
     return groupByResultHolder.getResult(groupKey);
   }
+
+  /// Merges two final results by concatenating them. Final results are only 
merged across partitions of the
+  /// aggregated values, so the values of the distinct variants do not overlap.
+  @Override
+  public F mergeFinalResult(F finalResult1, F finalResult2) {
+    int length1 = Array.getLength(finalResult1);
+    if (length1 == 0) {
+      return finalResult2;
+    }
+    int length2 = Array.getLength(finalResult2);
+    if (length2 == 0) {
+      return finalResult1;
+    }
+    //noinspection unchecked
+    F mergedResult = (F) 
Array.newInstance(finalResult1.getClass().getComponentType(), length1 + 
length2);
+    System.arraycopy(finalResult1, 0, mergedResult, 0, length1);
+    System.arraycopy(finalResult2, 0, mergedResult, length1, length2);
+    return mergedResult;
+  }
 }
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/array/ListAggFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/array/ListAggFunction.java
index 78c02b54d22..71c371ba8af 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/array/ListAggFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/array/ListAggFunction.java
@@ -23,6 +23,7 @@ import it.unimi.dsi.fastutil.objects.ObjectCollection;
 import java.util.Arrays;
 import java.util.Map;
 import javax.annotation.Nullable;
+import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.pinot.common.CustomObject;
 import org.apache.pinot.common.request.context.ExpressionContext;
@@ -198,12 +199,18 @@ public class ListAggFunction extends 
BaseSingleInputAggregationFunction<ObjectCo
     return ColumnDataType.STRING;
   }
 
+  /// Joins the values with the separator. Returns `null` when there are no 
values, which keeps them apart from a
+  /// single empty-string value.
   @Nullable
   @Override
   public String extractFinalResult(@Nullable ObjectCollection<String> strings) 
{
-    if (strings == null) {
-      return null;
-    }
-    return StringUtils.join(strings, _separator);
+    return CollectionUtils.isNotEmpty(strings) ? StringUtils.join(strings, 
_separator) : null;
+  }
+
+  /// Merges two final results by joining them with the separator. Final 
results are only merged across partitions of
+  /// the aggregated values, so the values of the distinct variant do not 
overlap.
+  @Override
+  public String mergeFinalResult(String finalResult1, String finalResult2) {
+    return finalResult1 + _separator + finalResult2;
   }
 }
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/array/SumArrayDoubleAggregationFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/array/SumArrayDoubleAggregationFunction.java
index dfa9693adaf..3f19d975dbe 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/array/SumArrayDoubleAggregationFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/array/SumArrayDoubleAggregationFunction.java
@@ -19,6 +19,7 @@
 package org.apache.pinot.core.query.aggregation.function.array;
 
 import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import javax.annotation.Nullable;
@@ -177,4 +178,13 @@ public class SumArrayDoubleAggregationFunction
   public double[] extractFinalResult(@Nullable DoubleArrayList result) {
     return result != null ? ArrayListUtils.toDoubleArray(result) : null;
   }
+
+  @Override
+  public double[] mergeFinalResult(double[] finalResult1, double[] 
finalResult2) {
+    double[] mergedResult = Arrays.copyOf(finalResult1, 
Math.max(finalResult1.length, finalResult2.length));
+    for (int i = 0; i < finalResult2.length; i++) {
+      mergedResult[i] += finalResult2[i];
+    }
+    return mergedResult;
+  }
 }
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/array/SumArrayLongAggregationFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/array/SumArrayLongAggregationFunction.java
index 11f52798e1e..99c0ef57fea 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/array/SumArrayLongAggregationFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/array/SumArrayLongAggregationFunction.java
@@ -19,6 +19,7 @@
 package org.apache.pinot.core.query.aggregation.function.array;
 
 import it.unimi.dsi.fastutil.longs.LongArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import javax.annotation.Nullable;
@@ -177,4 +178,13 @@ public class SumArrayLongAggregationFunction
   public long[] extractFinalResult(@Nullable LongArrayList result) {
     return result != null ? ArrayListUtils.toLongArray(result) : null;
   }
+
+  @Override
+  public long[] mergeFinalResult(long[] finalResult1, long[] finalResult2) {
+    long[] mergedResult = Arrays.copyOf(finalResult1, 
Math.max(finalResult1.length, finalResult2.length));
+    for (int i = 0; i < finalResult2.length; i++) {
+      mergedResult[i] += finalResult2[i];
+    }
+    return mergedResult;
+  }
 }
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/funnel/FunnelCountAggregationFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/funnel/FunnelCountAggregationFunction.java
index f405a75114f..bebe3eb2bd9 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/funnel/FunnelCountAggregationFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/funnel/FunnelCountAggregationFunction.java
@@ -182,10 +182,11 @@ public class FunnelCountAggregationFunction<A, I> 
implements AggregationFunction
 
   @Override
   public long[] mergeFinalResult(long[] finalResult1, long[] finalResult2) {
+    long[] mergedResult = finalResult1.clone();
     for (int i = 0; i < _numSteps; i++) {
-      finalResult1[i] += finalResult2[i];
+      mergedResult[i] += finalResult2[i];
     }
-    return finalResult1;
+    return mergedResult;
   }
 
   @Override
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/funnel/window/FunnelStepDurationStatsAggregationFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/funnel/window/FunnelStepDurationStatsAggregationFunction.java
index b3518a92905..2cdd28c403b 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/funnel/window/FunnelStepDurationStatsAggregationFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/funnel/window/FunnelStepDurationStatsAggregationFunction.java
@@ -257,9 +257,4 @@ public class FunnelStepDurationStatsAggregationFunction 
extends FunnelBaseAggreg
     }
     return maxStep;
   }
-
-  @Override
-  public double[] mergeFinalResult(double[] finalResult1, double[] 
finalResult2) {
-    return finalResult1;
-  }
 }
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/AggregationFunctionFactoryTest.java
 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/AggregationFunctionFactoryTest.java
index 0d44e9bcf00..d10465d426c 100644
--- 
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/AggregationFunctionFactoryTest.java
+++ 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/AggregationFunctionFactoryTest.java
@@ -202,6 +202,12 @@ public class AggregationFunctionFactoryTest {
     assertEquals(aggregationFunction.getType(), 
AggregationFunctionType.DISTINCTCOUNTRAWHLLPLUS);
     assertEquals(aggregationFunction.getResultColumnName(), 
function.toString());
 
+    function = getFunction("DiStInCtCoUnTrAwUlL");
+    aggregationFunction = 
AggregationFunctionFactory.getAggregationFunction(function, false);
+    assertTrue(aggregationFunction instanceof 
DistinctCountRawULLAggregationFunction);
+    assertEquals(aggregationFunction.getType(), 
AggregationFunctionType.DISTINCTCOUNTRAWULL);
+    assertEquals(aggregationFunction.getResultColumnName(), 
function.toString());
+
     function = getFunction("FaStHlL");
     aggregationFunction = 
AggregationFunctionFactory.getAggregationFunction(function, false);
     assertTrue(aggregationFunction instanceof FastHLLAggregationFunction);
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/AnyValueAggregationFunctionTest.java
 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/AnyValueAggregationFunctionTest.java
index 77d27f06195..516c0c74358 100644
--- 
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/AnyValueAggregationFunctionTest.java
+++ 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/AnyValueAggregationFunctionTest.java
@@ -511,6 +511,12 @@ public class AnyValueAggregationFunctionTest extends 
AbstractAggregationFunction
         serialize(bigDecimalFunction, value)), new BigDecimal("1")), value);
   }
 
+  @Test
+  void testMergeFinalResult() {
+    AnyValueAggregationFunction function = createFunction("LONG");
+    assertEquals(function.mergeFinalResult(1L, 2L), 1L);
+  }
+
   private static AnyValueAggregationFunction createFunction(String dataType) {
     return new AnyValueAggregationFunction(List.of(EXPRESSION, 
ExpressionContext.forLiteral(DataType.STRING, dataType)),
         true);
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/DistinctCountCPCSketchAggregationFunctionTest.java
 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/DistinctCountCPCSketchAggregationFunctionTest.java
index 051a7f86f0c..fb5441805dc 100644
--- 
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/DistinctCountCPCSketchAggregationFunctionTest.java
+++ 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/DistinctCountCPCSketchAggregationFunctionTest.java
@@ -30,9 +30,10 @@ import 
org.apache.pinot.core.query.aggregation.AggregationResultHolder;
 import org.apache.pinot.segment.local.customobject.CpcSketchAccumulator;
 import org.apache.pinot.segment.local.customobject.SerializedCPCSketch;
 import org.apache.pinot.segment.spi.Constants;
-import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import static org.testng.Assert.*;
+
 
 public class DistinctCountCPCSketchAggregationFunctionTest {
 
@@ -41,18 +42,18 @@ public class DistinctCountCPCSketchAggregationFunctionTest {
     DistinctCountCPCSketchAggregationFunction function =
         new 
DistinctCountCPCSketchAggregationFunction(List.of(ExpressionContext.forIdentifier("col")),
 false);
 
-    Assert.assertTrue(function.canUseStarTree(Map.of()));
-    
Assert.assertTrue(function.canUseStarTree(Map.of(Constants.CPCSKETCH_LGK_KEY, 
"12")));
-    
Assert.assertTrue(function.canUseStarTree(Map.of(Constants.CPCSKETCH_LGK_KEY, 
12)));
-    
Assert.assertFalse(function.canUseStarTree(Map.of(Constants.CPCSKETCH_LGK_KEY, 
11)));
+    assertTrue(function.canUseStarTree(Map.of()));
+    assertTrue(function.canUseStarTree(Map.of(Constants.CPCSKETCH_LGK_KEY, 
"12")));
+    assertTrue(function.canUseStarTree(Map.of(Constants.CPCSKETCH_LGK_KEY, 
12)));
+    assertFalse(function.canUseStarTree(Map.of(Constants.CPCSKETCH_LGK_KEY, 
11)));
 
     function = new DistinctCountCPCSketchAggregationFunction(
         List.of(ExpressionContext.forIdentifier("col"), 
ExpressionContext.forLiteral(Literal.intValue(12))), false);
 
-    Assert.assertTrue(function.canUseStarTree(Map.of()));
-    
Assert.assertTrue(function.canUseStarTree(Map.of(Constants.CPCSKETCH_LGK_KEY, 
"12")));
-    
Assert.assertTrue(function.canUseStarTree(Map.of(Constants.CPCSKETCH_LGK_KEY, 
12)));
-    
Assert.assertFalse(function.canUseStarTree(Map.of(Constants.CPCSKETCH_LGK_KEY, 
11)));
+    assertTrue(function.canUseStarTree(Map.of()));
+    assertTrue(function.canUseStarTree(Map.of(Constants.CPCSKETCH_LGK_KEY, 
"12")));
+    assertTrue(function.canUseStarTree(Map.of(Constants.CPCSKETCH_LGK_KEY, 
12)));
+    assertFalse(function.canUseStarTree(Map.of(Constants.CPCSKETCH_LGK_KEY, 
11)));
   }
 
   @Test
@@ -62,10 +63,10 @@ public class DistinctCountCPCSketchAggregationFunctionTest {
             
ExpressionContext.forLiteral(Literal.stringValue("nominalEntries=8192"))), 
false);
 
     // Default lgK = 12 / K=4096
-    Assert.assertFalse(function.canUseStarTree(Map.of()));
-    
Assert.assertTrue(function.canUseStarTree(Map.of(Constants.CPCSKETCH_LGK_KEY, 
"14")));
-    
Assert.assertTrue(function.canUseStarTree(Map.of(Constants.CPCSKETCH_LGK_KEY, 
13)));
-    
Assert.assertTrue(function.canUseStarTree(Map.of(Constants.CPCSKETCH_LGK_KEY, 
"13")));
+    assertFalse(function.canUseStarTree(Map.of()));
+    assertTrue(function.canUseStarTree(Map.of(Constants.CPCSKETCH_LGK_KEY, 
"14")));
+    assertTrue(function.canUseStarTree(Map.of(Constants.CPCSKETCH_LGK_KEY, 
13)));
+    assertTrue(function.canUseStarTree(Map.of(Constants.CPCSKETCH_LGK_KEY, 
"13")));
   }
 
   /// Tests the empty result path used by BROKER_EVALUATE when all segments 
are pruned.
@@ -78,15 +79,15 @@ public class DistinctCountCPCSketchAggregationFunctionTest {
 
     AggregationResultHolder holder = function.createAggregationResultHolder();
     CpcSketchAccumulator accumulator = 
function.extractAggregationResult(holder);
-    Assert.assertTrue(accumulator.isEmpty());
+    assertTrue(accumulator.isEmpty());
 
     // extractFinalResult should return 0 for an empty accumulator
     Comparable result = function.extractFinalResult(accumulator);
-    Assert.assertEquals(result, 0L);
+    assertEquals(result, 0L);
 
     // The result must be convertible via the declared column type
     Object converted = function.getFinalResultColumnType().convert(result);
-    Assert.assertEquals(converted, 0L);
+    assertEquals(converted, 0L);
   }
 
   @Test
@@ -96,22 +97,22 @@ public class DistinctCountCPCSketchAggregationFunctionTest {
 
     AggregationResultHolder holder = function.createAggregationResultHolder();
     CpcSketchAccumulator accumulator = 
function.extractAggregationResult(holder);
-    Assert.assertTrue(accumulator.isEmpty());
+    assertTrue(accumulator.isEmpty());
 
     // extractFinalResult should return a SerializedCPCSketch
     SerializedCPCSketch rawResult = function.extractFinalResult(accumulator);
-    Assert.assertNotNull(rawResult);
+    assertNotNull(rawResult);
 
     // The declared column type is STRING; convert() must produce a String
-    Assert.assertEquals(function.getFinalResultColumnType(), 
ColumnDataType.STRING);
+    assertEquals(function.getFinalResultColumnType(), ColumnDataType.STRING);
     Object converted = function.getFinalResultColumnType().convert(rawResult);
-    Assert.assertTrue(converted instanceof String);
+    assertTrue(converted instanceof String);
 
     // The string should be a valid Base64-encoded CPC sketch that round-trips
     String base64 = (String) converted;
     byte[] bytes = Base64.getDecoder().decode(base64);
     CpcSketch deserialized = CpcSketch.heapify(MemorySegment.ofArray(bytes));
-    Assert.assertEquals(deserialized.getEstimate(), 0.0);
+    assertEquals(deserialized.getEstimate(), 0.0);
   }
 
   @Test
@@ -128,14 +129,30 @@ public class 
DistinctCountCPCSketchAggregationFunctionTest {
 
     // merge(empty, non-empty) should return non-empty
     CpcSketchAccumulator result = function.merge(empty1, nonEmpty);
-    Assert.assertSame(result, nonEmpty);
+    assertSame(result, nonEmpty);
 
     // merge(non-empty, empty) should return non-empty
     result = function.merge(nonEmpty, empty2);
-    Assert.assertSame(result, nonEmpty);
+    assertSame(result, nonEmpty);
 
     // merge(empty, empty) should return empty
     result = function.merge(new CpcSketchAccumulator(12, 2), new 
CpcSketchAccumulator(12, 2));
-    Assert.assertTrue(result.isEmpty());
+    assertTrue(result.isEmpty());
+  }
+
+  @Test
+  public void testMergeRawFinalResults() {
+    DistinctCountRawCPCSketchAggregationFunction function =
+        new 
DistinctCountRawCPCSketchAggregationFunction(List.of(ExpressionContext.forIdentifier("col")),
 false);
+    CpcSketch sketch1 = new CpcSketch(12);
+    sketch1.update("a");
+    sketch1.update("b");
+    CpcSketch sketch2 = new CpcSketch(12);
+    sketch2.update("c");
+
+    String merged = function.mergeFinalResult(new 
SerializedCPCSketch(sketch1).toString(),
+        new SerializedCPCSketch(sketch2).toString());
+    CpcSketch mergedSketch = 
CpcSketch.heapify(MemorySegment.ofArray(Base64.getDecoder().decode(merged)));
+    assertEquals(Math.round(mergedSketch.getEstimate()), 3L);
   }
 }
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/DistinctCountULLAggregationFunctionTest.java
 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/DistinctCountULLAggregationFunctionTest.java
index 0abed9d5690..2a31b0a3aa7 100644
--- 
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/DistinctCountULLAggregationFunctionTest.java
+++ 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/DistinctCountULLAggregationFunctionTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.pinot.core.query.aggregation.function;
 
+import com.dynatrace.hash4j.distinctcount.UltraLogLog;
 import java.util.List;
 import java.util.Map;
 import org.apache.pinot.common.request.Literal;
@@ -27,6 +28,7 @@ import org.apache.pinot.core.common.SyntheticBlockValSets;
 import org.apache.pinot.core.query.aggregation.AggregationResultHolder;
 import org.apache.pinot.core.query.aggregation.groupby.GroupByResultHolder;
 import 
org.apache.pinot.core.query.aggregation.groupby.ObjectGroupByResultHolder;
+import org.apache.pinot.segment.local.customobject.SerializedULL;
 import org.apache.pinot.segment.spi.Constants;
 import org.apache.pinot.segment.spi.index.reader.Dictionary;
 import org.apache.pinot.spi.data.FieldSpec.DataType;
@@ -90,6 +92,19 @@ public class DistinctCountULLAggregationFunctionTest {
     return Map.of(COLUMN, SyntheticBlockValSets.LongMV.create(null, MV_ROWS));
   }
 
+  @Test
+  public void testMergeRawFinalResults() {
+    DistinctCountRawULLAggregationFunction function =
+        new 
DistinctCountRawULLAggregationFunction(List.of(ExpressionContext.forIdentifier("col")),
 false);
+    UltraLogLog ull1 = 
UltraLogLog.create(12).add(0x0123456789ABCDEFL).add(0x1111111111111111L);
+    UltraLogLog ull2 = UltraLogLog.create(12).add(0x7777777777777777L);
+    UltraLogLog expected =
+        
UltraLogLog.create(12).add(0x0123456789ABCDEFL).add(0x1111111111111111L).add(0x7777777777777777L);
+
+    String merged = function.mergeFinalResult(new 
SerializedULL(ull1).toString(), new SerializedULL(ull2).toString());
+    assertEquals(merged, new SerializedULL(expected).toString());
+  }
+
   /// Aggregating a multi-value column gives exactly what aggregating its 
flattened values as a single-value column
   /// gives. Comparing the two rather than asserting an estimate keeps this 
exact for a sketch.
   @Test
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/FinalResultMergeTest.java
 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/FinalResultMergeTest.java
new file mode 100644
index 00000000000..7e226a85d46
--- /dev/null
+++ 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/FinalResultMergeTest.java
@@ -0,0 +1,176 @@
+/**
+ * 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.pinot.core.query.aggregation.function;
+
+import java.io.IOException;
+import java.lang.foreign.MemorySegment;
+import java.math.BigDecimal;
+import java.util.Base64;
+import java.util.List;
+import org.apache.datasketches.common.ArrayOfStringsSerDe;
+import org.apache.datasketches.frequencies.FrequentItemsSketch;
+import org.apache.datasketches.frequencies.FrequentLongsSketch;
+import org.apache.datasketches.kll.KllDoublesSketch;
+import org.apache.pinot.common.request.Literal;
+import org.apache.pinot.common.request.context.ExpressionContext;
+import 
org.apache.pinot.core.query.aggregation.function.array.ArrayAggBigDecimalFunction;
+import 
org.apache.pinot.core.query.aggregation.function.array.ArrayAggDistinctIntFunction;
+import 
org.apache.pinot.core.query.aggregation.function.array.ArrayAggIntFunction;
+import 
org.apache.pinot.core.query.aggregation.function.array.SumArrayDoubleAggregationFunction;
+import 
org.apache.pinot.core.query.aggregation.function.array.SumArrayLongAggregationFunction;
+import org.apache.pinot.core.query.utils.idset.IdSet;
+import org.apache.pinot.core.query.utils.idset.IdSets;
+import 
org.apache.pinot.segment.local.customobject.SerializedFrequentLongsSketch;
+import 
org.apache.pinot.segment.local.customobject.SerializedFrequentStringsSketch;
+import org.apache.pinot.segment.local.customobject.SerializedKLL;
+import org.apache.pinot.spi.data.FieldSpec.DataType;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+
+/// Merges the final results that servers or leaf stage workers return for 
partitions of the aggregated values, in the
+/// form they are transported in.
+public class FinalResultMergeTest {
+  private static final ExpressionContext COLUMN = 
ExpressionContext.forIdentifier("column");
+
+  @Test
+  public void testHistogram() {
+    HistogramAggregationFunction function = new 
HistogramAggregationFunction(List.of(COLUMN,
+        ExpressionContext.forLiteral(Literal.doubleValue(0)), 
ExpressionContext.forLiteral(Literal.doubleValue(10)),
+        ExpressionContext.forLiteral(Literal.intValue(2))), false);
+    double[] finalResult1 = {1, 2};
+    double[] finalResult2 = {3, 4};
+    assertEquals(function.mergeFinalResult(finalResult1, finalResult2), new 
double[]{4, 6});
+    assertEquals(finalResult1, new double[]{1, 2});
+    assertEquals(finalResult2, new double[]{3, 4});
+  }
+
+  @Test
+  public void testSumArrayLong() {
+    SumArrayLongAggregationFunction function = new 
SumArrayLongAggregationFunction(List.of(COLUMN), false);
+    long[] longer = {1, 2, 3};
+    long[] shorter = {4};
+    assertEquals(function.mergeFinalResult(longer, shorter), new long[]{5, 2, 
3});
+    assertEquals(function.mergeFinalResult(shorter, longer), new long[]{5, 2, 
3});
+    assertEquals(longer, new long[]{1, 2, 3});
+    assertEquals(shorter, new long[]{4});
+  }
+
+  @Test
+  public void testSumArrayDouble() {
+    SumArrayDoubleAggregationFunction function = new 
SumArrayDoubleAggregationFunction(List.of(COLUMN), false);
+    double[] longer = {1, 2};
+    double[] shorter = {0.5};
+    assertEquals(function.mergeFinalResult(longer, shorter), new double[]{1.5, 
2});
+    assertEquals(function.mergeFinalResult(shorter, longer), new double[]{1.5, 
2});
+    assertEquals(longer, new double[]{1, 2});
+    assertEquals(shorter, new double[]{0.5});
+  }
+
+  @Test
+  public void testArrayAgg() {
+    ArrayAggIntFunction function = new ArrayAggIntFunction(COLUMN, 
DataType.INT, false);
+    assertEquals(function.mergeFinalResult(new int[]{1, 2}, new int[]{2}), new 
int[]{1, 2, 2});
+    assertEquals(function.mergeFinalResult(new int[0], new int[]{3}), new 
int[]{3});
+    assertEquals(function.mergeFinalResult(new int[]{3}, new int[0]), new 
int[]{3});
+
+    ArrayAggDistinctIntFunction distinctFunction = new 
ArrayAggDistinctIntFunction(COLUMN, DataType.INT, false);
+    assertEquals(distinctFunction.mergeFinalResult(new int[]{1, 2}, new 
int[]{3}), new int[]{1, 2, 3});
+
+    ArrayAggBigDecimalFunction bigDecimalFunction = new 
ArrayAggBigDecimalFunction(COLUMN, false);
+    BigDecimal[] merged =
+        bigDecimalFunction.mergeFinalResult(new BigDecimal[]{BigDecimal.ONE}, 
new BigDecimal[]{BigDecimal.TEN});
+    assertEquals(merged, new BigDecimal[]{BigDecimal.ONE, BigDecimal.TEN});
+  }
+
+  @Test
+  public void testPercentileRawKLL() {
+    List<ExpressionContext> arguments = List.of(COLUMN, 
ExpressionContext.forLiteral(Literal.doubleValue(50)));
+    for (PercentileKLLAggregationFunction function : List.of(new 
PercentileRawKLLAggregationFunction(arguments, false),
+        new PercentileRawKLLMVAggregationFunction(arguments, false))) {
+      KllDoublesSketch sketch1 = KllDoublesSketch.newHeapInstance();
+      sketch1.update(1);
+      sketch1.update(2);
+      KllDoublesSketch sketch2 = KllDoublesSketch.newHeapInstance();
+      sketch2.update(3);
+      String merged = (String) function.mergeFinalResult(new 
SerializedKLL(sketch1, 50).toString(),
+          new SerializedKLL(sketch2, 50).toString());
+      KllDoublesSketch mergedSketch =
+          
KllDoublesSketch.heapify(MemorySegment.ofArray(Base64.getDecoder().decode(merged)));
+      assertEquals(mergedSketch.getN(), 3);
+      assertEquals(mergedSketch.getMinItem(), 1.0);
+      assertEquals(mergedSketch.getMaxItem(), 3.0);
+    }
+  }
+
+  @Test
+  public void testFrequentLongsSketch() {
+    FrequentLongsSketchAggregationFunction function =
+        new FrequentLongsSketchAggregationFunction(List.of(COLUMN), false);
+    FrequentLongsSketch sketch1 = new FrequentLongsSketch(8);
+    sketch1.update(1, 3);
+    sketch1.update(2);
+    FrequentLongsSketch sketch2 = new FrequentLongsSketch(8);
+    sketch2.update(3, 2);
+    String merged = function.mergeFinalResult(new 
SerializedFrequentLongsSketch(sketch1).toString(),
+        new SerializedFrequentLongsSketch(sketch2).toString());
+    FrequentLongsSketch mergedSketch =
+        
FrequentLongsSketch.getInstance(MemorySegment.ofArray(Base64.getDecoder().decode(merged)));
+    assertEquals(mergedSketch.getStreamLength(), 6);
+    assertEquals(mergedSketch.getEstimate(1), 3);
+    assertEquals(mergedSketch.getEstimate(2), 1);
+    assertEquals(mergedSketch.getEstimate(3), 2);
+  }
+
+  @Test
+  public void testFrequentStringsSketch() {
+    FrequentStringsSketchAggregationFunction function =
+        new FrequentStringsSketchAggregationFunction(List.of(COLUMN), false);
+    FrequentItemsSketch<String> sketch1 = new FrequentItemsSketch<>(8);
+    sketch1.update("a", 3);
+    sketch1.update("b");
+    FrequentItemsSketch<String> sketch2 = new FrequentItemsSketch<>(8);
+    sketch2.update("c", 2);
+    String merged = function.mergeFinalResult(new 
SerializedFrequentStringsSketch(sketch1).toString(),
+        new SerializedFrequentStringsSketch(sketch2).toString());
+    FrequentItemsSketch<String> mergedSketch = FrequentItemsSketch.getInstance(
+        MemorySegment.ofArray(Base64.getDecoder().decode(merged)), new 
ArrayOfStringsSerDe());
+    assertEquals(mergedSketch.getStreamLength(), 6);
+    assertEquals(mergedSketch.getEstimate("a"), 3);
+    assertEquals(mergedSketch.getEstimate("b"), 1);
+    assertEquals(mergedSketch.getEstimate("c"), 2);
+  }
+
+  @Test
+  public void testIdSet()
+      throws IOException {
+    IdSetAggregationFunction function = new 
IdSetAggregationFunction(List.of(COLUMN), false);
+    IdSet idSet1 = IdSets.create(DataType.INT);
+    idSet1.add(1);
+    IdSet idSet2 = IdSets.create(DataType.INT);
+    idSet2.add(2);
+    IdSet merged = 
IdSets.fromBase64String(function.mergeFinalResult(idSet1.toBase64String(), 
idSet2.toBase64String()));
+    assertTrue(merged.contains(1));
+    assertTrue(merged.contains(2));
+    assertFalse(merged.contains(3));
+  }
+}
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/IntegerTupleSketchAggregationFunctionTest.java
 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/IntegerTupleSketchAggregationFunctionTest.java
index 20c1eb7d836..08c26999b10 100644
--- 
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/IntegerTupleSketchAggregationFunctionTest.java
+++ 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/IntegerTupleSketchAggregationFunctionTest.java
@@ -18,15 +18,25 @@
  */
 package org.apache.pinot.core.query.aggregation.function;
 
+import java.lang.foreign.MemorySegment;
+import java.util.Base64;
 import java.util.List;
 import java.util.Map;
+import org.apache.datasketches.tuple.TupleSketch;
+import org.apache.datasketches.tuple.TupleSketchIterator;
 import org.apache.datasketches.tuple.aninteger.IntegerSummary;
+import org.apache.datasketches.tuple.aninteger.IntegerSummaryDeserializer;
+import org.apache.datasketches.tuple.aninteger.IntegerTupleSketch;
 import org.apache.pinot.common.request.Literal;
 import org.apache.pinot.common.request.context.ExpressionContext;
 import org.apache.pinot.segment.spi.Constants;
-import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertThrows;
+import static org.testng.Assert.assertTrue;
+
 
 public class IntegerTupleSketchAggregationFunctionTest {
 
@@ -36,10 +46,10 @@ public class IntegerTupleSketchAggregationFunctionTest {
         new 
IntegerTupleSketchAggregationFunction(List.of(ExpressionContext.forIdentifier("col")),
             IntegerSummary.Mode.Sum, false);
 
-    Assert.assertTrue(function.canUseStarTree(Map.of()));
-    
Assert.assertTrue(function.canUseStarTree(Map.of(Constants.THETA_TUPLE_SKETCH_NOMINAL_ENTRIES,
 "16384")));
-    
Assert.assertTrue(function.canUseStarTree(Map.of(Constants.THETA_TUPLE_SKETCH_NOMINAL_ENTRIES,
 16384)));
-    
Assert.assertFalse(function.canUseStarTree(Map.of(Constants.THETA_TUPLE_SKETCH_NOMINAL_ENTRIES,
 8192)));
+    assertTrue(function.canUseStarTree(Map.of()));
+    
assertTrue(function.canUseStarTree(Map.of(Constants.THETA_TUPLE_SKETCH_NOMINAL_ENTRIES,
 "16384")));
+    
assertTrue(function.canUseStarTree(Map.of(Constants.THETA_TUPLE_SKETCH_NOMINAL_ENTRIES,
 16384)));
+    
assertFalse(function.canUseStarTree(Map.of(Constants.THETA_TUPLE_SKETCH_NOMINAL_ENTRIES,
 8192)));
   }
 
   @Test
@@ -49,10 +59,45 @@ public class IntegerTupleSketchAggregationFunctionTest {
         IntegerSummary.Mode.Sum, false);
 
     // Default StarTree lgK = 14 / K=16384
-    Assert.assertFalse(function.canUseStarTree(Map.of()));
-    
Assert.assertFalse(function.canUseStarTree(Map.of(Constants.THETA_TUPLE_SKETCH_NOMINAL_ENTRIES,
 "16384")));
-    
Assert.assertTrue(function.canUseStarTree(Map.of(Constants.THETA_TUPLE_SKETCH_NOMINAL_ENTRIES,
 "65536")));
-    
Assert.assertTrue(function.canUseStarTree(Map.of(Constants.THETA_TUPLE_SKETCH_NOMINAL_ENTRIES,
 32768)));
-    
Assert.assertTrue(function.canUseStarTree(Map.of(Constants.THETA_TUPLE_SKETCH_NOMINAL_ENTRIES,
 "32768")));
+    assertFalse(function.canUseStarTree(Map.of()));
+    
assertFalse(function.canUseStarTree(Map.of(Constants.THETA_TUPLE_SKETCH_NOMINAL_ENTRIES,
 "16384")));
+    
assertTrue(function.canUseStarTree(Map.of(Constants.THETA_TUPLE_SKETCH_NOMINAL_ENTRIES,
 "65536")));
+    
assertTrue(function.canUseStarTree(Map.of(Constants.THETA_TUPLE_SKETCH_NOMINAL_ENTRIES,
 32768)));
+    
assertTrue(function.canUseStarTree(Map.of(Constants.THETA_TUPLE_SKETCH_NOMINAL_ENTRIES,
 "32768")));
+  }
+
+  @Test
+  public void testMergeRawFinalResults() {
+    IntegerTupleSketchAggregationFunction function =
+        new 
IntegerTupleSketchAggregationFunction(List.of(ExpressionContext.forIdentifier("col")),
+            IntegerSummary.Mode.Sum, false);
+    IntegerTupleSketch sketch1 = new IntegerTupleSketch(4, 
IntegerSummary.Mode.Sum);
+    sketch1.update(1L, 1);
+    sketch1.update(2L, 2);
+    IntegerTupleSketch sketch2 = new IntegerTupleSketch(4, 
IntegerSummary.Mode.Sum);
+    sketch2.update(3L, 3);
+
+    String merged = (String) function.mergeFinalResult(serialize(sketch1), 
serialize(sketch2));
+    TupleSketch<IntegerSummary> mergedSketch = TupleSketch.heapifySketch(
+        MemorySegment.ofArray(Base64.getDecoder().decode(merged)), new 
IntegerSummaryDeserializer());
+    assertEquals(mergedSketch.getEstimate(), 3.0);
+    int sum = 0;
+    TupleSketchIterator<IntegerSummary> iterator = mergedSketch.iterator();
+    while (iterator.next()) {
+      sum += iterator.getSummary().getValue();
+    }
+    assertEquals(sum, 6);
+  }
+
+  @Test
+  public void testMergeAvgValueFinalResults() {
+    AvgValueIntegerTupleSketchAggregationFunction function =
+        new 
AvgValueIntegerTupleSketchAggregationFunction(List.of(ExpressionContext.forIdentifier("col")),
+            IntegerSummary.Mode.Sum, false);
+    assertThrows(UnsupportedOperationException.class, () -> 
function.mergeFinalResult(1L, 2L));
+  }
+
+  private static String serialize(IntegerTupleSketch sketch) {
+    return Base64.getEncoder().encodeToString(sketch.compact().toByteArray());
   }
 }
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/ListAggFunctionTest.java
 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/ListAggFunctionTest.java
index 92fce918ad5..c78059c16b9 100644
--- 
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/ListAggFunctionTest.java
+++ 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/ListAggFunctionTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.pinot.core.query.aggregation.function;
 
+import java.util.List;
 import java.util.Map;
 import org.apache.pinot.common.request.context.ExpressionContext;
 import org.apache.pinot.core.common.SyntheticBlockValSets;
@@ -27,9 +28,11 @@ import 
org.apache.pinot.core.query.aggregation.function.array.ListAggFunction;
 import org.apache.pinot.core.query.aggregation.groupby.GroupByResultHolder;
 import 
org.apache.pinot.core.query.aggregation.groupby.ObjectGroupByResultHolder;
 import org.apache.pinot.spi.data.FieldSpec.DataType;
+import org.roaringbitmap.RoaringBitmap;
 import org.testng.annotations.Test;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
 
 
 public class ListAggFunctionTest extends AbstractAggregationFunctionTest {
@@ -148,4 +151,35 @@ public class ListAggFunctionTest extends 
AbstractAggregationFunctionTest {
     assertEquals(fn.extractFinalResult(gb.getResult(0)), "A");
     assertEquals(fn.extractFinalResult(gb.getResult(1)), "A:B:C");
   }
+
+  @Test
+  public void testMergeFinalResult() {
+    ListAggFunction fn = new 
ListAggFunction(ExpressionContext.forIdentifier("myField"), ",", false);
+    assertEquals(fn.mergeFinalResult("A,B", "C"), "A,B,C");
+    assertEquals(fn.mergeFinalResult("", "A"), ",A");
+    assertEquals(fn.mergeFinalResult("A", ""), "A,");
+
+    ListAggDistinctFunction distinctFn =
+        new 
ListAggDistinctFunction(ExpressionContext.forIdentifier("myField"), ",", false);
+    assertEquals(distinctFn.mergeFinalResult("A,B", "C"), "A,B,C");
+    assertEquals(distinctFn.mergeFinalResult("", "A"), ",A");
+    assertEquals(distinctFn.mergeFinalResult("A", ""), "A,");
+  }
+
+  @Test
+  public void testFinalResultOfNullValues() {
+    RoaringBitmap nullBitmap = RoaringBitmap.bitmapOf(0, 1);
+    for (ListAggFunction fn : List.of(
+        new ListAggFunction(ExpressionContext.forIdentifier("myField"), ",", 
true),
+        new 
ListAggDistinctFunction(ExpressionContext.forIdentifier("myField"), ",", true)
+    )) {
+      AggregationResultHolder holder = fn.createAggregationResultHolder();
+      fn.aggregate(2, holder, 
Map.of(ExpressionContext.forIdentifier("myField"),
+          SyntheticBlockValSets.Str.create(nullBitmap, new String[]{"", ""})));
+      String finalResult = fn.extractFinalResult(holder.getResult());
+      assertNull(finalResult);
+      assertEquals(AggregationFunctionUtils.mergeFinalResult(fn, finalResult, 
"A"), "A");
+      assertEquals(AggregationFunctionUtils.mergeFinalResult(fn, "A", 
finalResult), "A");
+    }
+  }
 }
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/StUnionAggregationFunctionTest.java
 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/StUnionAggregationFunctionTest.java
index cad60ae7b68..2efaa6c6df6 100644
--- 
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/StUnionAggregationFunctionTest.java
+++ 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/StUnionAggregationFunctionTest.java
@@ -27,6 +27,7 @@ import 
org.apache.pinot.core.query.aggregation.AggregationResultHolder;
 import org.apache.pinot.segment.local.utils.GeometrySerializer;
 import org.apache.pinot.segment.local.utils.GeometryUtils;
 import org.apache.pinot.spi.data.FieldSpec.DataType;
+import org.apache.pinot.spi.utils.ByteArray;
 import org.locationtech.jts.geom.Geometry;
 import org.locationtech.jts.geom.util.GeometryCombiner;
 import org.locationtech.jts.operation.union.UnaryUnionOp;
@@ -79,6 +80,19 @@ public class StUnionAggregationFunctionTest {
     assertEquals(aggregationResultHolder.getResult(), expected);
   }
 
+  @Test
+  public void testMergeFinalResults()
+      throws Exception {
+    ExpressionContext expression = 
ExpressionContext.forIdentifier("geometryColumn");
+    StUnionAggregationFunction aggregationFunction = new 
StUnionAggregationFunction(List.of(expression), false);
+    Geometry polygon = GeometryUtils.GEOMETRY_WKT_READER.read("POLYGON((0 0, 0 
5, 5 5, 5 0, 0 0))");
+    Geometry line = GeometryUtils.GEOMETRY_WKT_READER.read("LINESTRING(20 20, 
25 25)");
+
+    ByteArray merged = aggregationFunction.mergeFinalResult(new 
ByteArray(GeometrySerializer.serialize(polygon)),
+        new ByteArray(GeometrySerializer.serialize(line)));
+    assertEquals(GeometrySerializer.deserialize(merged.getBytes()), 
aggregationFunction.merge(polygon, line));
+  }
+
   private static class BytesBlockValSet extends SyntheticBlockValSets.Base {
     private final byte[][] _values;
 
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/SumPrecisionAggregationFunctionTest.java
 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/SumPrecisionAggregationFunctionTest.java
index 7b5345700e0..4b43e089170 100644
--- 
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/SumPrecisionAggregationFunctionTest.java
+++ 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/SumPrecisionAggregationFunctionTest.java
@@ -18,6 +18,10 @@
  */
 package org.apache.pinot.core.query.aggregation.function;
 
+import java.math.BigDecimal;
+import java.util.List;
+import org.apache.pinot.common.request.Literal;
+import org.apache.pinot.common.request.context.ExpressionContext;
 import org.apache.pinot.queries.FluentQueryTest;
 import org.apache.pinot.spi.data.FieldSpec;
 import org.apache.pinot.spi.data.FieldSpec.DataType;
@@ -26,6 +30,8 @@ import org.apache.pinot.spi.data.Schema;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
+import static org.testng.Assert.assertEquals;
+
 
 public class SumPrecisionAggregationFunctionTest extends 
AbstractAggregationFunctionTest {
 
@@ -145,6 +151,19 @@ public class SumPrecisionAggregationFunctionTest extends 
AbstractAggregationFunc
         );
   }
 
+  @Test
+  void mergeFinalResultRoundsToPrecisionAndScale() {
+    ExpressionContext column = ExpressionContext.forIdentifier("myField");
+    ExpressionContext precision = 
ExpressionContext.forLiteral(Literal.intValue(3));
+    SumPrecisionAggregationFunction function = new 
SumPrecisionAggregationFunction(List.of(column, precision), false);
+    assertEquals(function.mergeFinalResult(new BigDecimal("999"), new 
BigDecimal("999")), new BigDecimal("2.00E+3"));
+
+    ExpressionContext scale = 
ExpressionContext.forLiteral(Literal.intValue(1));
+    function = new SumPrecisionAggregationFunction(List.of(column, precision, 
scale), false);
+    assertEquals(function.mergeFinalResult(new BigDecimal("999.0"), new 
BigDecimal("999.0")),
+        new BigDecimal("2000.0"));
+  }
+
   private String getStringValueOfSum(int sum, DataType dataType) {
     if (dataType == DataType.FLOAT || dataType == DataType.DOUBLE) {
       return String.valueOf((double) sum);
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/funnel/FunnelFinalResultTest.java
 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/funnel/FunnelFinalResultTest.java
index 4de127fbc34..9cc85adf9e0 100644
--- 
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/funnel/FunnelFinalResultTest.java
+++ 
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/funnel/FunnelFinalResultTest.java
@@ -112,6 +112,22 @@ public class FunnelFinalResultTest {
     assertEquals(function.mergeFinalResult(new String[0], oneFunnel), 
oneFunnel);
   }
 
+  @Test
+  public void testFunnelCountFinalResultMerge() {
+    List<ExpressionContext> arguments = List.of(
+        ExpressionContext.forFunction(new 
FunctionContext(FunctionContext.Type.TRANSFORM, "steps",
+            List.of(ExpressionContext.forIdentifier("step0"), 
ExpressionContext.forIdentifier("step1")))),
+        ExpressionContext.forFunction(new 
FunctionContext(FunctionContext.Type.TRANSFORM, "correlateby",
+            List.of(ExpressionContext.forIdentifier("userId")))));
+    FunnelCountAggregationFunction<?, ?> function =
+        (FunnelCountAggregationFunction<?, ?>) new 
FunnelCountAggregationFunctionFactory(arguments, true).get();
+    long[] finalResult1 = {2, 1};
+    long[] finalResult2 = {1, 0};
+    assertEquals(function.mergeFinalResult(finalResult1, finalResult2), new 
long[]{3, 1});
+    assertEquals(finalResult1, new long[]{2, 1});
+    assertEquals(finalResult2, new long[]{1, 0});
+  }
+
   @Test
   public void testBitmapFinalExtractionAvoidsUnnecessaryCopies() {
     RoaringBitmap firstStep = mock(RoaringBitmap.class);
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/queries/ArrayAggQueriesTest.java 
b/pinot-core/src/test/java/org/apache/pinot/queries/ArrayAggQueriesTest.java
index 674203533e9..dcb34efd42d 100644
--- a/pinot-core/src/test/java/org/apache/pinot/queries/ArrayAggQueriesTest.java
+++ b/pinot-core/src/test/java/org/apache/pinot/queries/ArrayAggQueriesTest.java
@@ -25,6 +25,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Random;
 import java.util.Set;
 import java.util.UUID;
@@ -45,6 +46,7 @@ import org.apache.pinot.spi.config.table.TableType;
 import org.apache.pinot.spi.data.FieldSpec.DataType;
 import org.apache.pinot.spi.data.Schema;
 import org.apache.pinot.spi.data.readers.GenericRow;
+import 
org.apache.pinot.spi.utils.CommonConstants.Broker.Request.QueryOptionKey;
 import org.apache.pinot.spi.utils.ReadMode;
 import org.apache.pinot.spi.utils.builder.TableConfigBuilder;
 import org.testng.annotations.AfterClass;
@@ -189,17 +191,20 @@ public class ArrayAggQueriesTest extends BaseQueriesTest {
       assertEquals(((List) aggregationResult.get(i)).size(), NUM_RECORDS);
     }
 
-    // Inter segments
-    ResultTable resultTable = getBrokerResponse(query).getResultTable();
-    assertEquals(resultTable.getRows().get(0).length, 8);
-    assertEquals(((int[]) resultTable.getRows().get(0)[0]).length, 4 * 
NUM_RECORDS);
-    assertEquals(((long[]) resultTable.getRows().get(0)[1]).length, 4 * 
NUM_RECORDS);
-    assertEquals(((float[]) resultTable.getRows().get(0)[2]).length, 4 * 
NUM_RECORDS);
-    assertEquals(((double[]) resultTable.getRows().get(0)[3]).length, 4 * 
NUM_RECORDS);
-    assertEquals(((String[]) resultTable.getRows().get(0)[4]).length, 4 * 
NUM_RECORDS);
-    assertEquals(((String[]) resultTable.getRows().get(0)[5]).length, 4 * 
NUM_RECORDS);
-    assertEquals(((String[]) resultTable.getRows().get(0)[6]).length, 4 * 
NUM_RECORDS);
-    assertEquals(((String[]) resultTable.getRows().get(0)[7]).length, 4 * 
NUM_RECORDS);
+    // Inter segments, with and without servers returning final results
+    for (Map<String, String> queryOptions : List.of(Map.<String, String>of(),
+        Map.of(QueryOptionKey.SERVER_RETURN_FINAL_RESULT, "true"))) {
+      ResultTable resultTable = getBrokerResponse(query, 
queryOptions).getResultTable();
+      assertEquals(resultTable.getRows().get(0).length, 8);
+      assertEquals(((int[]) resultTable.getRows().get(0)[0]).length, 4 * 
NUM_RECORDS);
+      assertEquals(((long[]) resultTable.getRows().get(0)[1]).length, 4 * 
NUM_RECORDS);
+      assertEquals(((float[]) resultTable.getRows().get(0)[2]).length, 4 * 
NUM_RECORDS);
+      assertEquals(((double[]) resultTable.getRows().get(0)[3]).length, 4 * 
NUM_RECORDS);
+      assertEquals(((String[]) resultTable.getRows().get(0)[4]).length, 4 * 
NUM_RECORDS);
+      assertEquals(((String[]) resultTable.getRows().get(0)[5]).length, 4 * 
NUM_RECORDS);
+      assertEquals(((String[]) resultTable.getRows().get(0)[6]).length, 4 * 
NUM_RECORDS);
+      assertEquals(((String[]) resultTable.getRows().get(0)[7]).length, 4 * 
NUM_RECORDS);
+    }
   }
 
   @Test
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/queries/DistinctCountThetaSketchQueriesTest.java
 
b/pinot-core/src/test/java/org/apache/pinot/queries/DistinctCountThetaSketchQueriesTest.java
index 7d498377724..c7080d07216 100644
--- 
a/pinot-core/src/test/java/org/apache/pinot/queries/DistinctCountThetaSketchQueriesTest.java
+++ 
b/pinot-core/src/test/java/org/apache/pinot/queries/DistinctCountThetaSketchQueriesTest.java
@@ -25,6 +25,7 @@ import java.util.Arrays;
 import java.util.Base64;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import org.apache.commons.io.FileUtils;
 import org.apache.datasketches.theta.ThetaSketch;
 import org.apache.datasketches.theta.UpdatableThetaSketch;
@@ -50,6 +51,7 @@ import org.apache.pinot.spi.data.FieldSpec.DataType;
 import org.apache.pinot.spi.data.Schema;
 import org.apache.pinot.spi.data.readers.GenericRow;
 import org.apache.pinot.spi.exception.BadQueryRequestException;
+import 
org.apache.pinot.spi.utils.CommonConstants.Broker.Request.QueryOptionKey;
 import org.apache.pinot.spi.utils.ReadMode;
 import org.apache.pinot.spi.utils.builder.TableConfigBuilder;
 import org.testng.annotations.AfterClass;
@@ -302,6 +304,11 @@ public class DistinctCountThetaSketchQueriesTest extends 
BaseQueriesTest {
     ThetaSketch sketch =
         
ObjectSerDeUtils.DATA_SKETCH_THETA_SER_DE.deserialize(Base64.getDecoder().decode(serializedSketch));
     assertEquals(Math.round(sketch.getEstimate()), NUM_RECORDS);
+
+    brokerResponse = getBrokerResponse(query, 
Map.of(QueryOptionKey.SERVER_RETURN_FINAL_RESULT, "true"));
+    serializedSketch = (String) 
brokerResponse.getResultTable().getRows().get(0)[0];
+    sketch = 
ObjectSerDeUtils.DATA_SKETCH_THETA_SER_DE.deserialize(Base64.getDecoder().decode(serializedSketch));
+    assertEquals(Math.round(sketch.getEstimate()), NUM_RECORDS);
   }
 
   @Test
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/queries/InterSegmentAggregationSingleValueQueriesTest.java
 
b/pinot-core/src/test/java/org/apache/pinot/queries/InterSegmentAggregationSingleValueQueriesTest.java
index d85a8d9e500..5912d03d3fb 100644
--- 
a/pinot-core/src/test/java/org/apache/pinot/queries/InterSegmentAggregationSingleValueQueriesTest.java
+++ 
b/pinot-core/src/test/java/org/apache/pinot/queries/InterSegmentAggregationSingleValueQueriesTest.java
@@ -20,6 +20,7 @@ package org.apache.pinot.queries;
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 import java.util.function.Function;
 import org.apache.pinot.common.response.broker.BrokerResponseNative;
 import org.apache.pinot.common.response.broker.ResultTable;
@@ -28,6 +29,7 @@ import 
org.apache.pinot.common.utils.DataSchema.ColumnDataType;
 import org.apache.pinot.core.common.ObjectSerDeUtils;
 import org.apache.pinot.core.plan.maker.InstancePlanMakerImplV2;
 import org.apache.pinot.spi.utils.BytesUtils;
+import 
org.apache.pinot.spi.utils.CommonConstants.Broker.Request.QueryOptionKey;
 import org.testng.annotations.Test;
 
 import static org.testng.Assert.assertFalse;
@@ -36,6 +38,8 @@ import static org.testng.Assert.assertTrue;
 
 public class InterSegmentAggregationSingleValueQueriesTest extends 
BaseSingleValueQueriesTest {
   private static final String GROUP_BY = " GROUP BY column9 ORDER BY v1 DESC, 
v2 DESC LIMIT 1";
+  private static final Map<String, String> SERVER_RETURN_FINAL_RESULT =
+      Map.of(QueryOptionKey.SERVER_RETURN_FINAL_RESULT, "true");
 
   // Allow 5% quantile error due to the randomness of TDigest merge
   private static final double PERCENTILE_TDIGEST_DELTA = 0.05 * 
Integer.MAX_VALUE;
@@ -324,6 +328,10 @@ public class InterSegmentAggregationSingleValueQueriesTest 
extends BaseSingleVal
     QueriesTestUtils.testInterSegmentsResult(brokerResponse, 120000L, 0L, 0L, 
120000L, expectedResultTable,
         cardinalityExtractor);
 
+    brokerResponse = getBrokerResponse(query, SERVER_RETURN_FINAL_RESULT);
+    QueriesTestUtils.testInterSegmentsResult(brokerResponse, 120000L, 0L, 0L, 
120000L, expectedResultTable,
+        cardinalityExtractor);
+
     brokerResponse = getBrokerResponse(query + FILTER);
     expectedResultTable = new ResultTable(expectedDataSchema, 
List.<Object[]>of(new Object[]{1886L, 4492L}));
     QueriesTestUtils.testInterSegmentsResult(brokerResponse, 24516L, 252256L, 
49032L, 120000L, expectedResultTable,
@@ -358,6 +366,10 @@ public class InterSegmentAggregationSingleValueQueriesTest 
extends BaseSingleVal
     QueriesTestUtils.testInterSegmentsResult(brokerResponse, 120000L, 0L, 0L, 
120000L, expectedResultTable,
         cardinalityExtractor);
 
+    brokerResponse = getBrokerResponse(query, SERVER_RETURN_FINAL_RESULT);
+    QueriesTestUtils.testInterSegmentsResult(brokerResponse, 120000L, 0L, 0L, 
120000L, expectedResultTable,
+        cardinalityExtractor);
+
     brokerResponse = getBrokerResponse(query + FILTER);
     expectedResultTable = new ResultTable(expectedDataSchema, 
List.<Object[]>of(new Object[]{1885L, 4545L}));
     QueriesTestUtils.testInterSegmentsResult(brokerResponse, 24516L, 252256L, 
49032L, 120000L, expectedResultTable,
@@ -585,6 +597,8 @@ public class InterSegmentAggregationSingleValueQueriesTest 
extends BaseSingleVal
             percentile, percentile);
     QueriesTestUtils.testInterSegmentsResult(getBrokerResponse(rawQuery), 
getBrokerResponse(regularQuery),
         quantileExtractor);
+    QueriesTestUtils.testInterSegmentsResult(getBrokerResponse(rawQuery, 
SERVER_RETURN_FINAL_RESULT),
+        getBrokerResponse(regularQuery), quantileExtractor);
     QueriesTestUtils.testInterSegmentsResult(getBrokerResponse(rawQuery + 
FILTER),
         getBrokerResponse(regularQuery + FILTER), quantileExtractor);
     QueriesTestUtils.testInterSegmentsResult(getBrokerResponse(rawQuery + 
GROUP_BY),
@@ -619,6 +633,8 @@ public class InterSegmentAggregationSingleValueQueriesTest 
extends BaseSingleVal
             percentile, percentile);
     QueriesTestUtils.testInterSegmentsResult(getBrokerResponse(rawQuery), 
getBrokerResponse(regularQuery),
         quantileExtractor, PERCENTILE_TDIGEST_DELTA);
+    QueriesTestUtils.testInterSegmentsResult(getBrokerResponse(rawQuery, 
SERVER_RETURN_FINAL_RESULT),
+        getBrokerResponse(regularQuery), quantileExtractor, 
PERCENTILE_TDIGEST_DELTA);
     QueriesTestUtils.testInterSegmentsResult(getBrokerResponse(rawQuery + 
FILTER),
         getBrokerResponse(regularQuery + FILTER), quantileExtractor, 
PERCENTILE_TDIGEST_DELTA);
     QueriesTestUtils.testInterSegmentsResult(getBrokerResponse(rawQuery + 
GROUP_BY),


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to