xiangfu0 commented on code in PR #20063:
URL: https://github.com/apache/pinot/pull/20063#discussion_r4395222790


##########
pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/MultistageAggregationExecutor.java:
##########
@@ -84,19 +88,28 @@ public List<Object[]> getResult() {
       AggregationFunction aggFunction = _aggFunctions[i];
       Object value;
       switch (_aggType) {
-        case LEAF:
-          value = 
aggFunction.extractAggregationResult(_aggregateResultHolder[i]);
+        case LEAF: {
+          Object intermediateResult = 
aggFunction.extractAggregationResult(_aggregateResultHolder[i]);
+          value = _leafReturnFinalResult ? 
aggFunction.extractFinalResult(intermediateResult) : intermediateResult;
           break;
+        }
         case INTERMEDIATE:
           value = _mergeResultHolder[i];
           break;
-        case FINAL:
-          value = aggFunction.extractFinalResult(_mergeResultHolder[i]);
+        case FINAL: {
+          Object mergedResult = _mergeResultHolder[i];
+          // The merged result stays null when there is no input row, even if 
the leaf returns final results. In that
+          // case, extract the final result of the empty input, e.g. 0 for 
COUNT.
+          value = _leafReturnFinalResult && mergedResult != null
+              ? mergedResult

Review Comment:
   The null-guard here (extract final result of empty input, e.g. COUNT=0) is 
correctly the one difference from MultistageGroupByExecutor.getResultValue, 
where an existing group always has >=1 row so mergedResult is never null. Good 
call documenting it.



##########
pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/plan/server/ServerPlanRequestVisitor.java:
##########
@@ -75,24 +75,29 @@ public Void visitAggregate(AggregateNode node, 
ServerPlanRequestContext context)
       if (!groupByList.isEmpty()) {
         pinotQuery.setGroupByList(groupByList);
       }
-      /// GROUP BY GROUPING SETS / ROLLUP / CUBE: push the per-set row 
expansion down to the single-stage engine. The
-      /// per-set column-index lists are over groupByList (== 
node.getGroupKeys()), so they transfer directly. The
-      /// single-stage leaf expands each row across the sets and appends the 
synthetic $groupingId ordinal column; the
-      /// multi-stage final stage then groups on it (it is one of the group 
keys), keeping the grouping sets distinct.
+      // GROUP BY GROUPING SETS / ROLLUP / CUBE: push the per-set row 
expansion down to the single-stage engine. The
+      // per-set column-index lists are over groupByList (== 
node.getGroupKeys()), so they transfer directly. The
+      // single-stage leaf expands each row across the sets and appends the 
synthetic $groupingId ordinal column; the
+      // multi-stage final stage then groups on it (it is one of the group 
keys), keeping the grouping sets distinct.
       if (node.isGroupingSets()) {
         pinotQuery.setGroupingSets(node.getGroupingSets());
       }
-      List<Expression> selectList = 
CalciteRexExpressionParser.convertAggregateList(groupByList, node.getAggCalls(),
-          node.getFilterArgs(), pinotQuery.getSelectList());
+      List<Expression> selectList =
+          CalciteRexExpressionParser.convertAggregateList(groupByList, 
node.getAggCalls(), node.getFilterArgs(),
+              pinotQuery.getSelectList());
       for (Expression expression : selectList) {
         applyTimestampIndex(expression, pinotQuery);
       }
       pinotQuery.setSelectList(selectList);
       if (node.getAggType() == AggregateNode.AggType.DIRECT) {
-        
pinotQuery.putToQueryOptions(CommonConstants.Broker.Request.QueryOptionKey.SERVER_RETURN_FINAL_RESULT,
 "true");
+        
pinotQuery.putToQueryOptions(QueryOptionKey.SERVER_RETURN_FINAL_RESULT, "true");
       } else if (node.isLeafReturnFinalResult()) {
-        pinotQuery.putToQueryOptions(
-            
CommonConstants.Broker.Request.QueryOptionKey.SERVER_RETURN_FINAL_RESULT_KEY_UNPARTITIONED,
 "true");
+        // SERVER_RETURN_FINAL_RESULT_KEY_UNPARTITIONED only applies to 
group-by queries.
+        if (groupByList.isEmpty()) {
+          
pinotQuery.putToQueryOptions(QueryOptionKey.SERVER_RETURN_FINAL_RESULT, "true");
+        } else {
+          
pinotQuery.putToQueryOptions(QueryOptionKey.SERVER_RETURN_FINAL_RESULT_KEY_UNPARTITIONED,
 "true");
+        }

Review Comment:
   Correct split: for no-GROUP-BY, SERVER_RETURN_FINAL_RESULT_KEY_UNPARTITIONED 
was effectively a no-op (AggregationResultsBlock only honors 
SERVER_RETURN_FINAL_RESULT), so routing non-group-by to 
SERVER_RETURN_FINAL_RESULT is what makes leaf-return-final actually take 
effect. GROUP BY path unchanged.



-- 
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