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


##########
pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/ModeAggregationFunction.java:
##########
@@ -495,18 +587,22 @@ public SerializedIntermediateResult 
serializeIntermediateResult(Map<? extends Nu
   }
 
   @Override
-  public Map<? extends Number, Long> 
deserializeIntermediateResult(CustomObject customObject) {
+  public Map<?, Long> deserializeIntermediateResult(CustomObject customObject) 
{
     return ObjectSerDeUtils.deserialize(customObject);
   }
 
   @Override
   public ColumnDataType getFinalResultColumnType() {
-    return ColumnDataType.DOUBLE;
+    return _resultType;
   }
 
   @Nullable
   @Override
-  public Double extractFinalResult(@Nullable Map<? extends Number, Long> 
intermediateResult) {
+  public Comparable<?> extractFinalResult(@Nullable Map<?, Long> 
intermediateResult) {
+    if (_resultType != ColumnDataType.DOUBLE) {
+      return intermediateResult == null || intermediateResult.isEmpty() ? null

Review Comment:
   With null handling off, an empty group now gives `null` for BOOLEAN and 
TIMESTAMP. Before, it gave the -Infinity sentinel. An ORDER BY on this value 
then fails with an NPE in `TableResizer`:
   ```sql
   SELECT intColumn, MODE(booleanColumn) FILTER (WHERE intColumn < 5) FROM 
testTable
   GROUP BY intColumn ORDER BY MODE(booleanColumn) FILTER (WHERE intColumn < 5) 
DESC LIMIT 3
   ```
   MINSTRING and ANYVALUE fail the same way today, so the root cause is the 
comparator in `TableResizer`. Please make it null-safe, or at least add a test 
for this case. Not blocking.



##########
pinot-core/src/main/java/org/apache/pinot/core/query/optimizer/statement/AggregateFunctionRewriteOptimizer.java:
##########
@@ -134,6 +139,31 @@ private static void addDataTypeArgument(Function function, 
String functionName,
         functionName, fieldSpec.isSingleValueField() ? "" : "multi-value ", 
fieldSpec.getDataType()));
   }
 
+  /// Appends the default tie reducer when missing, then the data column's 
logical type when it is non-numeric.
+  private static void addModeDataTypeArgument(Function function, FieldSpec 
fieldSpec) {
+    if (!fieldSpec.isSingleValueField()) {
+      return;
+    }
+    String dataType;
+    switch (fieldSpec.getDataType()) {
+      case STRING:
+      case BOOLEAN:

Review Comment:
   This breaks working queries during a rolling upgrade. The broker upgrades 
before the servers. It rewrites `MODE(boolCol)` and `MODE(tsCol, 'MAX')` to the 
3-arg form, and an old server rejects that form with `Mode expects at most 2 
arguments, got: 3`. Today these queries work (they return DOUBLE), so the 
labels alone do not cover it. This is not the same as FIRST_WITH_TIME in 
#19986, because that form never worked before. STRING and JSON also failed 
before, so they are safe to rewrite now.
   
   Also, when MODE is inside an expression, the column name now shows the added 
arguments, for example 
`datetrunc('DAY',mode(timestampColumn,'MIN','TIMESTAMP'))`.
   
   Suggestion: rewrite only STRING and JSON in this PR. Put BOOLEAN and 
TIMESTAMP behind a broker config that is off by default (like 
`inferAnyValueType` in #19990), or move them to a later release.



##########
pinot-query-runtime/src/test/resources/queries/UDFAggregates.json:
##########
@@ -108,6 +127,28 @@
         "sql": "SELECT /*+ aggOptions(is_skip_leaf_stage_aggregate='true') */ 
string_col, FIRST_WITH_TIME(decimal_col, timestamp_col), 
LAST_WITH_TIME(decimal_col, timestamp_col), FIRST_WITH_TIME(bytes_col, 
timestamp_col), LAST_WITH_TIME(bytes_col, timestamp_col) FROM {tbl} GROUP BY 
string_col",
         "outputs": [["a", "1E-307", "1E+307", "deadbeef", "fdeadbee"], ["b", 
"1E-308", "1E+308", "efdeadbe", "beefdead"], ["c", "1E+328", "1E-317", 
"deadbe02", "adbeefde"]]
       },
+      {
+        "comment": "MODE returns the type of a non-numeric argument, with ties 
broken by the smallest value",
+        "sql": "SELECT MODE(string_col), MODE(UPPER(string_col)), 
MODE(bool_col), MODE(timestamp_col), MODE(int_col) FROM {tbl}",
+        "outputs": [["c", "C", false, "2020-01-01 03:10:12.0", 2]]
+      },
+      {
+        "sql": "SELECT bool_col, MODE(string_col), MODE(timestamp_col) FROM 
{tbl} GROUP BY bool_col",
+        "outputs": [[false, "b", "2020-01-01 08:32:12.0"], [true, "a", 
"2020-01-01 03:10:12.0"]]
+      },
+      {
+        "sql": "SELECT /*+ aggOptions(is_skip_leaf_stage_aggregate='true') */ 
int_col, MODE(string_col), MODE(bool_col), MODE(timestamp_col) FROM {tbl} GROUP 
BY int_col",

Review Comment:
   `is_skip_leaf_stage_aggregate` is not a hint (only 
`is_skip_leaf_stage_group_by` exists), and unknown hints are ignored. So this 
case runs the normal leaf plan. Use `is_skip_leaf_stage_group_by='true'`. I ran 
it locally with that hint and it passes.



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