Skip to content

Commit b40c0dd

Browse files
committed
Fix the count() only aggregation pushdown issue
Signed-off-by: Lantao Jin <ltjin@amazon.com>
1 parent 71aa9ba commit b40c0dd

7 files changed

Lines changed: 72 additions & 0 deletions

File tree

core/src/main/java/org/opensearch/sql/calcite/CalciteRelNodeVisitor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,11 @@ private Pair<List<RexNode>, List<AggCall>> aggregateWithTrimming(
556556
// \- Project([c, b])
557557
// \- Filter(a > 1)
558558
// \- Scan t
559+
// Example 3: source=t | stats count(): no project added for count()
560+
// Before: Aggregate(count)
561+
// \- Scan t
562+
// After: Aggregate(count)
563+
// \- Scan t
559564
Pair<List<RexNode>, List<AggCall>> resolved =
560565
resolveAttributesForAggregation(groupExprList, aggExprList, context);
561566
List<RexInputRef> trimmedRefs = new ArrayList<>();

integ-test/src/test/java/org/opensearch/sql/ppl/ExplainIT.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,14 @@ public void testFilterAndAggPushDownExplain() throws IOException {
9797
+ "| stats avg(age) AS avg_age by state, city"));
9898
}
9999

100+
@Test
101+
public void testCountAggPushDownExplain() throws IOException {
102+
String expected = loadExpectedPlan("explain_count_agg_push.json");
103+
assertJsonEqualsIgnoreId(
104+
expected,
105+
explainQueryToString("source=opensearch-sql_test_index_account | stats count() as cnt"));
106+
}
107+
100108
@Test
101109
public void testSortPushDownExplain() throws IOException {
102110
String expected = loadExpectedPlan("explain_sort_push.json");
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"calcite": {
3+
"logical": "LogicalAggregate(group=[{}], cnt=[COUNT()])\n CalciteLogicalIndexScan(table=[[OpenSearch, opensearch-sql_test_index_account]])\n",
4+
"physical": "CalciteEnumerableIndexScan(table=[[OpenSearch, opensearch-sql_test_index_account]], PushDownContext=[[AGGREGATION->rel#:LogicalAggregate.NONE.[](input=RelSubset#,group={},cnt=COUNT())], OpenSearchRequestBuilder(sourceBuilder={\"from\":0,\"size\":0,\"timeout\":\"1m\",\"aggregations\":{\"cnt\":{\"value_count\":{\"field\":\"_index\"}}}}, requestedTotalSize=2147483647, pageSize=null, startFrom=0)])\n"
5+
}
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"calcite": {
3+
"logical": "LogicalAggregate(group=[{}], cnt=[COUNT()])\n CalciteLogicalIndexScan(table=[[OpenSearch, opensearch-sql_test_index_account]])\n",
4+
"physical": "EnumerableAggregate(group=[{}], cnt=[COUNT()])\n CalciteEnumerableIndexScan(table=[[OpenSearch, opensearch-sql_test_index_account]])\n"
5+
}
6+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"root": {
3+
"name": "ProjectOperator",
4+
"description": {
5+
"fields": "[cnt]"
6+
},
7+
"children": [{
8+
"name": "OpenSearchIndexScan",
9+
"description": {
10+
"request": "OpenSearchQueryRequest(indexName=opensearch-sql_test_index_account, sourceBuilder={\"from\":0,\"size\":0,\"timeout\":\"1m\",\"aggregations\":{\"cnt\":{\"value_count\":{\"field\":\"_index\"}}}}, needClean=true, searchDone=false, pitId=*, cursorKeepAlive=null, searchAfter=null, searchResponse=null)"
11+
},
12+
"children": []
13+
}]
14+
}
15+
}

