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 1c78e5d47ff Give the integer tuple sketch functions the query's null
handling option (#19217)
1c78e5d47ff is described below
commit 1c78e5d47ffd708bf2da187492e3272a1bd0225e
Author: Xiaotian (Jackie) Jiang <[email protected]>
AuthorDate: Wed Aug 12 09:30:02 2026 -0700
Give the integer tuple sketch functions the query's null handling option
(#19217)
---
.../aggregation/function/AggregationFunction.java | 5 +-
.../function/AggregationFunctionFactory.java | 11 +-
...ValueIntegerTupleSketchAggregationFunction.java | 8 +-
...CountIntegerTupleSketchAggregationFunction.java | 5 +-
.../IntegerTupleSketchAggregationFunction.java | 81 +++++++------
...aluesIntegerTupleSketchAggregationFunction.java | 10 +-
.../IntegerTupleSketchAggregationFunctionTest.java | 4 +-
.../IntegerTupleSketchNullHandlingTest.java | 132 +++++++++++++++++++++
8 files changed, 204 insertions(+), 52 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 7569aa03967..45208aa6e02 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
@@ -100,9 +100,8 @@ import org.apache.pinot.segment.spi.AggregationFunctionType;
/// null rows and fold the column's default value into the aggregate
whatever the query asked for: the
/// sketch-backed distinct counts (`DISTINCTCOUNTBITMAP`,
`DISTINCTCOUNTHLL`, `DISTINCTCOUNTTHETASKETCH`,
/// `DISTINCTCOUNTCPCSKETCH`, `FASTHLL`,
`SEGMENTPARTITIONEDDISTINCTCOUNT` and the raw and smart variants of
-/// each), the tuple and frequency sketches, `HISTOGRAM`, `IDSET`,
`STUNION`, the array sums, and the funnel
-/// family. Whether a function takes the option is visible at its
construction site, which is the reliable way
-/// to tell.
+/// each), the frequency sketches, `HISTOGRAM`, `IDSET`, `STUNION`, the
array sums, and the funnel family.
+/// Whether a function takes the option is visible at its construction
site, which is the reliable way to tell.
/// The family names are not a safe shorthand for this, in either
direction. The exact distinct functions
/// (`DISTINCTCOUNT`, `DISTINCTSUM`, `DISTINCTAVG`,
`DISTINCTCOUNTOFFHEAP`) do take the option and skip null
/// rows through their shared base, as do the variance,
standard-deviation and covariance functions and the
diff --git
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/AggregationFunctionFactory.java
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/AggregationFunctionFactory.java
index 5616244d770..cc6efe8a17d 100644
---
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/AggregationFunctionFactory.java
+++
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/AggregationFunctionFactory.java
@@ -481,13 +481,16 @@ public class AggregationFunctionFactory {
return new FourthMomentAggregationFunction(arguments,
FourthMomentAggregationFunction.Type.MOMENT);
case DISTINCTCOUNTTUPLESKETCH:
// mode actually doesn't matter here because we only care about
keys, not values
- return new
DistinctCountIntegerTupleSketchAggregationFunction(arguments,
IntegerSummary.Mode.Sum);
+ return new
DistinctCountIntegerTupleSketchAggregationFunction(arguments,
IntegerSummary.Mode.Sum,
+ nullHandlingEnabled);
case DISTINCTCOUNTRAWINTEGERSUMTUPLESKETCH:
- return new IntegerTupleSketchAggregationFunction(arguments,
IntegerSummary.Mode.Sum);
+ return new IntegerTupleSketchAggregationFunction(arguments,
IntegerSummary.Mode.Sum, nullHandlingEnabled);
case SUMVALUESINTEGERSUMTUPLESKETCH:
- return new
SumValuesIntegerTupleSketchAggregationFunction(arguments,
IntegerSummary.Mode.Sum);
+ return new
SumValuesIntegerTupleSketchAggregationFunction(arguments,
IntegerSummary.Mode.Sum,
+ nullHandlingEnabled);
case AVGVALUEINTEGERSUMTUPLESKETCH:
- return new
AvgValueIntegerTupleSketchAggregationFunction(arguments,
IntegerSummary.Mode.Sum);
+ return new
AvgValueIntegerTupleSketchAggregationFunction(arguments,
IntegerSummary.Mode.Sum,
+ nullHandlingEnabled);
case PINOTPARENTAGGEXPRMAX:
return new ParentExprMinMaxAggregationFunction(arguments, true);
case PINOTPARENTAGGEXPRMIN:
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 fba88d643cf..f279a157aae 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
@@ -32,8 +32,9 @@ import org.apache.pinot.segment.spi.AggregationFunctionType;
public class AvgValueIntegerTupleSketchAggregationFunction
extends IntegerTupleSketchAggregationFunction {
- public AvgValueIntegerTupleSketchAggregationFunction(List<ExpressionContext>
arguments, IntegerSummary.Mode mode) {
- super(arguments, mode);
+ public AvgValueIntegerTupleSketchAggregationFunction(List<ExpressionContext>
arguments, IntegerSummary.Mode mode,
+ boolean nullHandlingEnabled) {
+ super(arguments, mode, nullHandlingEnabled);
}
// TODO if extra aggregation modes are supported, make this switch
@@ -51,7 +52,8 @@ public class AvgValueIntegerTupleSketchAggregationFunction
@Nullable
@Override
public Comparable extractFinalResult(@Nullable TupleIntSketchAccumulator
accumulator) {
- // A null intermediate result means nothing was aggregated, and there is
nothing to average
+ // A null intermediate result means nothing was aggregated, and so does an
empty sketch, which the retained-entry
+ // check below already answers NULL for. The average of nothing is NULL in
either mode.
if (accumulator == null) {
return null;
}
diff --git
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/DistinctCountIntegerTupleSketchAggregationFunction.java
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/DistinctCountIntegerTupleSketchAggregationFunction.java
index c65cd9ed7fa..4e071706eb4 100644
---
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/DistinctCountIntegerTupleSketchAggregationFunction.java
+++
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/DistinctCountIntegerTupleSketchAggregationFunction.java
@@ -30,8 +30,8 @@ import org.apache.pinot.segment.spi.AggregationFunctionType;
public class DistinctCountIntegerTupleSketchAggregationFunction extends
IntegerTupleSketchAggregationFunction {
public
DistinctCountIntegerTupleSketchAggregationFunction(List<ExpressionContext>
arguments,
- IntegerSummary.Mode mode) {
- super(arguments, mode);
+ IntegerSummary.Mode mode, boolean nullHandlingEnabled) {
+ super(arguments, mode, nullHandlingEnabled);
}
// TODO if extra aggregation modes are supported, make this switch
@@ -47,6 +47,7 @@ public class
DistinctCountIntegerTupleSketchAggregationFunction extends IntegerT
@Override
public Comparable extractFinalResult(@Nullable TupleIntSketchAccumulator
accumulator) {
+ // Nothing aggregated: an empty sketch estimates to 0 in either mode, so
the counting answer is the same
if (accumulator == null) {
return 0L;
}
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 83ae908cfa8..7591272c0d5 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
@@ -96,15 +96,16 @@ import org.apache.pinot.spi.utils.CommonConstants;
/// )
@SuppressWarnings({"rawtypes"})
public class IntegerTupleSketchAggregationFunction
- extends BaseSingleInputAggregationFunction<TupleIntSketchAccumulator,
Comparable> {
+ extends NullableSingleInputAggregationFunction<TupleIntSketchAccumulator,
Comparable> {
private static final int DEFAULT_ACCUMULATOR_THRESHOLD = 2;
final ExpressionContext _expressionContext;
final IntegerSummarySetOperations _setOps;
protected int _accumulatorThreshold = DEFAULT_ACCUMULATOR_THRESHOLD;
protected int _nominalEntries;
- public IntegerTupleSketchAggregationFunction(List<ExpressionContext>
arguments, IntegerSummary.Mode mode) {
- super(arguments.get(0));
+ public IntegerTupleSketchAggregationFunction(List<ExpressionContext>
arguments, IntegerSummary.Mode mode,
+ boolean nullHandlingEnabled) {
+ super(arguments.get(0), nullHandlingEnabled);
Preconditions.checkArgument(arguments.size() <= 2,
"Tuple Sketch Aggregation Function expects at most 2 arguments, got:
%s", arguments.size());
@@ -156,11 +157,19 @@ public class IntegerTupleSketchAggregationFunction
if (storedType == FieldSpec.DataType.BYTES) {
byte[][] bytesValues = blockValSet.getBytesValuesSV();
try {
- TupleIntSketchAccumulator tupleIntSketchAccumulator =
getAccumulator(aggregationResultHolder);
- TupleSketch<IntegerSummary>[] sketches =
deserializeSketches(bytesValues, length);
- for (TupleSketch<IntegerSummary> sketch : sketches) {
- tupleIntSketchAccumulator.apply(sketch);
- }
+ // The accumulator is created inside the range, so an all-null block
leaves the holder untouched and
+ // extractFinalResult sees the null that means nothing was aggregated
+ forEachNotNull(length, blockValSet, (from, to) -> {
+ // An empty range still reaches here, for a zero-length block.
Creating the accumulator for it would mark
+ // the holder as aggregated and lose the signal this whole
arrangement exists to carry.
+ if (to == from) {
+ return;
+ }
+ TupleIntSketchAccumulator tupleIntSketchAccumulator =
getAccumulator(aggregationResultHolder);
+ for (int i = from; i < to; i++) {
+ tupleIntSketchAccumulator.apply(deserializeSketch(bytesValues[i]));
+ }
+ });
} catch (Exception e) {
throw new RuntimeException("Caught exception while aggregating Tuple
Sketches", e);
}
@@ -181,12 +190,11 @@ public class IntegerTupleSketchAggregationFunction
if (storedType == FieldSpec.DataType.BYTES) {
byte[][] bytesValues = blockValSet.getBytesValuesSV();
try {
- TupleSketch<IntegerSummary>[] sketches =
deserializeSketches(bytesValues, length);
- for (int i = 0; i < length; i++) {
- TupleIntSketchAccumulator tupleIntSketchAccumulator =
getAccumulator(groupByResultHolder, groupKeyArray[i]);
- TupleSketch<IntegerSummary> sketch = sketches[i];
- tupleIntSketchAccumulator.apply(sketch);
- }
+ forEachNotNull(length, blockValSet, (from, to) -> {
+ for (int i = from; i < to; i++) {
+ getAccumulator(groupByResultHolder,
groupKeyArray[i]).apply(deserializeSketch(bytesValues[i]));
+ }
+ });
} catch (Exception e) {
throw new RuntimeException("Caught exception while aggregating Tuple
Sketches", e);
}
@@ -208,12 +216,15 @@ public class IntegerTupleSketchAggregationFunction
if (singleValue && storedType == FieldSpec.DataType.BYTES) {
byte[][] bytesValues =
blockValSetMap.get(_expression).getBytesValuesSV();
try {
- TupleSketch<IntegerSummary>[] sketches =
deserializeSketches(bytesValues, length);
- for (int i = 0; i < length; i++) {
- for (int groupKey : groupKeysArray[i]) {
- getAccumulator(groupByResultHolder, groupKey).apply(sketches[i]);
+ forEachNotNull(length, blockValSet, (from, to) -> {
+ for (int i = from; i < to; i++) {
+ // Deserialized once per row, not once per group key the row
belongs to
+ TupleSketch<IntegerSummary> sketch =
deserializeSketch(bytesValues[i]);
+ for (int groupKey : groupKeysArray[i]) {
+ getAccumulator(groupByResultHolder, groupKey).apply(sketch);
+ }
}
- }
+ });
} catch (Exception e) {
throw new RuntimeException("Caught exception while aggregating Tuple
Sketches", e);
}
@@ -224,12 +235,15 @@ public class IntegerTupleSketchAggregationFunction
}
@Override
+ @Nullable
public TupleIntSketchAccumulator
extractAggregationResult(AggregationResultHolder aggregationResultHolder) {
- TupleIntSketchAccumulator result = aggregationResultHolder.getResult();
- if (result == null) {
- return new TupleIntSketchAccumulator(_setOps, _nominalEntries,
_accumulatorThreshold);
- }
- return result;
+ return aggregationResultHolder.getResult();
+ }
+
+ /// The accumulator an untouched holder stands for, built where the
disabled-mode answer is rendered rather than
+ /// substituted during extraction.
+ TupleIntSketchAccumulator emptyAccumulator() {
+ return new TupleIntSketchAccumulator(_setOps, _nominalEntries,
_accumulatorThreshold);
}
@Nullable
@@ -278,8 +292,13 @@ public class IntegerTupleSketchAggregationFunction
@Nullable
@Override
public Comparable extractFinalResult(@Nullable TupleIntSketchAccumulator
accumulator) {
+ // A null intermediate result means nothing was aggregated. With null
handling enabled that is NULL; with it
+ // disabled the answer stays what it has always been, the serialized empty
sketch.
if (accumulator == null) {
- return null;
+ if (_nullHandlingEnabled) {
+ return null;
+ }
+ accumulator = emptyAccumulator();
}
accumulator.setNominalEntries(_nominalEntries);
accumulator.setSetOperations(_setOps);
@@ -327,15 +346,9 @@ public class IntegerTupleSketchAggregationFunction
return accumulator;
}
- /// Deserializes the sketches from the bytes.
- @SuppressWarnings({"unchecked"})
- private TupleSketch<IntegerSummary>[] deserializeSketches(byte[][]
serializedSketches, int length) {
- TupleSketch<IntegerSummary>[] sketches = new TupleSketch[length];
- for (int i = 0; i < length; i++) {
- sketches[i] =
-
TupleSketch.heapifySketch(MemorySegment.ofArray(serializedSketches[i]), new
IntegerSummaryDeserializer());
- }
- return sketches;
+ /// Deserializes a single serialized sketch, so a row that is skipped as
null is never heapified.
+ private TupleSketch<IntegerSummary> deserializeSketch(byte[]
serializedSketch) {
+ return TupleSketch.heapifySketch(MemorySegment.ofArray(serializedSketch),
new IntegerSummaryDeserializer());
}
/// Helper class to wrap the tuple-sketch parameters. The initial values
for the parameters are set to the
diff --git
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/SumValuesIntegerTupleSketchAggregationFunction.java
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/SumValuesIntegerTupleSketchAggregationFunction.java
index fb819290df5..d3b8c1bed8c 100644
---
a/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/SumValuesIntegerTupleSketchAggregationFunction.java
+++
b/pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/SumValuesIntegerTupleSketchAggregationFunction.java
@@ -31,8 +31,9 @@ import org.apache.pinot.segment.spi.AggregationFunctionType;
public class SumValuesIntegerTupleSketchAggregationFunction extends
IntegerTupleSketchAggregationFunction {
- public
SumValuesIntegerTupleSketchAggregationFunction(List<ExpressionContext>
arguments, IntegerSummary.Mode mode) {
- super(arguments, mode);
+ public
SumValuesIntegerTupleSketchAggregationFunction(List<ExpressionContext>
arguments, IntegerSummary.Mode mode,
+ boolean nullHandlingEnabled) {
+ super(arguments, mode, nullHandlingEnabled);
}
// TODO if extra aggregation modes are supported, make this switch
@@ -49,9 +50,10 @@ public class SumValuesIntegerTupleSketchAggregationFunction
extends IntegerTuple
@Nullable
@Override
public Comparable extractFinalResult(@Nullable TupleIntSketchAccumulator
accumulator) {
- // A null intermediate result means nothing was aggregated, and there is
nothing to sum
+ // A null intermediate result means nothing was aggregated. With null
handling enabled there is nothing to sum,
+ // so the answer is NULL; with it disabled it stays what an empty sketch
summed to, which is zero.
if (accumulator == null) {
- return null;
+ return _nullHandlingEnabled ? null : 0L;
}
double retainedTotal = 0L;
accumulator.setNominalEntries(_nominalEntries);
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 d486bf620e4..20c1eb7d836 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
@@ -34,7 +34,7 @@ public class IntegerTupleSketchAggregationFunctionTest {
public void testCanUseStarTreeDefaultK() {
IntegerTupleSketchAggregationFunction function =
new
IntegerTupleSketchAggregationFunction(List.of(ExpressionContext.forIdentifier("col")),
- IntegerSummary.Mode.Sum);
+ IntegerSummary.Mode.Sum, false);
Assert.assertTrue(function.canUseStarTree(Map.of()));
Assert.assertTrue(function.canUseStarTree(Map.of(Constants.THETA_TUPLE_SKETCH_NOMINAL_ENTRIES,
"16384")));
@@ -46,7 +46,7 @@ public class IntegerTupleSketchAggregationFunctionTest {
public void testCanUseCustomK() {
IntegerTupleSketchAggregationFunction function = new
IntegerTupleSketchAggregationFunction(
List.of(ExpressionContext.forIdentifier("col"),
ExpressionContext.forLiteral(Literal.intValue(32768))),
- IntegerSummary.Mode.Sum);
+ IntegerSummary.Mode.Sum, false);
// Default StarTree lgK = 14 / K=16384
Assert.assertFalse(function.canUseStarTree(Map.of()));
diff --git
a/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/IntegerTupleSketchNullHandlingTest.java
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/IntegerTupleSketchNullHandlingTest.java
new file mode 100644
index 00000000000..bbb227b272e
--- /dev/null
+++
b/pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/IntegerTupleSketchNullHandlingTest.java
@@ -0,0 +1,132 @@
+/**
+ * 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.util.List;
+import java.util.Map;
+import org.apache.datasketches.tuple.aninteger.IntegerSummary;
+import org.apache.datasketches.tuple.aninteger.IntegerTupleSketch;
+import org.apache.pinot.common.request.context.ExpressionContext;
+import org.apache.pinot.core.common.BlockValSet;
+import org.apache.pinot.core.common.ObjectSerDeUtils;
+import org.apache.pinot.core.common.SyntheticBlockValSets;
+import org.apache.pinot.core.query.aggregation.AggregationResultHolder;
+import org.apache.pinot.segment.local.customobject.TupleIntSketchAccumulator;
+import org.roaringbitmap.RoaringBitmap;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+
+
+/// Null handling for the integer tuple sketch family, whose input is always a
column of serialized sketches.
+///
+/// [AggregationFunctionNullContractTest] cannot reach these functions: its
synthetic `BYTES` block supplies empty
+/// byte arrays, which are not deserializable sketches. They are pinned in its
skip list, so this is the only place
+/// their null behaviour is checked.
+public class IntegerTupleSketchNullHandlingTest {
+ private static final ExpressionContext COLUMN =
ExpressionContext.forIdentifier("column");
+ private static final int NUM_DOCS = 4;
+
+ private static byte[][] serializedSketches() {
+ byte[][] values = new byte[NUM_DOCS][];
+ for (int i = 0; i < NUM_DOCS; i++) {
+ IntegerTupleSketch sketch = new IntegerTupleSketch(4,
IntegerSummary.Mode.Sum);
+ sketch.update(i, 1);
+ values[i] =
ObjectSerDeUtils.DATA_SKETCH_INT_TUPLE_SER_DE.serialize(sketch.compact());
+ }
+ return values;
+ }
+
+ private static Map<ExpressionContext, BlockValSet> block(RoaringBitmap
nullBitmap) {
+ return Map.of(COLUMN, SyntheticBlockValSets.Bytes.create(nullBitmap,
serializedSketches()));
+ }
+
+ private static RoaringBitmap allNull() {
+ RoaringBitmap bitmap = new RoaringBitmap();
+ bitmap.add(0L, NUM_DOCS);
+ return bitmap;
+ }
+
+ private static TupleIntSketchAccumulator
aggregate(IntegerTupleSketchAggregationFunction function,
+ RoaringBitmap nullBitmap) {
+ AggregationResultHolder resultHolder =
function.createAggregationResultHolder();
+ function.aggregate(NUM_DOCS, resultHolder, block(nullBitmap));
+ return function.extractAggregationResult(resultHolder);
+ }
+
+ private static IntegerTupleSketchAggregationFunction raw(boolean
nullHandlingEnabled) {
+ return new IntegerTupleSketchAggregationFunction(List.of(COLUMN),
IntegerSummary.Mode.Sum, nullHandlingEnabled);
+ }
+
+ /// Only the rows that carry a sketch are deserialized and unioned.
+ @Test
+ public void testNullRowsAreSkipped() {
+ TupleIntSketchAccumulator accumulator = aggregate(raw(true),
RoaringBitmap.bitmapOf(1, 3));
+
+ assertNotNull(accumulator);
+ assertEquals(accumulator.getResult().getRetainedEntries(), 2);
+ }
+
+ /// Nothing aggregated leaves the holder untouched, which is how the state
reaches `extractFinalResult`.
+ @Test
+ public void testEveryRowNullYieldsNoIntermediateResult() {
+ assertNull(aggregate(raw(true), allNull()));
+ }
+
+ /// A zero-length block still reaches the range callback, and must not mark
the holder as aggregated.
+ @Test
+ public void testZeroLengthBlockLeavesTheHolderUntouched() {
+ IntegerTupleSketchAggregationFunction function = raw(true);
+ AggregationResultHolder resultHolder =
function.createAggregationResultHolder();
+ function.aggregate(0, resultHolder, block(null));
+
+ assertNull(function.extractAggregationResult(resultHolder));
+ }
+
+ /// With the option enabled each function gives its own answer for an empty
input.
+ @Test
+ public void testEmptyInputAnswersPerFunctionWhenEnabled() {
+ assertNull(raw(true).extractFinalResult(null));
+ assertEquals(new
DistinctCountIntegerTupleSketchAggregationFunction(List.of(COLUMN),
IntegerSummary.Mode.Sum, true)
+ .extractFinalResult(null), 0L);
+ assertNull(new
SumValuesIntegerTupleSketchAggregationFunction(List.of(COLUMN),
IntegerSummary.Mode.Sum, true)
+ .extractFinalResult(null));
+ assertNull(new
AvgValueIntegerTupleSketchAggregationFunction(List.of(COLUMN),
IntegerSummary.Mode.Sum, true)
+ .extractFinalResult(null));
+ }
+
+ /// With the option disabled each function renders what its empty
accumulator has always rendered: the raw variant
+ /// a serialized empty sketch, the counting and summing variants zero, the
average `NULL` for want of entries.
+ @Test
+ public void testEmptyInputRendersTheIdentityWhenDisabled() {
+ IntegerTupleSketchAggregationFunction rawFunction = raw(false);
+ Comparable<?> rendered = rawFunction.extractFinalResult(null);
+ assertNotNull(rendered);
+ assertEquals(rendered,
rawFunction.extractFinalResult(rawFunction.emptyAccumulator()));
+
+ assertEquals(new
DistinctCountIntegerTupleSketchAggregationFunction(List.of(COLUMN),
IntegerSummary.Mode.Sum,
+ false).extractFinalResult(null), 0L);
+ assertEquals(new
SumValuesIntegerTupleSketchAggregationFunction(List.of(COLUMN),
IntegerSummary.Mode.Sum, false)
+ .extractFinalResult(null), 0L);
+ assertNull(new
AvgValueIntegerTupleSketchAggregationFunction(List.of(COLUMN),
IntegerSummary.Mode.Sum, false)
+ .extractFinalResult(null));
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]