Repository navigation
fix(sql-parser): accept global aggregates without GROUP BY - #826
milindsrivastava1997 wants to merge 2 commits into
Conversation
milindsrivastava1997
left a comment
There was a problem hiding this comment.
Automated code review: 6 findings, posted inline. Findings 1, 2, 5 and 6 concern code outside this diff, so they are anchored on the Some(group_bys) change that makes those paths reachable.
| } else { | ||
| Some(group_bys) | ||
| } | ||
| Some(group_bys) |
There was a problem hiding this comment.
[correctness] Global SUM/COUNT can match a label-keyed CMS via capability fallback (asap-query-engine/src/engines/simple_engine/sql.rs:258)
Global aggregates now reach find_compatible_aggregation with empty grouping_labels, and labels_compatible (capability_matching.rs:157) compares only grouping_labels, never aggregated_labels.
Scenario: the planner plans SELECT job, SUM(value) ... GROUP BY job, giving CountMinSketch(sum) with grouping=[] and aggregated=[job]. An unplanned SELECT SUM(value) FROM data ... has grouping=[], matches that config, and point-queries the empty key, returning ~0 instead of the total. Before this PR these queries were rejected at parse time.
Suggested fix: require aggregated_labels to be empty when the requirement has no grouping labels, or exclude configs with aggregated labels.
There was a problem hiding this comment.
This was generated by AI during triage.
Fixed in c9bc666. Non-top-k global aggregates now require an exact query configuration instead of capability fallback, so a grouped CMS cannot be selected for an unlabeled scalar query. Added global_aggregate_does_not_fallback_to_grouped_config.
| } else { | ||
| Some(group_bys) | ||
| } | ||
| Some(group_bys) |
There was a problem hiding this comment.
[correctness] Global COUNT also matches top-k CountMinSketchWithHeap configs (asap-query-engine/src/engines/simple_engine/sql.rs:258)
Statistic::Count lists CountMinSketchWithHeap as compatible, and cms_family_sub_type_compatible only checks count vs sum mode. Heap configs always have grouping=[].
Scenario: SELECT srcip, COUNT(value) AS c ... GROUP BY srcip ORDER BY c DESC LIMIT 5 (heap: grouping=[], aggregated=[srcip], count) plus an unplanned SELECT COUNT(value) FROM t .... The global COUNT is served by the srcip-keyed heap sketch and returns a bogus empty-key estimate instead of the event count.
There was a problem hiding this comment.
This was generated by AI during triage.
Fixed by the same c9bc666 guard: a non-top-k global COUNT cannot capability-match a keyed heap configuration. Exact configurations remain eligible, and existing top-k capability-fallback tests still pass.
| } else { | ||
| Some(group_bys) | ||
| } | ||
| Some(group_bys) |
There was a problem hiding this comment.
[correctness] Subquery path skips the unexpected-SELECT-fields check (sqlpattern_parser.rs:244)
Inner subqueries without GROUP BY now parse, but the subquery branch never runs the DISTINCT/HAVING/QUALIFY check that the flat path applies.
Scenario: SELECT SUM(t) FROM (SELECT SUM(value) AS t FROM x WHERE time BETWEEN ... HAVING SUM(value) > 5) used to be rejected by get_groupbys and now parses with the HAVING dropped. Only the matcher's NestedQueryUnsupported stops it today.
There was a problem hiding this comment.
This was generated by AI during triage.
Deferred intentionally: this PR changes parsing only, while nested-query matching and execution remain unsupported. c9bc666 adds a matcher regression asserting NestedQueryUnsupported, so the parsed shape cannot enter execution. Tightening unsupported-clause validation inside nested parsing belongs with the nested-execution work.
| } else { | ||
| Some(group_bys) | ||
| } | ||
| Some(group_bys) |
There was a problem hiding this comment.
[efficiency] Global SUM/COUNT plans a full CMS + DeltaSetAggregator for one scalar (asap-planner-rs/src/planner/sql.rs:124)
With zero aggregated labels, the planner allocates a multi-population CountMinSketch (width x depth) plus a key tracker for a population that is always one empty key. When spatial_output is empty, mapping to a single-population operator (Sum, or MinMax for exact) would be much cheaper.
There was a problem hiding this comment.
This was generated by AI during triage.
Acknowledged and deferred. This PR is scoped to parser acceptance and safe execution of exact global configurations; choosing a cheaper single-population physical plan is a planner optimization and should be addressed separately.
| - name: sum | ||
| query: SELECT SUM(value) FROM data WHERE time BETWEEN DATEADD(s, -1, NOW()) AND NOW() | ||
| expected: unsupported | ||
| expected: supported |
There was a problem hiding this comment.
[test-coverage] Newly supported cases don't check result values (applies to all ~30 flipped cases; runner at asap-query-engine/tests/sql_query_support.rs:90)
The runner only checks that an execution context gets built. Nothing ingests data and asserts that global SUM/COUNT/MIN/MAX/QUANTILE/COUNT(DISTINCT) combines all series. If the empty-grouping path returned one row per series or an empty result, every case would still pass. The parser tests only assert labels.is_empty().
There was a problem hiding this comment.
This was generated by AI during triage.
Fixed in c9bc666 with global_sum_returns_one_unlabeled_result. It inserts two global SUM windows, executes the full SQL pipeline, and asserts one empty-label row with value 8.0; this catches both duplicate-per-series and empty-result failures.
| ) | ||
| .expect("nested global aggregates parse"); | ||
|
|
||
| // Matching and execution may reject nested queries, but parsing retains both layers. |
There was a problem hiding this comment.
[test-coverage] No negative-path test for nested global aggregates
This test only shows that parsing keeps both layers. Nothing asserts that the matcher/planner then rejects the shape with NestedQueryUnsupported, so loosening the n > 1 check in query_info_to_pattern would let these flow into execution with time_info UNUSED and no test would fail. The "may reject" comment also leaves the expected outcome unstated.
There was a problem hiding this comment.
This was generated by AI during triage.
Fixed in c9bc666. test_nested_global_aggregates_remain_unsupported_by_matcher now parses the nested global aggregate and asserts QueryError::NestedQueryUnsupported, making the accepted parser shape and rejected matcher shape explicit.
Summary
Before this change, SQL global aggregates without
GROUP BYwere rejected bySQLPatternParser, because its empty grouping-label set was treated as a parse failure. For example, these queries could not be planned:After this change, an omitted
GROUP BYparses as an empty grouping-label set. Single-layer globalSUM,COUNT(column),MIN,MAX,QUANTILE, andCOUNT(DISTINCT column)queries now plan and build an execution context, yielding a global result with no output grouping labels.The parser also preserves empty grouping labels within nested SQL ASTs. Nested matching and execution remain intentionally unsupported and are deferred to #819.
Tests
cargo fmt --checkcargo test -p sql_utilitiescargo test -p query_engine_rust --test sql_query_supportCloses #817