MEASURE(AGG(...)) causes 30-60s CPU spin before failing with 'Physical plan does not support logical expression'
- Dominant language
- Rust
- Stars
- 20.8k
- Forks
- 2.1k
- Avg merge
- 1d 2h
- Merged PRs (30d)
- 181
Description
## Describe the bug
When a CubeSQL query contains a nested aggregate function inside `MEASURE()` — e.g., `MEASURE(SUM(column))` — the query planner spins the CPU for **30-60 seconds** before eventually returning an error. During this time, the single-threaded Cube process is completely blocked and cannot serve any other requests.
The correct syntax is either `MEASURE(column)` or `SUM(column)` — these are alternatives, not composable. However, instead of immediately rejecting the invalid nesting with a parse/validation error, Cube attempts to build a physical plan and gets stuck in an expensive planning loop.
## Expected behavior
`MEASURE(SUM(...))` (and similar nesting like `MEASURE(COUNT(...))`, `MEASURE(AVG(...))`, etc.) should be rejected **immediately** during SQL parsing or logical plan validation — ideally in under 100ms — with a clear error message like:
> "Nested aggregate functions inside MEASURE() are not supported. Use MEASURE(column) or SUM(column), not MEASURE(SUM(column))."
## Actual behavior
Cube spins the CPU for 30-60 seconds, blocking all concurrent requests, then returns:
```
This feature is not implemented: Physical plan does not support logical expression SUM(#ItemSales.sales_quantity)
```
The `duration` field in the error log confirms the excessive time (e.g., `"duration": 64902` — nearly 65 seconds for a query that never reaches the database).
## To Reproduce
### Data model (YAML)
```yaml
cubes:
- name: ItemSales
sql: "SELECT * FROM some_table"
dimensions:
- name: item_name
sql: "{CUBE}.item_name"
type: string
- name: local_date
sql: "{CUBE}.local_date"
type: time
- name: check_state
sql: "{CUBE}.check_state"
type: string
measures:
- name: sales_quantity
sql: "CASE WHEN {CUBE}.transaction_type = 'SALE' THEN {CUBE}.quantity ELSE 0 END"
type: sum
```
### Query (via SQL API — REST or Postgres wire protocol)
```sql
SELECT
ItemSales.item_name,
MEASURE(SUM(ItemSales.sales_quantity)) AS total_quantity
FROM ItemSales
WHERE ItemSales.local_date >= '2026-01-11'
AND ItemSales.local_date <= '2026-04-10'
AND ItemSales.check_state = 'CLOSED_CHECK'
GROUP BY
ItemSales.item_name
HAVING MEASURE(SUM(ItemSales.sales_quantity)) > 0
ORDER BY total_quantity DESC
LIMIT 20
```
### Expected result
Immediate error (< 1 second) rejecting the query.
### Actual result
~30-65 second hang, then:
```json
{
"error": "This feature is not implemented: Physical plan does not support logical expression SUM(#ItemSales.sales_quantity)"
}
```
## Impact
This is an **availability/DoS concern**. Because Cube's query planner is single-threaded, a single `MEASURE(SUM(...))` query blocks all other traffic for 30-60 seconds. In our production environment, a client submitted ~34 such queries in rapid succession, causing a sustained outage where no other requests could be served.
We've added a workaround on our API gateway layer to reject these patterns with a 400 before they reach Cube, but this should ideally be handled in Cube itself — both the immediate rejection and preventing the CPU spin.
## Additional context
The table of valid aggregate functions from the [Cube SQL API docs](https://cube.dev/docs/product/apis-integrations/core-data-apis/sql-api/query-format) confirms that `MEASURE(column)` and `SUM(column)` are alternatives for `sum`-type measures. The docs do not document `MEASURE(SUM(...))` as valid syntax, and the error confirms it isn't — the issue is purely that the rejection path is catastrophically slow.
| Measure type | Valid aggregate functions |
|---|---|
| sum | `MEASURE` or `SUM` |
| count | `MEASURE` or `COUNT` |
| avg | `MEASURE` or `AVG` |
| min | `MEASURE` or `MIN` |
| max | `MEASURE` or `MAX` |
None of these support nesting (e.g., `MEASURE(SUM(...))`, `MEASURE(COUNT(...))`).
Contributor guide
Assessment
This issue has not been assessed yet.