This is an automated email from the ASF dual-hosted git repository.

yashmayya 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 18d0fbe186d Fix alias handling for empty response on SSE queries with 
post aggregation (#17815)
18d0fbe186d is described below

commit 18d0fbe186d56136a215227672e23838fe39aa61
Author: Yash Mayya <[email protected]>
AuthorDate: Thu Mar 5 09:54:30 2026 -0800

    Fix alias handling for empty response on SSE queries with post aggregation 
(#17815)
---
 .../broker/requesthandler/EmptyResponseUtils.java  | 92 ++++++++++++++--------
 .../requesthandler/EmptyResponseUtilsTest.java     | 34 ++++++++
 2 files changed, 93 insertions(+), 33 deletions(-)

diff --git 
a/pinot-broker/src/main/java/org/apache/pinot/broker/requesthandler/EmptyResponseUtils.java
 
b/pinot-broker/src/main/java/org/apache/pinot/broker/requesthandler/EmptyResponseUtils.java
index a6c7278ff72..bc940b15498 100644
--- 
a/pinot-broker/src/main/java/org/apache/pinot/broker/requesthandler/EmptyResponseUtils.java
+++ 
b/pinot-broker/src/main/java/org/apache/pinot/broker/requesthandler/EmptyResponseUtils.java
@@ -33,6 +33,7 @@ import org.apache.pinot.common.utils.DataSchema;
 import org.apache.pinot.common.utils.DataSchema.ColumnDataType;
 import org.apache.pinot.core.query.aggregation.function.AggregationFunction;
 import 
org.apache.pinot.core.query.aggregation.function.AggregationFunctionUtils;
+import org.apache.pinot.core.query.reduce.PostAggregationHandler;
 import org.apache.pinot.core.query.request.context.QueryContext;
 import org.apache.pinot.query.QueryEnvironment;
 import org.apache.pinot.query.planner.logical.RelToPlanNodeConverter;
@@ -80,59 +81,84 @@ public class EmptyResponseUtils {
   private static ResultTable buildEmptyAggregationResultTable(QueryContext 
queryContext) {
     List<Pair<AggregationFunction, FilterContext>> 
filteredAggregationFunctions =
         queryContext.getFilteredAggregationFunctions();
-    List<String> aliases = queryContext.getAliasList();
     assert filteredAggregationFunctions != null;
     int numAggregations = filteredAggregationFunctions.size();
-    String[] columnNames = new String[numAggregations];
-    ColumnDataType[] columnDataTypes = new ColumnDataType[numAggregations];
-    Object[] row = new Object[numAggregations];
+
+    // Build pre-post-aggregation DataSchema from individual aggregation 
functions
+    String[] preAggColumnNames = new String[numAggregations];
+    ColumnDataType[] preAggColumnDataTypes = new 
ColumnDataType[numAggregations];
+    Object[] rawRow = new Object[numAggregations];
     for (int i = 0; i < numAggregations; i++) {
       Pair<AggregationFunction, FilterContext> pair = 
filteredAggregationFunctions.get(i);
       AggregationFunction aggregationFunction = pair.getLeft();
-      if (aliases.size() == numAggregations && aliases.get(i) != null) {
-        columnNames[i] = aliases.get(i);
-      } else {
-        columnNames[i] = 
AggregationFunctionUtils.getResultColumnName(aggregationFunction, 
pair.getRight());
-      }
-      columnDataTypes[i] = aggregationFunction.getFinalResultColumnType();
+      preAggColumnNames[i] = 
AggregationFunctionUtils.getResultColumnName(aggregationFunction, 
pair.getRight());
+      preAggColumnDataTypes[i] = 
aggregationFunction.getFinalResultColumnType();
       Object finalResult = aggregationFunction.extractFinalResult(
           
aggregationFunction.extractAggregationResult(aggregationFunction.createAggregationResultHolder()));
-      row[i] = finalResult != null ? columnDataTypes[i].convert(finalResult) : 
null;
+      rawRow[i] = finalResult != null ? 
preAggColumnDataTypes[i].convert(finalResult) : null;
     }
-    return new ResultTable(new DataSchema(columnNames, columnDataTypes), 
List.<Object[]>of(row));
+    DataSchema prePostAggDataSchema = new DataSchema(preAggColumnNames, 
preAggColumnDataTypes);
+
+    // Use PostAggregationHandler to evaluate post-aggregation expressions and 
reorder columns
+    PostAggregationHandler postAggregationHandler = new 
PostAggregationHandler(queryContext, prePostAggDataSchema);
+    DataSchema resultDataSchema = postAggregationHandler.getResultDataSchema();
+    Object[] resultRow = postAggregationHandler.getResult(rawRow);
+
+    applyAliases(queryContext, resultDataSchema);
+    return new ResultTable(resultDataSchema, List.<Object[]>of(resultRow));
   }
 
   private static ResultTable buildEmptyGroupByResultTable(QueryContext 
queryContext) {
     List<Pair<AggregationFunction, FilterContext>> 
filteredAggregationFunctions =
         queryContext.getFilteredAggregationFunctions();
-    List<String> aliases = queryContext.getAliasList();
     List<ExpressionContext> groupByExpressions = 
queryContext.getGroupByExpressions();
     assert filteredAggregationFunctions != null && groupByExpressions != null;
-    int numColumns = groupByExpressions.size() + 
filteredAggregationFunctions.size();
-    String[] columnNames = new String[numColumns];
-    ColumnDataType[] columnDataTypes = new ColumnDataType[numColumns];
-    int index = 0;
-    for (ExpressionContext groupByExpression : groupByExpressions) {
-      if (aliases.size() == numColumns && aliases.get(index) != null) {
-        columnNames[index] = aliases.get(index);
-      } else {
-        columnNames[index] = groupByExpression.toString();
-      }
+    int numGroupByExpressions = groupByExpressions.size();
+    int numAggregations = filteredAggregationFunctions.size();
+    int numColumns = numGroupByExpressions + numAggregations;
+
+    // Build pre-post-aggregation DataSchema with group-by columns followed by 
aggregation columns
+    String[] preAggColumnNames = new String[numColumns];
+    ColumnDataType[] preAggColumnDataTypes = new ColumnDataType[numColumns];
+    for (int i = 0; i < numGroupByExpressions; i++) {
+      preAggColumnNames[i] = groupByExpressions.get(i).toString();
       // Use STRING column data type as default for group-by expressions
-      columnDataTypes[index] = ColumnDataType.STRING;
-      index++;
+      preAggColumnDataTypes[i] = ColumnDataType.STRING;
     }
-    for (Pair<AggregationFunction, FilterContext> pair : 
filteredAggregationFunctions) {
+    for (int i = 0; i < numAggregations; i++) {
+      Pair<AggregationFunction, FilterContext> pair = 
filteredAggregationFunctions.get(i);
       AggregationFunction aggregationFunction = pair.getLeft();
-      if (aliases.size() == numColumns && aliases.get(index) != null) {
-        columnNames[index] = aliases.get(index);
-      } else {
-        columnNames[index] = 
AggregationFunctionUtils.getResultColumnName(aggregationFunction, 
pair.getRight());
+      preAggColumnNames[numGroupByExpressions + i] =
+          AggregationFunctionUtils.getResultColumnName(aggregationFunction, 
pair.getRight());
+      preAggColumnDataTypes[numGroupByExpressions + i] = 
aggregationFunction.getFinalResultColumnType();
+    }
+    DataSchema prePostAggDataSchema = new DataSchema(preAggColumnNames, 
preAggColumnDataTypes);
+
+    // Use PostAggregationHandler to evaluate post-aggregation expressions and 
reorder columns
+    PostAggregationHandler postAggregationHandler = new 
PostAggregationHandler(queryContext, prePostAggDataSchema);
+    DataSchema resultDataSchema = postAggregationHandler.getResultDataSchema();
+
+    applyAliases(queryContext, resultDataSchema);
+    return new ResultTable(resultDataSchema, List.of());
+  }
+
+  private static void applyAliases(QueryContext queryContext, DataSchema 
dataSchema) {
+    List<String> aliasList = queryContext.getAliasList();
+    if (aliasList == null || aliasList.isEmpty()) {
+      return;
+    }
+    String[] columnNames = dataSchema.getColumnNames();
+    List<ExpressionContext> selectExpressions = 
queryContext.getSelectExpressions();
+    int numSelectExpressions = selectExpressions.size();
+    if (columnNames.length != numSelectExpressions || aliasList.size() != 
numSelectExpressions) {
+      return;
+    }
+    for (int i = 0; i < numSelectExpressions; i++) {
+      String alias = aliasList.get(i);
+      if (alias != null) {
+        columnNames[i] = alias;
       }
-      columnDataTypes[index] = aggregationFunction.getFinalResultColumnType();
-      index++;
     }
-    return new ResultTable(new DataSchema(columnNames, columnDataTypes), 
List.of());
   }
 
   /// Tries to fill an [DataSchema] when no row has been returned.
diff --git 
a/pinot-broker/src/test/java/org/apache/pinot/broker/requesthandler/EmptyResponseUtilsTest.java
 
b/pinot-broker/src/test/java/org/apache/pinot/broker/requesthandler/EmptyResponseUtilsTest.java
index c9ceafc8e0a..ca0295b1974 100644
--- 
a/pinot-broker/src/test/java/org/apache/pinot/broker/requesthandler/EmptyResponseUtilsTest.java
+++ 
b/pinot-broker/src/test/java/org/apache/pinot/broker/requesthandler/EmptyResponseUtilsTest.java
@@ -140,6 +140,40 @@ public class EmptyResponseUtilsTest {
     assertTrue(resultTable.getRows().isEmpty());
   }
 
+  @Test
+  public void testBuildEmptyResultTableWithPostAggregation() {
+    // Aggregation with post-aggregation expression and aliases
+    QueryContext queryContext = QueryContextConverterUtils.getQueryContext(
+        "SELECT SUM(a) * 1.0 / COUNT(*) AS rate, SUM(a) AS total FROM 
testTable WHERE foo = 'bar'");
+    ResultTable resultTable = 
EmptyResponseUtils.buildEmptyResultTable(queryContext);
+    DataSchema dataSchema = resultTable.getDataSchema();
+    assertEquals(dataSchema.getColumnNames(), new String[]{"rate", "total"});
+    assertEquals(dataSchema.getColumnDataTypes(),
+        new ColumnDataType[]{ColumnDataType.DOUBLE, ColumnDataType.DOUBLE});
+    List<Object[]> rows = resultTable.getRows();
+    assertEquals(rows.size(), 1);
+
+    // Aggregation with post-aggregation expression without alias
+    queryContext = QueryContextConverterUtils.getQueryContext(
+        "SELECT SUM(a) + MAX(b) FROM testTable WHERE foo = 'bar'");
+    resultTable = EmptyResponseUtils.buildEmptyResultTable(queryContext);
+    dataSchema = resultTable.getDataSchema();
+    assertEquals(dataSchema.size(), 1);
+    assertEquals(dataSchema.getColumnDataTypes(), new 
ColumnDataType[]{ColumnDataType.DOUBLE});
+    rows = resultTable.getRows();
+    assertEquals(rows.size(), 1);
+
+    // Group-by with post-aggregation expression
+    queryContext = QueryContextConverterUtils.getQueryContext(
+        "SELECT c, SUM(a) * 1.0 / COUNT(*) AS rate FROM testTable WHERE foo = 
'bar' GROUP BY c");
+    resultTable = EmptyResponseUtils.buildEmptyResultTable(queryContext);
+    dataSchema = resultTable.getDataSchema();
+    assertEquals(dataSchema.getColumnNames(), new String[]{"c", "rate"});
+    assertEquals(dataSchema.getColumnDataTypes(),
+        new ColumnDataType[]{ColumnDataType.STRING, ColumnDataType.DOUBLE});
+    assertTrue(resultTable.getRows().isEmpty());
+  }
+
   @Test
   public void testBuildEmptyResultTableWithDistinctCountRawHLL() {
     // Test DISTINCTCOUNTRAWHLL aggregation with empty results


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

Reply via email to