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

gortiz 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 b398fed58b Fix v1 query engine behavior for aggregations without group 
by where the limit is zero (#13564)
b398fed58b is described below

commit b398fed58b6e3d1681ca12ad95a657a1378b99eb
Author: Yash Mayya <yash.ma...@gmail.com>
AuthorDate: Tue Dec 24 15:28:31 2024 +0700

    Fix v1 query engine behavior for aggregations without group by where the 
limit is zero (#13564)
---
 .../blocks/results/AggregationResultsBlock.java    |  8 ++-
 .../merger/AggregationResultsBlockMerger.java      |  5 ++
 .../operator/query/EmptyAggregationOperator.java   | 64 ++++++++++++++++++++++
 .../pinot/core/plan/AggregationPlanNode.java       |  6 ++
 .../tests/OfflineClusterIntegrationTest.java       | 29 +++++++++-
 5 files changed, 110 insertions(+), 2 deletions(-)

diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/blocks/results/AggregationResultsBlock.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/blocks/results/AggregationResultsBlock.java
index 38ac595548..0fd2e29a25 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/blocks/results/AggregationResultsBlock.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/blocks/results/AggregationResultsBlock.java
@@ -68,7 +68,7 @@ public class AggregationResultsBlock extends BaseResultsBlock 
{
 
   @Override
   public int getNumRows() {
-    return 1;
+    return _queryContext.getLimit() == 0 ? 0 : 1;
   }
 
   @Override
@@ -108,6 +108,12 @@ public class AggregationResultsBlock extends 
BaseResultsBlock {
     ColumnDataType[] columnDataTypes = dataSchema.getColumnDataTypes();
     int numColumns = columnDataTypes.length;
     DataTableBuilder dataTableBuilder = 
DataTableBuilderFactory.getDataTableBuilder(dataSchema);
+
+    // For LIMIT 0 queries
+    if (_results.isEmpty()) {
+      return dataTableBuilder.build();
+    }
+
     boolean returnFinalResult = _queryContext.isServerReturnFinalResult();
     if (_queryContext.isNullHandlingEnabled()) {
       RoaringBitmap[] nullBitmaps = new RoaringBitmap[numColumns];
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/combine/merger/AggregationResultsBlockMerger.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/combine/merger/AggregationResultsBlockMerger.java
index ccdf86bd3c..82adace78d 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/combine/merger/AggregationResultsBlockMerger.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/combine/merger/AggregationResultsBlockMerger.java
@@ -37,6 +37,11 @@ public class AggregationResultsBlockMerger implements 
ResultsBlockMerger<Aggrega
     List<Object> resultsToMerge = blockToMerge.getResults();
     assert aggregationFunctions != null && mergedResults != null && 
resultsToMerge != null;
 
+    // Skip merging empty results (LIMIT 0 queries)
+    if (mergedBlock.getNumRows() == 0 && blockToMerge.getNumRows() == 0) {
+      return;
+    }
+
     int numAggregationFunctions = aggregationFunctions.length;
     for (int i = 0; i < numAggregationFunctions; i++) {
       mergedResults.set(i, aggregationFunctions[i].merge(mergedResults.get(i), 
resultsToMerge.get(i)));
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/query/EmptyAggregationOperator.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/query/EmptyAggregationOperator.java
new file mode 100644
index 0000000000..a0fcdbc358
--- /dev/null
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/query/EmptyAggregationOperator.java
@@ -0,0 +1,64 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.pinot.core.operator.query;
+
+import java.util.Collections;
+import java.util.List;
+import org.apache.pinot.core.common.Operator;
+import org.apache.pinot.core.operator.BaseOperator;
+import org.apache.pinot.core.operator.ExecutionStatistics;
+import org.apache.pinot.core.operator.blocks.results.AggregationResultsBlock;
+import org.apache.pinot.core.query.request.context.QueryContext;
+
+
+/**
+ * The <code>EmptyAggregationOperator</code> provides a way to short circuit 
aggregation only queries (no group by)
+ * with a LIMIT of zero.
+ */
+public class EmptyAggregationOperator extends 
BaseOperator<AggregationResultsBlock> {
+
+  private static final String EXPLAIN_NAME = "AGGREGATE_EMPTY";
+  private final QueryContext _queryContext;
+  private final ExecutionStatistics _executionStatistics;
+
+  public EmptyAggregationOperator(QueryContext queryContext, int numTotalDocs) 
{
+    _queryContext = queryContext;
+    _executionStatistics = new ExecutionStatistics(0, 0, 0, numTotalDocs);
+  }
+
+  @Override
+  protected AggregationResultsBlock getNextBlock() {
+    return new 
AggregationResultsBlock(_queryContext.getAggregationFunctions(), 
Collections.emptyList(), _queryContext);
+  }
+
+  @Override
+  public List<Operator> getChildOperators() {
+    return Collections.emptyList();
+  }
+
+  @Override
+  public String toExplainString() {
+    return EXPLAIN_NAME;
+  }
+
+  @Override
+  public ExecutionStatistics getExecutionStatistics() {
+    return _executionStatistics;
+  }
+}
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/plan/AggregationPlanNode.java 
b/pinot-core/src/main/java/org/apache/pinot/core/plan/AggregationPlanNode.java
index 6202f0890d..361bc47545 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/plan/AggregationPlanNode.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/plan/AggregationPlanNode.java
@@ -25,6 +25,7 @@ import org.apache.pinot.core.common.Operator;
 import org.apache.pinot.core.operator.blocks.results.AggregationResultsBlock;
 import org.apache.pinot.core.operator.filter.BaseFilterOperator;
 import org.apache.pinot.core.operator.query.AggregationOperator;
+import org.apache.pinot.core.operator.query.EmptyAggregationOperator;
 import org.apache.pinot.core.operator.query.FastFilteredCountOperator;
 import org.apache.pinot.core.operator.query.FilteredAggregationOperator;
 import org.apache.pinot.core.operator.query.NonScanBasedAggregationOperator;
@@ -70,6 +71,11 @@ public class AggregationPlanNode implements PlanNode {
   @Override
   public Operator<AggregationResultsBlock> run() {
     assert _queryContext.getAggregationFunctions() != null;
+
+    if (_queryContext.getLimit() == 0) {
+      return new EmptyAggregationOperator(_queryContext, 
_indexSegment.getSegmentMetadata().getTotalDocs());
+    }
+
     return _queryContext.hasFilteredAggregations() ? 
buildFilteredAggOperator() : buildNonFilteredAggOperator();
   }
 
diff --git 
a/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/OfflineClusterIntegrationTest.java
 
b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/OfflineClusterIntegrationTest.java
index f788eeb5ac..1f19efa778 100644
--- 
a/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/OfflineClusterIntegrationTest.java
+++ 
b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/OfflineClusterIntegrationTest.java
@@ -3658,7 +3658,34 @@ public class OfflineClusterIntegrationTest extends 
BaseClusterIntegrationTestSet
   public void testGroupByAggregationWithLimitZero(boolean 
useMultiStageQueryEngine)
       throws Exception {
     setUseMultiStageQueryEngine(useMultiStageQueryEngine);
-    testQuery("SELECT Origin, SUM(ArrDelay) FROM mytable GROUP BY Origin LIMIT 
0");
+
+    String sqlQuery = "SELECT Origin, SUM(ArrDelay) FROM mytable GROUP BY 
Origin LIMIT 0";
+    JsonNode response = postQuery(sqlQuery);
+    assertTrue(response.get("exceptions").isEmpty());
+    JsonNode rows = response.get("resultTable").get("rows");
+    assertEquals(rows.size(), 0);
+
+    // Ensure data schema returned is accurate even if there are no rows 
returned
+    JsonNode columnDataTypes = 
response.get("resultTable").get("dataSchema").get("columnDataTypes");
+    assertEquals(columnDataTypes.size(), 2);
+    assertEquals(columnDataTypes.get(1).asText(), "DOUBLE");
+  }
+
+  @Test(dataProvider = "useBothQueryEngines")
+  public void testAggregationWithLimitZero(boolean useMultiStageQueryEngine)
+      throws Exception {
+    setUseMultiStageQueryEngine(useMultiStageQueryEngine);
+
+    String sqlQuery = "SELECT AVG(ArrDelay) FROM mytable LIMIT 0";
+    JsonNode response = postQuery(sqlQuery);
+    assertTrue(response.get("exceptions").isEmpty());
+    JsonNode rows = response.get("resultTable").get("rows");
+    assertEquals(rows.size(), 0);
+
+    // Ensure data schema returned is accurate even if there are no rows 
returned
+    JsonNode columnDataTypes = 
response.get("resultTable").get("dataSchema").get("columnDataTypes");
+    assertEquals(columnDataTypes.size(), 1);
+    assertEquals(columnDataTypes.get(0).asText(), "DOUBLE");
   }
 
   @Test(dataProvider = "useBothQueryEngines")


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@pinot.apache.org
For additional commands, e-mail: commits-h...@pinot.apache.org

Reply via email to