cube-js / cube-js/cube

Cube SQL documentation when pushdown is enabled omits key functionality details

Open
#10,951 2 comments 0 reactions 0 assignees View on GitHub
docs
Dominant language
Rust
Stars
20.8k
Forks
2.1k
Avg merge
1d 2h
Merged PRs (30d)
181

Description

I used a llm to clean this issue up and expand the examples so apologies for the tone.

The [SQL API reference](https://docs.cube.dev/reference/core-data-apis/sql-api/reference) presents a single, fixed list of supported SQL functions and operators. In practice, when query pushdown is enabled, the *actually-available* set is:

1. **Larger than the documented list** (several working functions aren't on the page), and
2. **Different per database driver** (the same function works on one source and is rejected on another).

Neither the per-driver variation nor the additional functions are documented. On top of that, there's an **undocumented structural limitation**: an aggregate over a "dimension-only" expression only accepts a **single dimension reference** in that expression.

This issue is about getting these rules documented (and ideally fixing the limitation in #2 below).

## Environment

- Cube version: `v1.6.50`
- Query pushdown: enabled (default since 1.0)
- Source database: PostgreSQL (Northwind dataset)

---

## Issue 1 — The supported-function set is undocumented per-driver and incomplete

The docs imply the function list is fixed and source-agnostic. It isn't. The allowlist is assembled in code as:

- **Base list:** `packages/cubejs-schema-compiler/src/adapter/BaseQuery.js` → `sqlTemplates()` → `functions: { ... }` (~line 4478).
- **Per-driver overrides:** each dialect's `*Query.ts` mutates that map. Examples:
- `PostgresQuery.ts` **adds** `DATE_PART`, `CURRENT_DATE`, `LEAST`, `GREATEST`, … and keeps `PERCENTILE_CONT`.
- `BigqueryQuery.ts:351` **deletes** `PERCENTILECONT` (and `TO_CHAR`).
- `ClickHouseQuery.ts:270`, `MssqlQuery.ts:276` **delete** `PERCENTILECONT`.

A function only resolves under pushdown if a `functions/` template exists for that data source (enforced in `rust/cubesql/.../compile/rewrite/rules/wrapper/mod.rs:212`, `can_rewrite_template`). There is no catch-all passthrough.

**Concrete example — works on Postgres but is NOT on the docs page:**

```sql
SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY OrderDetails.order_id) AS p50_num
FROM OrderDetails;
```

`PERCENTILE_CONT` does not appear in the SQL API reference, yet it pushes down successfully on Postgres. The **same query fails** on BigQuery / ClickHouse / MSSQL because those drivers remove the template.

Other functions that are templated (at least for Postgres) but absent from the reference page include: `STRING_AGG`, `LAG`, `LEAD`, `DATE_PART`, `CURRENT_DATE`.

**Request:** Document (a) that the supported-function set varies by data source, and (b) the actual per-driver set — or at minimum state that the reference list is a *baseline* and link to the driver-specific additions/removals.

---

## Issue 2 — Aggregates over a "dimension-only" expression only accept a single dimension (undocumented + appears to be a bug)

The query above works because the `ORDER BY` references **one** dimension. Add a second dimension reference and it fails:

```sql
-- FAILS
SELECT PERCENTILE_CONT(0.5)
WITHIN GROUP (ORDER BY OrderDetails.discount + OrderDetails.quantity) AS p50_num
FROM OrderDetails;
```

```
SQLCompilationError: Internal: Expected single cube for dimension-only measure
expr:OrderDetails.percentilecont_f, got ["OrderDetails", "OrderDetails"]
```

### Root cause (Tesseract planner)

The expression's children are two dimensions, so it's treated as a "dimension-only measure expression." The planner collects the cube name of **every child without deduplicating**:

`rust/cube/cubesqlplanner/.../planner/symbols/member_expression_symbol.rs:168`
```rust
let cube_names = childs
.into_iter()
.map(|child| child.cube_name()) // one entry per child
.collect_vec(); // no .unique()
```

Then `rust/cube/cubesqlplanner/.../planner/collectors/multiplied_measures_collector.rs:127` only accepts 0 or 1 cube names and errors otherwise:

```rust
} else if cube_names.len() == 1 {
...
} else {
return Err(CubeError::user(format!(
"Expected single cube for dimension-only measure {}, got {:?}", ...)));
}
```

So `discount + quantity` yields `["OrderDetails", "OrderDetails"]` and trips the `len() == 1` guard — **even though both references are the same cube**. This looks like a missing dedup: deduplicating `cube_names` (or comparing distinct cubes) before the check would let same-cube multi-dimension expressions through.

### Impact

You cannot run an ad-hoc aggregate over an arithmetic combination of two member columns; the combined expression must be pre-defined as a single dimension in the model, e.g.:

```yaml
dimensions:
- name: num
sql: "{CUBE}.discount + {CUBE}.quantity"
type: number
```
```sql
SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY OrderDetails.num) AS p50_num
FROM OrderDetails; -- works
```

**Request:** Either (a) fix the dedup so same-cube multi-dimension aggregate expressions are allowed, and/or (b) document that aggregate expressions over dimensions must reference a single dimension and that combined expressions must be modeled as a dimension.

---

## Expected vs. actual

| | Expected (from docs) | Actual |
|---|---|---|
| Supported function set | Fixed list on the reference page | Larger, and varies per database driver |
| `PERCENTILE_CONT` | Not listed → assume unsupported | Works on Postgres/Snowflake; rejected on BigQuery/ClickHouse/MSSQL |
| Aggregate over `dim_a + dim_b` | No guidance | Fails with an internal "Expected single cube" error |

## Suggested documentation additions

1. State that the SQL API function list is a **baseline**, and that pushdown exposes additional functions **subject to the data source driver**.
2. Provide (or link to) the **per-driver** added/removed functions.
3. Document the **single-dimension constraint** for aggregate-over-dimension expressions, with the recommended workaround (model the expression as a dimension).

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.