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


##########
pinot-segment-spi/src/main/java/org/apache/pinot/segment/spi/AggregationFunctionType.java:
##########
@@ -428,6 +428,15 @@ public static AggregationFunctionType 
getAggregationFunctionType(String function
     }
   }
 
+  /// Returns DOUBLE, or the type of the data argument for the explicit data 
type form MODE(expr, reducer, dataType).
+  private static class ModeFinalReturnTypeInference implements 
SqlReturnTypeInference {
+    @Override
+    public RelDataType inferReturnType(SqlOperatorBinding opBinding) {
+      return opBinding.getOperandCount() == 3 ? 
ReturnTypes.ARG0.inferReturnType(opBinding)
+          : opBinding.getTypeFactory().createSqlType(SqlTypeName.DOUBLE);
+    }

Review Comment:
   Small readability nit: this returns DOUBLE for the 1-arg form, which is what 
the leaf intermediate stage uses, while the final client type for 
BOOLEAN/TIMESTAMP comes from Calcite's ARG0 MODE at the FINAL stage. That 
interplay is subtle — a one-line comment here pointing at where the real final 
type is resolved would save the next reader a trace.



##########
pinot-query-planner/src/main/java/org/apache/pinot/calcite/rel/rules/PinotAggregateTypeArgumentRewriteRule.java:
##########
@@ -116,20 +119,31 @@ public void onMatch(RelOptRuleCall call) {
         aggRel.copy(aggRel.getTraitSet(), newInput, aggRel.getGroupSet(), 
aggRel.getGroupSets(), aggCalls));
   }
 
-  /// Returns the data type argument to append, or `null` when the call 
already has one or is not such a call. Throws
-  /// for a data argument without an explicit form, such as an array from a 
multi-value column.
+  /// Returns the literal arguments to append, or `null` when the call already 
has them or is not such a call.
   @Nullable
-  private static String getDataTypeArgument(AggregateCall aggCall, RelNode 
input) {
-    if (aggCall.getArgList().size() != 2) {
-      return null;
-    }
+  private static List<String> getLiteralArguments(AggregateCall aggCall, 
RelNode input) {
+    List<Integer> argList = aggCall.getArgList();
     String name = 
AggregationFunctionType.getNormalizedAggregationFunctionName(aggCall.getAggregation().getName());
-    if (!name.equals(AggregationFunctionType.FIRSTWITHTIME.name())
-        && !name.equals(AggregationFunctionType.LASTWITHTIME.name())) {
-      return null;
+    if (name.equals(AggregationFunctionType.MODE.name())) {
+      // BOOLEAN and TIMESTAMP already return their own type, cast from the 
DOUBLE result. The leaf stage may still
+      // rewrite them in the single-stage optimizer, which keeps the same 
INT/LONG count maps.
+      return argList.size() == 1 && 
SqlTypeName.CHAR_TYPES.contains(getSqlTypeName(input, argList.get(0)))
+          ? List.of("MIN", "STRING") : null;
     }
-    SqlTypeName dataType =
-        
input.getRowType().getFieldList().get(aggCall.getArgList().get(0)).getType().getSqlTypeName();
+    if (argList.size() == 2 && 
(name.equals(AggregationFunctionType.FIRSTWITHTIME.name())
+        || name.equals(AggregationFunctionType.LASTWITHTIME.name()))) {
+      return List.of(getDataTypeArgument(name, getSqlTypeName(input, 
argList.get(0))));

Review Comment:
   Nice that the single-stage path is covered by the optimizer. Worth noting 
the known rolling-upgrade gap you called out in the description: a pre-patch 
server rejects the rewritten 3-arg MODE. Same pattern as FIRST_WITH_TIME, so 
just confirming it's an accepted trade-off here.



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