yashmayya commented on code in PR #18334:
URL: https://github.com/apache/pinot/pull/18334#discussion_r3598646315


##########
pinot-core/src/main/java/org/apache/pinot/core/plan/AggregationPlanNode.java:
##########
@@ -115,17 +115,38 @@ public Operator<AggregationResultsBlock> 
buildNonFilteredAggOperator() {
 
     boolean hasNullValues = _queryContext.isNullHandlingEnabled() && 
hasNullValues(aggregationFunctions);
     if (!hasNullValues) {
+      DataSource[] dataSources = new DataSource[aggregationFunctions.length];
+      for (int i = 0; i < aggregationFunctions.length; i++) {
+        List<?> inputExpressions = 
aggregationFunctions[i].getInputExpressions();
+        if (!inputExpressions.isEmpty()) {
+          String column = ((ExpressionContext) 
inputExpressions.get(0)).getIdentifier();
+          dataSources[i] = _indexSegment.getDataSource(column, 
_queryContext.getSchema());
+        }
+      }
+
       // Priority 2: Check if non-scan based aggregation is feasible
       if (filterOperator.isResultMatchingAll() && isFitForNonScanBasedPlan()) {
-        DataSource[] dataSources = new DataSource[aggregationFunctions.length];
+        return new NonScanBasedAggregationOperator(_queryContext, dataSources, 
numTotalDocs);
+      }
+
+      if (filterOperator.isResultMatchingAll()) {
+        boolean anyResolved = false;
+        Object[] preAggregatedResults = new 
Object[aggregationFunctions.length];
         for (int i = 0; i < aggregationFunctions.length; i++) {
-          List<?> inputExpressions = 
aggregationFunctions[i].getInputExpressions();
-          if (!inputExpressions.isEmpty()) {
-            String column = ((ExpressionContext) 
inputExpressions.get(0)).getIdentifier();
-            dataSources[i] = _indexSegment.getDataSource(column, 
_queryContext.getSchema());
+          Object resolved = 
AggregationFunctionUtils.getAggregationResult(aggregationFunctions[i],

Review Comment:
   `getAggregationResult()` never returns null - for non-metadata functions it 
throws (the `default` in the util), and for `DISTINCT*` without a dictionary it 
NPEs on `requireNonNull(getDictionary())`. So `resolved != null` never actually 
skips anything.
   
   And this branch is only reached when `isFitForNonScanBasedPlan()` is false, 
i.e. at least one function *isn't* metadata-resolvable - so the loop is 
guaranteed to hit one of those throws. Net effect: `SELECT SUM(col) FROM t` (no 
filter) throws here instead of falling through to the scan path.
   
   I think you need a per-function feasibility check before resolving - the 
checks already in `isFitForNonScanBasedPlan()` are exactly that - and only 
resolve the ones that pass, leaving the rest null.



##########
pinot-core/src/main/java/org/apache/pinot/core/plan/AggregationPlanNode.java:
##########
@@ -115,17 +115,38 @@ public Operator<AggregationResultsBlock> 
buildNonFilteredAggOperator() {
 
     boolean hasNullValues = _queryContext.isNullHandlingEnabled() && 
hasNullValues(aggregationFunctions);
     if (!hasNullValues) {
+      DataSource[] dataSources = new DataSource[aggregationFunctions.length];
+      for (int i = 0; i < aggregationFunctions.length; i++) {
+        List<?> inputExpressions = 
aggregationFunctions[i].getInputExpressions();
+        if (!inputExpressions.isEmpty()) {
+          String column = ((ExpressionContext) 
inputExpressions.get(0)).getIdentifier();
+          dataSources[i] = _indexSegment.getDataSource(column, 
_queryContext.getSchema());
+        }
+      }
+
       // Priority 2: Check if non-scan based aggregation is feasible
       if (filterOperator.isResultMatchingAll() && isFitForNonScanBasedPlan()) {
-        DataSource[] dataSources = new DataSource[aggregationFunctions.length];
+        return new NonScanBasedAggregationOperator(_queryContext, dataSources, 
numTotalDocs);
+      }
+
+      if (filterOperator.isResultMatchingAll()) {
+        boolean anyResolved = false;
+        Object[] preAggregatedResults = new 
Object[aggregationFunctions.length];
         for (int i = 0; i < aggregationFunctions.length; i++) {
-          List<?> inputExpressions = 
aggregationFunctions[i].getInputExpressions();
-          if (!inputExpressions.isEmpty()) {
-            String column = ((ExpressionContext) 
inputExpressions.get(0)).getIdentifier();
-            dataSources[i] = _indexSegment.getDataSource(column, 
_queryContext.getSchema());
+          Object resolved = 
AggregationFunctionUtils.getAggregationResult(aggregationFunctions[i],
+              dataSources[i], numTotalDocs, 
NonScanBasedAggregationOperator.EXPLAIN_NAME);
+          if (resolved != null) {
+            preAggregatedResults[i] = resolved;
+            anyResolved = true;
           }
         }
-        return new NonScanBasedAggregationOperator(_queryContext, dataSources, 
numTotalDocs);
+
+        if (anyResolved) {
+          // build aggregation info for all functions (including those that 
cannot be resolved from metadata)
+          aggregationInfo = 
AggregationFunctionUtils.buildAggregationInfoWithoutStarTree(_segmentContext, 
_queryContext,

Review Comment:
   Couple of things on the resulting operator:
   1. `buildAggregationInfoWithoutStarTree` collects every function's columns, 
so the metadata-resolved columns still get projected/scanned - we only skip the 
per-row `aggregate()`, not the I/O. So the win is narrower than "avoid the 
scan".
   2. Resolution runs here at plan-build time (dictionary scans, HLL merges), 
whereas the full non-scan path does it lazily in `getNextBlock()`. Might be 
worth deferring to keep planning cheap and the two paths consistent.



##########
pinot-core/src/main/java/org/apache/pinot/core/plan/AggregationPlanNode.java:
##########
@@ -115,17 +115,38 @@ public Operator<AggregationResultsBlock> 
buildNonFilteredAggOperator() {
 
     boolean hasNullValues = _queryContext.isNullHandlingEnabled() && 
hasNullValues(aggregationFunctions);
     if (!hasNullValues) {
+      DataSource[] dataSources = new DataSource[aggregationFunctions.length];
+      for (int i = 0; i < aggregationFunctions.length; i++) {
+        List<?> inputExpressions = 
aggregationFunctions[i].getInputExpressions();
+        if (!inputExpressions.isEmpty()) {
+          String column = ((ExpressionContext) 
inputExpressions.get(0)).getIdentifier();
+          dataSources[i] = _indexSegment.getDataSource(column, 
_queryContext.getSchema());

Review Comment:
   This loop used to live inside the `isFitForNonScanBasedPlan()` block, which 
guaranteed every arg was a plain identifier. Now it runs for every query, but 
`getIdentifier()` returns null for non-identifier args and `getDataSource(null, 
schema)` throws (`checkState(fieldSpec != null, ...)`).
   
   So `SELECT SUM(a+b) FROM t` (any agg over an expression) now fails at plan 
time - and since this is above the `isResultMatchingAll()` checks, even 
filtered queries hit it. Worth guarding on `getType() == IDENTIFIER`.



##########
pinot-core/src/main/java/org/apache/pinot/core/plan/AggregationPlanNode.java:
##########
@@ -115,17 +115,38 @@ public Operator<AggregationResultsBlock> 
buildNonFilteredAggOperator() {
 
     boolean hasNullValues = _queryContext.isNullHandlingEnabled() && 
hasNullValues(aggregationFunctions);
     if (!hasNullValues) {
+      DataSource[] dataSources = new DataSource[aggregationFunctions.length];
+      for (int i = 0; i < aggregationFunctions.length; i++) {
+        List<?> inputExpressions = 
aggregationFunctions[i].getInputExpressions();
+        if (!inputExpressions.isEmpty()) {
+          String column = ((ExpressionContext) 
inputExpressions.get(0)).getIdentifier();
+          dataSources[i] = _indexSegment.getDataSource(column, 
_queryContext.getSchema());
+        }
+      }
+
       // Priority 2: Check if non-scan based aggregation is feasible
       if (filterOperator.isResultMatchingAll() && isFitForNonScanBasedPlan()) {
-        DataSource[] dataSources = new DataSource[aggregationFunctions.length];
+        return new NonScanBasedAggregationOperator(_queryContext, dataSources, 
numTotalDocs);
+      }
+
+      if (filterOperator.isResultMatchingAll()) {
+        boolean anyResolved = false;
+        Object[] preAggregatedResults = new 
Object[aggregationFunctions.length];
         for (int i = 0; i < aggregationFunctions.length; i++) {
-          List<?> inputExpressions = 
aggregationFunctions[i].getInputExpressions();
-          if (!inputExpressions.isEmpty()) {
-            String column = ((ExpressionContext) 
inputExpressions.get(0)).getIdentifier();
-            dataSources[i] = _indexSegment.getDataSource(column, 
_queryContext.getSchema());
+          Object resolved = 
AggregationFunctionUtils.getAggregationResult(aggregationFunctions[i],
+              dataSources[i], numTotalDocs, 
NonScanBasedAggregationOperator.EXPLAIN_NAME);

Review Comment:
   Minor: this tags the work as `AGGREGATE_NO_SCAN`, but the operator that 
actually runs here is `AggregationOperator` (`AGGREGATE`), so the usage 
sampling / termination checks get labeled with the wrong operator name. Also, 
making `EXPLAIN_NAME` public just to pass this constant is a bit of a smell - 
could pass the caller's own name or a neutral label.



##########
pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/AggregationFunctionUtils.java:
##########
@@ -501,4 +519,340 @@ public static String 
getResultColumnName(AggregationFunction aggregationFunction
     }
     return columnName;
   }
+
+  /**
+   * Gets the aggregation result without scanning the segment.
+   * This is used for non-scan based aggregation operator.
+   * @param aggregationFunction
+   * @param dataSource
+   * @param numTotalDocs
+   * @return
+   */
+  public static Object getAggregationResult(AggregationFunction 
aggregationFunction, DataSource dataSource,

Review Comment:
   For the partial path to work, this needs to return null when a function 
can't be resolved from metadata. Right now it either throws (`default`, L623) 
or NPEs (`requireNonNull(getDictionary())`, and the `MINSTRING`/`MAXSTRING` 
asserts) whenever the dictionary/metadata isn't present.
   
   Also the Javadoc `@param`/`@return` are empty and `explainName` isn't 
documented.



##########
pinot-core/src/main/java/org/apache/pinot/core/operator/query/AggregationOperator.java:
##########
@@ -67,7 +74,7 @@ protected AggregationResultsBlock getNextBlock() {
     if (_useStarTree) {
       aggregationExecutor = new 
StarTreeAggregationExecutor(_aggregationFunctions);
     } else {
-      aggregationExecutor = new 
DefaultAggregationExecutor(_aggregationFunctions);
+      aggregationExecutor = new 
DefaultAggregationExecutor(_aggregationFunctions, _preAggregatedResults);

Review Comment:
   `_preAggregatedResults` is only wired into `DefaultAggregationExecutor`; the 
star-tree branch above silently drops it. Safe today because the star-tree path 
returns earlier in the plan node, but it's fragile - and 
`StarTreeAggregationExecutor` overrides `aggregate()` but inherits 
`getResult()`, so if it were ever constructed with pre-aggregated results the 
two would disagree. An assert (`_preAggregatedResults == null` when star-tree) 
or a short comment would help.



##########
pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/AggregationFunctionUtils.java:
##########
@@ -501,4 +519,340 @@ public static String 
getResultColumnName(AggregationFunction aggregationFunction
     }
     return columnName;
   }
+
+  /**
+   * Gets the aggregation result without scanning the segment.
+   * This is used for non-scan based aggregation operator.
+   * @param aggregationFunction
+   * @param dataSource
+   * @param numTotalDocs
+   * @return
+   */
+  public static Object getAggregationResult(AggregationFunction 
aggregationFunction, DataSource dataSource,
+      int numTotalDocs, String explainName) {
+    Object result;
+    switch (aggregationFunction.getType()) {
+      case COUNT:
+        result = (long) numTotalDocs;
+        break;
+      case MIN:
+      case MINMV:
+        result = getMinValueNumeric(dataSource);
+        break;
+      case MINLONG:
+        result = getMinValueLong(dataSource);
+        break;
+      case MINSTRING:
+        assert dataSource.getDictionary() != null;
+        result = dataSource.getDictionary().getMinVal();
+        break;
+      case MAX:
+      case MAXMV:
+        result = getMaxValueNumeric(dataSource);
+        break;
+      case MAXLONG:
+        result = getMaxValueLong(dataSource);
+        break;
+      case MAXSTRING:
+        assert dataSource.getDictionary() != null;
+        result = dataSource.getDictionary().getMaxVal();
+        break;
+      case MINMAXRANGE:
+      case MINMAXRANGEMV:
+        result = new MinMaxRangePair(getMinValueNumeric(dataSource), 
getMaxValueNumeric(dataSource));
+        break;
+      case DISTINCTCOUNT:
+      case DISTINCTSUM:
+      case DISTINCTAVG:
+      case DISTINCTCOUNTMV:
+      case DISTINCTSUMMV:
+      case DISTINCTAVGMV:
+        result = 
getDistinctValueSet(Objects.requireNonNull(dataSource.getDictionary()), 
explainName);
+        break;
+      case DISTINCTCOUNTOFFHEAP:
+        result = ((DistinctCountOffHeapAggregationFunction) 
aggregationFunction).extractAggregationResult(
+            Objects.requireNonNull(dataSource.getDictionary()));
+        break;
+      case DISTINCTCOUNTHLL:
+      case DISTINCTCOUNTHLLMV:
+        result = 
getDistinctCountHLLResult(Objects.requireNonNull(dataSource.getDictionary()),
+            (DistinctCountHLLAggregationFunction) aggregationFunction, 
explainName);
+        break;
+      case DISTINCTCOUNTRAWHLL:
+      case DISTINCTCOUNTRAWHLLMV:
+        result = 
getDistinctCountHLLResult(Objects.requireNonNull(dataSource.getDictionary()),
+            ((DistinctCountRawHLLAggregationFunction) 
aggregationFunction).getDistinctCountHLLAggregationFunction(),
+            explainName);
+        break;
+      case DISTINCTCOUNTHLLPLUS:
+      case DISTINCTCOUNTHLLPLUSMV:
+        result = 
getDistinctCountHLLPlusResult(Objects.requireNonNull(dataSource.getDictionary()),
+            (DistinctCountHLLPlusAggregationFunction) aggregationFunction, 
explainName);
+        break;
+      case DISTINCTCOUNTRAWHLLPLUS:
+      case DISTINCTCOUNTRAWHLLPLUSMV:
+        result = 
getDistinctCountHLLPlusResult(Objects.requireNonNull(dataSource.getDictionary()),
+            ((DistinctCountRawHLLPlusAggregationFunction) aggregationFunction)
+                .getDistinctCountHLLPlusAggregationFunction(), explainName);
+        break;
+      case SEGMENTPARTITIONEDDISTINCTCOUNT:
+        result = (long) 
Objects.requireNonNull(dataSource.getDictionary()).length();
+        break;
+      case DISTINCTCOUNTSMARTHLL:
+        result = 
getDistinctCountSmartHLLResult(Objects.requireNonNull(dataSource.getDictionary()),
+            (DistinctCountSmartHLLAggregationFunction) aggregationFunction, 
explainName);
+        break;
+      case DISTINCTCOUNTSMARTHLLPLUS:
+        result = 
getDistinctCountSmartHLLPlusResult(Objects.requireNonNull(dataSource.getDictionary()),
+            (DistinctCountSmartHLLPlusAggregationFunction) 
aggregationFunction, explainName);
+        break;
+      case DISTINCTCOUNTULL:
+        result = 
getDistinctCountULLResult(Objects.requireNonNull(dataSource.getDictionary()),
+            (DistinctCountULLAggregationFunction) aggregationFunction, 
explainName);
+        break;
+      case DISTINCTCOUNTSMARTULL:
+        result = 
getDistinctCountSmartULLResult(Objects.requireNonNull(dataSource.getDictionary()),
+            (DistinctCountSmartULLAggregationFunction) aggregationFunction, 
explainName);
+        break;
+      case DISTINCTCOUNTRAWULL:
+        result = 
getDistinctCountULLResult(Objects.requireNonNull(dataSource.getDictionary()),
+            (DistinctCountULLAggregationFunction) aggregationFunction, 
explainName);
+        break;
+      default:
+        throw new IllegalStateException(
+            "Non-scan based aggregation operator does not support function 
type: " + aggregationFunction.getType());
+    }
+
+    return result;
+  }
+
+  private static Double getMinValueNumeric(DataSource dataSource) {
+    Dictionary dictionary = dataSource.getDictionary();
+    if (dictionary != null) {
+      return toDouble(dictionary.getMinVal());
+    }
+    return toDouble(dataSource.getDataSourceMetadata().getMinValue());
+  }
+
+  private static Long getMinValueLong(DataSource dataSource) {
+    FieldSpec.DataType dataType = 
dataSource.getDataSourceMetadata().getDataType().getStoredType();
+    Preconditions.checkArgument(
+        dataType == FieldSpec.DataType.LONG || dataType == 
FieldSpec.DataType.INT,
+        "MINLONG aggregation function can only be applied to columns of 
integer types");
+    Dictionary dictionary = dataSource.getDictionary();
+    if (dictionary != null) {
+      return ((Number) dictionary.getMinVal()).longValue();
+    }
+    return ((Number) 
dataSource.getDataSourceMetadata().getMinValue()).longValue();
+  }
+
+  private static Double getMaxValueNumeric(DataSource dataSource) {
+    Dictionary dictionary = dataSource.getDictionary();
+    if (dictionary != null) {
+      return toDouble(dictionary.getMaxVal());
+    }
+    return toDouble(dataSource.getDataSourceMetadata().getMaxValue());
+  }
+
+  private static Long getMaxValueLong(DataSource dataSource) {
+    FieldSpec.DataType dataType = 
dataSource.getDataSourceMetadata().getDataType().getStoredType();
+    Preconditions.checkArgument(
+        dataType == FieldSpec.DataType.LONG || dataType == 
FieldSpec.DataType.INT,
+        "MAXLONG aggregation function can only be applied to columns of 
integer types");
+    Dictionary dictionary = dataSource.getDictionary();
+    if (dictionary != null) {
+      return ((Number) dictionary.getMaxVal()).longValue();
+    }
+    return ((Number) 
dataSource.getDataSourceMetadata().getMaxValue()).longValue();
+  }
+
+  private static Double toDouble(Comparable<?> value) {
+    if (value instanceof Double) {
+      return (Double) value;
+    } else if (value instanceof Number) {
+      return ((Number) value).doubleValue();
+    } else {
+      return Double.parseDouble(value.toString());
+    }
+  }
+
+  private static Set getDistinctValueSet(Dictionary dictionary, String 
explainName) {
+    int dictionarySize = dictionary.length();
+    switch (dictionary.getValueType()) {
+      case INT:
+        IntOpenHashSet intSet = new IntOpenHashSet(dictionarySize);
+        for (int dictId = 0; dictId < dictionarySize; dictId++) {
+          
QueryThreadContext.checkTerminationAndSampleUsagePeriodically(dictId, 
explainName);
+          intSet.add(dictionary.getIntValue(dictId));
+        }
+        return intSet;
+      case LONG:
+        LongOpenHashSet longSet = new LongOpenHashSet(dictionarySize);
+        for (int dictId = 0; dictId < dictionarySize; dictId++) {
+          
QueryThreadContext.checkTerminationAndSampleUsagePeriodically(dictId, 
explainName);
+          longSet.add(dictionary.getLongValue(dictId));
+        }
+        return longSet;
+      case FLOAT:
+        FloatOpenHashSet floatSet = new FloatOpenHashSet(dictionarySize);
+        for (int dictId = 0; dictId < dictionarySize; dictId++) {
+          
QueryThreadContext.checkTerminationAndSampleUsagePeriodically(dictId, 
explainName);
+          floatSet.add(dictionary.getFloatValue(dictId));
+        }
+        return floatSet;
+      case DOUBLE:
+        DoubleOpenHashSet doubleSet = new DoubleOpenHashSet(dictionarySize);
+        for (int dictId = 0; dictId < dictionarySize; dictId++) {
+          
QueryThreadContext.checkTerminationAndSampleUsagePeriodically(dictId, 
explainName);
+          doubleSet.add(dictionary.getDoubleValue(dictId));
+        }
+        return doubleSet;
+      case BIG_DECIMAL:
+        ObjectOpenHashSet<BigDecimal> bigDecimalSet = new 
ObjectOpenHashSet<>(dictionarySize);
+        for (int dictId = 0; dictId < dictionarySize; dictId++) {
+          
QueryThreadContext.checkTerminationAndSampleUsagePeriodically(dictId, 
explainName);
+          bigDecimalSet.add(dictionary.getBigDecimalValue(dictId));
+        }
+        return bigDecimalSet;
+      case STRING:
+        ObjectOpenHashSet<String> stringSet = new 
ObjectOpenHashSet<>(dictionarySize);
+        for (int dictId = 0; dictId < dictionarySize; dictId++) {
+          
QueryThreadContext.checkTerminationAndSampleUsagePeriodically(dictId, 
explainName);
+          stringSet.add(dictionary.getStringValue(dictId));
+        }
+        return stringSet;
+      case BYTES:
+        ObjectOpenHashSet<ByteArray> bytesSet = new 
ObjectOpenHashSet<>(dictionarySize);
+        for (int dictId = 0; dictId < dictionarySize; dictId++) {
+          
QueryThreadContext.checkTerminationAndSampleUsagePeriodically(dictId, 
explainName);
+          bytesSet.add(new ByteArray(dictionary.getBytesValue(dictId)));
+        }
+        return bytesSet;
+      default:
+        throw new IllegalStateException();
+    }
+  }
+
+  private static HyperLogLog getDistinctValueHLL(Dictionary dictionary, int 
log2m, String explainName) {
+    HyperLogLog hll = new HyperLogLog(log2m);
+    int length = dictionary.length();
+    for (int i = 0; i < length; i++) {
+      QueryThreadContext.checkTerminationAndSampleUsagePeriodically(i, 
explainName);
+      hll.offer(dictionary.get(i));
+    }
+    return hll;
+  }
+
+  private static UltraLogLog getDistinctValueULL(Dictionary dictionary, int p, 
String explainName) {
+    UltraLogLog ull = UltraLogLog.create(p);
+    int length = dictionary.length();
+    for (int i = 0; i < length; i++) {
+      QueryThreadContext.checkTerminationAndSampleUsagePeriodically(i, 
explainName);
+      Object value = dictionary.get(i);
+      UltraLogLogUtils.hashObject(value).ifPresent(ull::add);
+    }
+    return ull;
+  }
+
+  private static HyperLogLogPlus getDistinctValueHLLPlus(Dictionary 
dictionary, int p, int sp, String explainName) {
+    HyperLogLogPlus hllPlus = new HyperLogLogPlus(p, sp);
+    int length = dictionary.length();
+    for (int i = 0; i < length; i++) {
+      QueryThreadContext.checkTerminationAndSampleUsagePeriodically(i, 
explainName);
+      hllPlus.offer(dictionary.get(i));
+    }
+    return hllPlus;
+  }
+
+  private static HyperLogLog getDistinctCountHLLResult(Dictionary dictionary,
+      DistinctCountHLLAggregationFunction function, String explainName) {
+    if (dictionary.getValueType() == FieldSpec.DataType.BYTES) {
+      // Treat BYTES value as serialized HyperLogLog
+      try {
+        QueryThreadContext.checkTerminationAndSampleUsage(explainName);
+        HyperLogLog hll = 
ObjectSerDeUtils.HYPER_LOG_LOG_SER_DE.deserialize(dictionary.getBytesValue(0));
+        int length = dictionary.length();
+        for (int i = 1; i < length; i++) {
+          QueryThreadContext.checkTerminationAndSampleUsagePeriodically(i, 
explainName);
+          
hll.addAll(ObjectSerDeUtils.HYPER_LOG_LOG_SER_DE.deserialize(dictionary.getBytesValue(i)));
+        }
+        return hll;
+      } catch (Exception e) {
+        throw new RuntimeException("Caught exception while merging 
HyperLogLogs", e);
+      }
+    } else {
+      return getDistinctValueHLL(dictionary, function.getLog2m(), explainName);
+    }
+  }
+
+  private static HyperLogLogPlus getDistinctCountHLLPlusResult(Dictionary 
dictionary,
+      DistinctCountHLLPlusAggregationFunction function, String explainName) {
+    if (dictionary.getValueType() == FieldSpec.DataType.BYTES) {
+      // Treat BYTES value as serialized HyperLogLogPlus
+      try {
+        QueryThreadContext.checkTerminationAndSampleUsage(explainName);
+        HyperLogLogPlus hllplus = 
ObjectSerDeUtils.HYPER_LOG_LOG_PLUS_SER_DE.deserialize(dictionary.getBytesValue(0));
+        int length = dictionary.length();
+        for (int i = 1; i < length; i++) {
+          QueryThreadContext.checkTerminationAndSampleUsagePeriodically(i, 
explainName);
+          
hllplus.addAll(ObjectSerDeUtils.HYPER_LOG_LOG_PLUS_SER_DE.deserialize(dictionary.getBytesValue(i)));
+        }
+        return hllplus;
+      } catch (Exception e) {
+        throw new RuntimeException("Caught exception while merging 
HyperLogLogPluses", e);
+      }
+    } else {
+      return getDistinctValueHLLPlus(dictionary, function.getP(), 
function.getSp(), explainName);
+    }
+  }
+
+  private static Object getDistinctCountSmartHLLResult(Dictionary dictionary,
+      DistinctCountSmartHLLAggregationFunction function, String 
explainPlanName) {

Review Comment:
   nit: `explainPlanName` here - every other method in this file calls it 
`explainName`.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to