opensearch/src/main/java/org/opensearch/sql/opensearch/planner/physical/OpenSearchAggregateIndexScanRule.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.apache.calcite.plan.RelRule;
1010
import org.apache.calcite.rel.logical.LogicalAggregate;
1111
import org.apache.calcite.rel.logical.LogicalProject;
12+
import org.apache.calcite.sql.SqlKind;
1213
import org.immutables.value.Value;
1314
import org.opensearch.sql.opensearch.storage.scan.CalciteLogicalIndexScan;
1415

@@ -29,6 +30,11 @@ public void onMatch(RelOptRuleCall call) {
2930
final LogicalProject project = call.rel(1);
3031
final CalciteLogicalIndexScan scan = call.rel(2);
3132
apply(call, aggregate, project, scan);
33+
} else if (call.rels.length == 2) {
34+
// case of count() without group-by
35+
final LogicalAggregate aggregate = call.rel(0);
36+
final CalciteLogicalIndexScan scan = call.rel(1);
37+
apply(call, aggregate, null, scan);
3238
} else {
3339
throw new AssertionError(
3440
String.format(
@@ -54,6 +60,7 @@ public interface Config extends RelRule.Config {
5460
Config DEFAULT =
5561
ImmutableOpenSearchAggregateIndexScanRule.Config.builder()
5662
.build()
63+
.withDescription("Agg-Project-TableScan")
5764
.withOperandSupplier(
5865
b0 ->
5966
b0.operand(LogicalAggregate.class)
@@ -71,6 +78,28 @@ public interface Config extends RelRule.Config {
7178
OpenSearchIndexScanRule
7279
::noAggregatePushed))
7380
.noInputs())));
81+
Config COUNT_STAR =
82+
ImmutableOpenSearchAggregateIndexScanRule.Config.builder()
83+
.build()
84+
.withDescription("Agg[count()]-TableScan")
85+
.withOperandSupplier(
86+
b0 ->
87+
b0.operand(LogicalAggregate.class)
88+
.predicate(
89+
agg ->
90+
agg.getGroupSet().isEmpty()
91+
&& agg.getAggCallList().stream()
92+
.allMatch(
93+
call ->
94+
call.getAggregation().kind == SqlKind.COUNT
95+
&& call.getArgList().isEmpty()))
96+
.oneInput(
97+
b1 ->
98+
b1.operand(CalciteLogicalIndexScan.class)
99+
.predicate(
100+
Predicate.not(OpenSearchIndexScanRule::isLimitPushed)
101+
.and(OpenSearchIndexScanRule::noAggregatePushed))
102+
.noInputs()));
74103

75104
@Override
76105
default OpenSearchAggregateIndexScanRule toRule() {

opensearch/src/main/java/org/opensearch/sql/opensearch/planner/physical/OpenSearchIndexRules.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ public class OpenSearchIndexRules {
1616
OpenSearchFilterIndexScanRule.Config.DEFAULT.toRule();
1717
private static final OpenSearchAggregateIndexScanRule AGGREGATE_INDEX_SCAN =
1818
OpenSearchAggregateIndexScanRule.Config.DEFAULT.toRule();
19+
private static final OpenSearchAggregateIndexScanRule COUNT_STAR_INDEX_SCAN =
20+
OpenSearchAggregateIndexScanRule.Config.COUNT_STAR.toRule();
1921
private static final OpenSearchLimitIndexScanRule LIMIT_INDEX_SCAN =
2022
OpenSearchLimitIndexScanRule.Config.DEFAULT.toRule();
2123
private static final OpenSearchSortIndexScanRule SORT_INDEX_SCAN =
@@ -26,6 +28,7 @@ public class OpenSearchIndexRules {
2628
PROJECT_INDEX_SCAN,
2729
FILTER_INDEX_SCAN,
2830
AGGREGATE_INDEX_SCAN,
31+
COUNT_STAR_INDEX_SCAN,
2932
LIMIT_INDEX_SCAN,
3033
SORT_INDEX_SCAN);
3134

0 commit comments

Comments
 (0)