From 1f80d5e21f0960685776c2f9a82b7f497e06e698 Mon Sep 17 00:00:00 2001 From: Egor Kraev Date: Sat, 3 Oct 2026 14:48:25 +0200 Subject: [PATCH 01/11] OpenSpec change for rank-family ordering: required direction on rank/dense_rank, ascending ntile/percent_rank, NULL inputs rank NULL, lazy stored-only migration --- .../.openspec.yaml | 2 + .../design.md | 115 +++ .../proposal.md | 64 ++ .../aggregations/functional-form/spec.md | 67 ++ .../specs/queries/computed-dimensions/spec.md | 203 +++++ .../specs/queries/measure-naming/spec.md | 43 + .../queries/partitioned-aggregates/spec.md | 834 ++++++++++++++++++ .../specs/queries/semantics/spec.md | 231 +++++ .../specs/queries/transforms/spec.md | 352 ++++++++ .../tasks.md | 116 +++ 10 files changed, 2027 insertions(+) create mode 100644 openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/.openspec.yaml create mode 100644 openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/design.md create mode 100644 openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/proposal.md create mode 100644 openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/aggregations/functional-form/spec.md create mode 100644 openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/computed-dimensions/spec.md create mode 100644 openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/measure-naming/spec.md create mode 100644 openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/partitioned-aggregates/spec.md create mode 100644 openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/semantics/spec.md create mode 100644 openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/transforms/spec.md create mode 100644 openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/tasks.md diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/.openspec.yaml b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/.openspec.yaml new file mode 100644 index 00000000..6a87d2b2 --- /dev/null +++ b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-10-03 diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/design.md b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/design.md new file mode 100644 index 00000000..9fb388fa --- /dev/null +++ b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/design.md @@ -0,0 +1,115 @@ +## Context + +- Migrations are dict→dict steps run by each persisted class's `mode="before"` validator + (`slayer/storage/migrations.py`, `migrate()`), so they run on every `model_validate`. + `migrate()` treats a missing `version` as v1. Fresh agent and API payloads carry no + `version`, so today every registry step also runs on fresh input. +- Stored `SlayerModel` documents load through `StorageBackend._migrate_and_refine_on_load` + (`slayer/storage/base.py`), which writes a migrated document back. Memories load + directly through `Memory.model_validate` in `yaml_storage._md_to_memory` and the + `sqlite_storage` row readers. Stored queries live nested in `source_queries`, in an + inline-query `source_model`, and in `Memory.query`. Each carries its own `version`. +- Two parsers read transform kwargs: `core/formula.py` `_parse_transform_kwargs` + (importer validation only; its retirement is DEV-1831) and `engine/binding.py` + `_bind_transform_params`. +- The window is emitted in `sql/generator.py` (`rank_order`, `_over`). The NULL position + comes from `build_ordered`'s dialect default, which on T-SQL is NULLs first on `ASC`. +- `StorageBackend.save_model` does not parse measure formulas (DEV-2043 tracks + save-time validation). +- Normative: `architecture/system.arc42.md` §3.11 (versioned persistence, migrations run + on load), §3.10 (two expression layers: only Mode-B text is DSL), §3.2 (`core` + imports no other node), §3.6 (SQL built as sqlglot AST); `storage.arc42.md` §3.1. + +## Goals / Non-Goals + +**Goals:** +- One `direction` rule that both parsers obey, so they cannot drift. +- Lazy migration that fills in only legacy documents and can never fill in a fresh + payload. +- NULL semantics that do not depend on the dialect. + +**Non-Goals:** +- Merging the two parsers (DEV-1831). +- Save-time formula validation (DEV-2043). +- Rewriting stored references to old auto-named rank result keys. +- Migrating `ntile` / `percent_rank` meaning, which no rewrite preserves exactly. + +## Decisions + +1. **Stored-only migration steps.** + - What: `register_migration(entity, source_version, stored_only=True)`. In `migrate()`, + note once whether the incoming dict carried an explicit `version`. A stored-only step + runs only if it did. Otherwise the version still advances past it. + - Why: a missing `version` cannot tell a v1 document from a fresh payload. An explicit + `version` can, because every persisted document stamps one. It is the generalised form + of the `strict` retirement precedent in `SlayerQuery._migrate_and_rewrite`. + - Alternative rejected: rewriting only in the storage load path (the v10/v11 + precedent). It needs a hand-rolled walk of nested queries and two memory hooks, and + any future direct `model_validate(stored_dict)` would silently miss it. +2. **Stamping legacy documents.** + - `_migrate_and_refine_on_load` and the two memory load sites set `version: 1` on a + stored dict that has none. + - The `SlayerModel` v12→v13 step and the `Memory` v2→v3 step (both stored-only) stamp + `version: 1` on each unversioned nested query dict before it validates: every + `source_queries` entry, recursively through inline-query `source_model`s, and + `Memory.query`. + - The `SlayerQuery` v4→v5 step stamps its own unversioned inline `source_model` query + the same way. + - A payload that declares an explicit old version is legacy by its own declaration + and is filled in (Codex review finding 1, rejected with this rationale). +3. **The rewrite is token-level, not AST.** + - One stdlib-`tokenize` function in `storage` inserts `, direction='desc'` before the + matching `)` of each `rank(` / `dense_rank(` NAME token that is not preceded by `.` + and has no top-level `direction` keyword. + - Why not AST: `ast.unparse` would drop colon syntax and the user's formatting. + - Strings and comments are skipped by construction. + - A `tokenize.TokenError` leaves the text byte-identical. Any formula the DSL accepts + tokenises, so such a formula is already broken and still fails loudly when queried. + Failing the migration instead would make the model unloadable for repair (Codex + finding 6, rejected). + - Applied to: `measures[].formula` for models; for queries, string or dict + `measures` (`formula`), `filters`, `dimensions` (string or `expression`), + `time_dimensions` (string or `dimension`), `order[].column`, `main_time_dimension`. +4. **One direction rule in core.** + - A core function validates a rank-family call's `direction`: required for + `rank` / `dense_rank`, forbidden for `ntile` / `percent_rank`, string-literal only. + It normalises through the shared synonym table, which moves out of `core/query.py` + and which `OrderItem` keeps using. It raises `TransformArgumentError`. + - Both parsers call it. The binder stores `("direction", "asc"|"desc")` in + `TransformKey.kwargs`, so asc and desc intern separately. + - The binder's other transform-kwarg `ValueError`s move onto `TransformArgumentError`, + which is backwards compatible since `QueryTypeError` subclasses `ValueError`. +5. **NULL→NULL emission.** + - Shape: + `CASE WHEN v IS NULL THEN NULL ELSE fn() OVER (PARTITION BY , CASE WHEN v IS NULL THEN 1 ELSE 0 END ORDER BY v ) END`, + built as a sqlglot AST. + - The integer flag (not a boolean predicate) keeps `PARTITION BY` valid on T-SQL. + - The NULL rows sit in their own window, so the NULL ordering inside the window no + longer matters on any dialect. + - The same shape is used for all four functions, even though `rank` / `dense_rank` + alone would only need NULLs-last ordering. +6. **Naming.** The canonical formula text renders `direction` as its bare value, so the + sanitiser yields `rank_a_sum_desc`. Other kwargs keep `name_value`. + +## Risks / Trade-offs + +- [Unnamed rank keys change; downstream references to old keys break] → Accepted by the + user; release notes list it. +- [`ntile` / `percent_rank` results flip, and NULL inputs now yield NULL, with no + migration] → Release notes. `rank(...) <= N` filters now drop NULL-inner rows. +- [Explicit-version payloads from API clients are treated as legacy] → Intended; covered + by a scenario. +- [The CASE wrapper adds noise to golden SQL for non-nullable inners such as `count(*)`] + → Accepted for one uniform shape; goldens are re-blessed. +- [Very old unversioned queries reachable through some path not covered by stamping] + → They fail loudly with the typed error that names both spellings. + +## Migration Plan + +- `CURRENT_VERSIONS` goes to `SlayerModel` 13, `SlayerQuery` 5, `Memory` 3. All three new + steps are stored-only. +- Model documents are written back on first load. Memories and nested queries are + persisted with `direction` the next time they are saved. +- Rollback: older code reading a v13 / v5 document best-effort-loads it, but its parser + rejects the unknown `direction` keyword. Downgrading after migration therefore needs the + documents restored from backup. diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/proposal.md b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/proposal.md new file mode 100644 index 00000000..e561b390 --- /dev/null +++ b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/proposal.md @@ -0,0 +1,64 @@ +## Why + +The rank family always orders by the inner value descending, with no way to choose. An +agent asked for "the cheapest ACI" wrote `rank(total_fees) <= 1` and silently got the +most expensive one. The only workaround, `rank(-x)`, works only for numbers, so +"earliest" or "alphabetically first" cannot be ranked from the bottom at all. + +## What Changes + +- **BREAKING** `rank` and `dense_rank` take a required keyword `direction='asc' | 'desc'` + (synonyms `ascending` / `descending`, any case — the words of `OrderItem.direction`). + Omitting it is a typed `TransformArgumentError` that shows both spellings; there is + no default. +- **BREAKING** `ntile` and `percent_rank` reject `direction` and order ascending: + quartile 1 is the lowest, and a higher value gets a higher percent rank. This flips + today's descending order and is not migrated. +- **BREAKING** For all four, a NULL inner value gives a NULL result, and NULL rows take + no rank position, no bucket and no share of `percent_rank`'s denominator. The result + is identical on every dialect. +- **BREAKING** An unnamed rank's derived result key spells its direction as a bare + value (`rank_a_sum_desc` / `rank_a_sum_asc`); existing unnamed rank keys change. +- Stored artifacts migrate lazily on load: a bare `rank(` / `dense_rank(` in a + persisted model, query or memory older than this change gains `direction='desc'`, + keeping its meaning. Fresh payloads are never filled in. The migration registry gains + a general stored-only step kind for this. +- One core rule for `direction`, shared by the query binder and the importer formula + validator. +- Docs, examples and agent-facing text (the `query` tool's description, error + suggestions) spell the direction. + +## Capabilities + +### New Capabilities + +(none) + +### Modified Capabilities + +- `queries/transforms`: adds the ordering-direction, NULL-input and stored-rank + migration requirements; existing rank scenarios spell `direction` and their + NULL-inner values become NULL. +- `queries/measure-naming`: the derived key of an unnamed rank spells its direction as + a bare value. +- `queries/partitioned-aggregates`: rank scenarios spell `direction`; values for the + all-NULL Void cell become NULL. +- `queries/computed-dimensions`: rank scenarios spell `direction`; the Void cell's rank + becomes NULL. +- `queries/semantics`: rank scenarios spell `direction`. +- `aggregations/functional-form`: rank scenarios spell `direction`. + +## Impact + +- `slayer/core` (shared direction rule, `TransformArgumentError`, `OrderItem` synonym + table), `slayer/core/formula.py` (importer validator), `slayer/engine/binding.py` + (binder), `slayer/sql/generator.py` (window emission), the result-key naming of + rank-family transforms. +- `slayer/storage/migrations.py` and new migration steps (`SlayerModel` v13, + `SlayerQuery` v5, `Memory` v3), plus version stamping on the model and memory load + paths in `slayer/storage/base.py`, `yaml_storage.py` and `sqlite_storage.py`. +- `slayer/mcp/server.py`, `slayer/sql/window_detect.py`, `slayer/core/errors.py` + agent-facing text; `docs/`, `docs/examples/` notebooks, `examples/`. +- About 98 test files that use rank-family formulas; golden SQL baselines. +- Save-time validation of formulas is out of scope (DEV-2043): a bare `rank` saved + after this change fails when queried. diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/aggregations/functional-form/spec.md b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/aggregations/functional-form/spec.md new file mode 100644 index 00000000..b08a58c0 --- /dev/null +++ b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/aggregations/functional-form/spec.md @@ -0,0 +1,67 @@ +## MODIFIED Requirements + +### Requirement: Positional parameters fold onto declared parameter order +An aggregation call MAY pass declared parameters positionally after its source +(or, for a re-aggregation, after its operand): positional values SHALL bind to +the aggregation's declared parameter order — the built-in registry +(`percentile` → `p`; `weighted_avg` → `weight`; `corr`/`covar_samp`/`covar_pop` +→ `other`) or a custom aggregation's `params` declaration order — yielding the +identical aggregation identity, SQL, results, and result keys as the named +spelling. Passing a parameter both positionally and by name, or more positional +values than declared parameters — any positional value at all on an aggregation +that declares none — SHALL fail with a clear error naming the rule. Ranked +`first`/`last` declare no parameters — they take at most one positional value, +their ranking column (the time axis when omitted), which, when given, SHALL be a +column reference, never a literal or an attached value. After binding an +attached (aggregate- or transform-valued) parameter is therefore always a named +parameter. + +#### Scenario: Positional percentile equals named +- **WHEN** a measure is written `percentile(price, 0.9)` or `price:percentile(0.9)` +- **THEN** SQL, results, and result keys are identical to the `p=0.9` spellings + +#### Scenario: Positional parameter on a re-aggregation outer +- **WHEN** a measure is written + `percentile(sum(amount, partition_by=[city, region]), 0.9)` +- **THEN** it equals the `p=0.9` spelling by executed values + +#### Scenario: Custom aggregation binds positionals by declared order +- **WHEN** a model declares `wavg(weight)` and a measure is written `wavg(amount, id)` +- **THEN** it equals `wavg(amount, weight=id)` by executed values + +#### Scenario: Duplicate and excess positional parameters error +- **WHEN** `percentile(price, 0.9, p=0.5)` or `percentile(price, 0.9, 0.5)` is submitted +- **THEN** each fails with a clear error naming the duplicated parameter or the + declared-parameter count + +#### Scenario: Positional value on a parameterless aggregation errors +- **WHEN** `sum(amount, 1)` or `customers.spend:sum(sum(customers.spend, partition_by=status))` + is submitted, under any `to_many_handling` mode +- **THEN** each fails at bind with a clear error naming the aggregation and that it + takes no parameters — never an executed value + +#### Scenario: Invalid first/last ranking key errors +- **WHEN** `last(amount, sum(amount, partition_by=region))`, `last(amount, 1)` or + `last(amount, id, amount)` is submitted +- **THEN** each fails at bind with a clear error naming the ranking-column rule + +#### Scenario: Positional transform parameter equals named +- **WHEN** a measure is written + `customers.spend:weighted_avg(rank(sum(amount, partition_by=customers.regions.name), direction='desc'))` + rooted at `orders` +- **THEN** it binds to the identical aggregation identity as the `weight=rank(..., direction='desc')` + spelling and returns identical result keys and values + +### Requirement: Repeated keyword arguments are rejected +A call in a Mode-B expression — an aggregation in functional or colon spelling, or a +transform — SHALL reject a keyword argument that appears more than once with a +parse-time error naming the call and the keyword; the parser never keeps the last +occurrence and never concatenates the values. + +#### Scenario: Repeated partition_by on a transform +- **WHEN** a measure names `rank(sum(amount), partition_by=region, partition_by=city, direction='desc')` +- **THEN** parsing fails with an error naming `rank` and `partition_by` + +#### Scenario: Repeated keyword on an aggregation +- **WHEN** a measure names `sum(amount, partition_by=region, partition_by=city)` or `amount:sum(partition_by=region, partition_by=city)` +- **THEN** parsing fails with an error naming the aggregation and `partition_by` diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/computed-dimensions/spec.md b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/computed-dimensions/spec.md new file mode 100644 index 00000000..f5966aac --- /dev/null +++ b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/computed-dimensions/spec.md @@ -0,0 +1,203 @@ +## MODIFIED Requirements + +### Requirement: Measure-dimension symmetry with grain self-containment +Any measure-legal expression SHALL be legal as a computed dimension provided it is grain-self-contained: every aggregate in it carries an explicit `partition_by=` whose keys are attributable from that aggregate's root (local or cross-model alike, over provably many-to-one join hops), and every transform in it applies within such an explicitly-grained subexpression. Once declared, a computed dimension behaves everywhere as a plain dimension: it can be grouped by, banded, filtered on, ordered by, and used as a transform partition. + +#### Scenario: Banded partitioned aggregate as a dimension +- WHEN a query declares the dimension `CASE WHEN amount:sum(partition_by=city) > 5000 THEN 'high' ELSE 'low' END` +- THEN rows group by the band, measures aggregate within each band, and executed values are correct + +#### Scenario: Expression over two different partition sets +- WHEN a dimension expression combines `x:sum(partition_by=region)` and `y:sum(partition_by=country)` arithmetically +- THEN each aggregate is computed at its own declared grain and the expression is evaluated per row over the two attached values + +#### Scenario: Cross-model aggregate source in a dimension expression +- WHEN a dimension expression bands an aggregate whose source crosses a join (e.g. `customers.spend:sum(partition_by=)`) +- THEN rows group by the band with correct executed values and unchanged cardinality + +#### Scenario: Used as a transform partition +- WHEN a query declares the computed dimension `ureg` = `upper(region)` and selects `rank(sum(amount), partition_by=ureg, direction='desc')` +- THEN the transform partitions by the dimension's value exactly as `sum(amount, partition_by=ureg)` would, in the measure, aggregation-parameter, filter, order and computed-dimension positions (values per `queries/partitioned-aggregates` › Transform partition keys bind like aggregate partition keys) + +### Requirement: Transforms inside dimension expressions +A transform inside a dimension expression SHALL evaluate at the union of its inner aggregates' effective grains — the grain of its containing context — unlike the same expression used as a measure, which evaluates at the query grain. An inner aggregate's effective grain is its declared `partition_by=` set, plus the query's active time bucket when the aggregate is windowed (`window=`); a `first`/`last` inner aggregate contributes its declared partition set only. Each inner aggregate is computed at its own effective grain and broadcast to the union-grain rows; when all inner aggregates share one grain the union degenerates to that grain (behavior unchanged). The rule is recursive: a nested transform evaluates at the union of its OWN inner aggregates' grains and its result is broadcast into the containing union like any other grained value. Keyword references on the transform (e.g. an explicit `partition_by=`) SHALL resolve against the union grain. A time-ordered transform (e.g. `cumsum`, `lag`, `time_shift`) inside a dimension expression SHALL fail with a clear error when its evaluation grain does not contain its time-ordering key — never duplicated result rows. When a windowed inner aggregate contributes the active time bucket, that synthesized bucket IS the query's bucketed time dimension — one dimension for all grain purposes (union membership, deduplication, attachment keys) — and a mixed-grain transform with a windowed inner aggregate but no resolvable time dimension SHALL fail with the same time-resolution error as windowed measures; single-grain windowed and `first`/`last` transform inputs remain legal. + +#### Scenario: Rank of partitions as a bandable dimension +- WHEN a query declares the dimension `rank(revenue:sum(partition_by=region), direction='desc')` +- THEN each row carries its region's rank among all regions by total revenue, and grouping or banding by that rank is legal and correct + +#### Scenario: Context grain distinguishes dimension use from measure use +- WHEN `rank(revenue:sum(partition_by=region), direction='desc')` is used once as a dimension and once as a measure in otherwise identical queries +- THEN the dimension form ranks regions at region grain while the measure form ranks result rows at query grain + +#### Scenario: Different grains in one transform union and broadcast +- WHEN a dimension expression applies a transform over an arithmetic of two aggregates at different partition grains (e.g. `rank(a:sum(partition_by=region) - b:sum(partition_by=city), direction='desc')`) +- THEN each aggregate is computed at its own declared grain, both are broadcast to the (region, city) union rows, the transform evaluates over exactly those rows, and executed values are correct + +#### Scenario: Keyless grain in a mixed transform +- WHEN a dimension expression ranks a share-of-total, e.g. `rank(amount:sum(partition_by=region) / amount:sum(partition_by=[]), direction='desc')` +- THEN the overall total broadcasts to every region row, the ratio and rank evaluate per region, and executed values are correct + +#### Scenario: A subset grain computes at its own grain +- WHEN a mixed-grain transform combines an aggregate at the union grain with one at a strictly coarser grain (e.g. `rank(a:sum(partition_by=[region, city]) - a:sum(partition_by=region), direction='desc')`) +- THEN the union-grain aggregate is computed directly at the union grain while the coarser one is computed at its own grain and broadcast, and executed values are correct + +#### Scenario: Nested transform evaluates at its own grain +- WHEN a mixed-grain transform contains a nested transform over a strictly coarser grain (e.g. `rank(cumsum(a:sum(partition_by=[region, ordered_at])) - b:sum(partition_by=city), direction='desc')`) +- THEN the inner transform evaluates over its own grain's rows (the cumulative sum accumulates across that grain's time buckets, not across union rows) before broadcasting into the union, and executed values are correct + +#### Scenario: Temporal transform without its time axis in the grain fails cleanly +- WHEN a dimension expression applies a time-ordered transform over aggregates whose union grain lacks the transform's time-ordering key (e.g. `cumsum(amount:sum(partition_by=[region, city]))` in a query with a monthly time dimension) +- THEN the query fails with a clear error directing the author to include the time key in `partition_by`, and never returns duplicated rows + +#### Scenario: Explicit transform partition over union rows +- WHEN a mixed-grain transform declares `partition_by=` naming a key of the union grain (e.g. `rank(a:sum(partition_by=region) - b:sum(partition_by=city), partition_by=region, direction='desc')`) +- THEN the transform partitions the union-grain rows by the declared key, and executed values are correct + +#### Scenario: Transform keyword outside the union grain fails cleanly +- WHEN a mixed-grain transform declares `partition_by=` naming a key not in the union grain +- THEN the query fails with a clear reference error, not an internal producer-slot error + +#### Scenario: Union attach is cardinality-neutral on the complete union grain +- WHEN a query runs with and without a mixed-grain transform dimension +- THEN the attach joins on the complete union grain, and both runs return the same rows with identical values in all shared columns + +#### Scenario: Same mixed-grain transform as dimension and measure in one query +- WHEN the same mixed-grain transform expression appears both as a dimension and as a measure +- THEN the dimension form evaluates at the union grain, the measure form at the query grain, and both are correct in one result + +#### Scenario: Different grains in one transform are deferred, not misgrained +- WHEN a mixed-grain transform's inner aggregates include a `window=` or `first`/`last` aggregation at a different grain than a sibling aggregate +- THEN the query is no longer deferred: each aggregate is computed at its own effective grain (a windowed one contributing the active time bucket) and broadcast to the union rows, returning correct executed values — never a misgrained value and never the former DEV-1835 not-yet-supported error + +#### Scenario: A windowed inner aggregate contributes the time bucket to the union +- WHEN a dimension expression applies a transform over `a:sum(window='90d', partition_by=region) - b:sum(partition_by=region)` in a query with a month time dimension +- THEN the union grain is (region, month bucket), the plain region total broadcasts across the region's buckets, and executed values are correct + +#### Scenario: First/last inner aggregate mixes with a different-grain sibling +- WHEN a dimension expression applies a transform over `a:last(partition_by=region) - b:sum(partition_by=city)` +- THEN the union grain is (region, city), each value broadcasts from its own grain, and executed values are correct + +#### Scenario: Windowed inner aggregate without a resolvable time dimension fails +- WHEN such a transform-in-dimension contains a windowed inner aggregate but the query has no resolvable time dimension +- THEN the query fails with the same clear time-resolution error as windowed measures + +### Requirement: Computed dimensions coexist with transform measures +A grain-self-contained computed dimension (one whose aggregates all carry explicit `partition_by=`) SHALL be legal in the same query as transform measures — `time_shift`, `change`, `change_pct`, `cumsum`, `lag`, `lead`, `consecutive_periods`, and rank-family transforms of a measure — alone and alongside plain and partitioned measures, with correct executed values and unchanged result cardinality. + +#### Scenario: Banded dimension with a time-shift measure +- WHEN a query groups by a dimension banding `amount:sum(partition_by=city)` and selects `time_shift(amount:sum, periods=-1)` over a month time dimension +- THEN each row carries the previous month's total for its (band, other-dimension) group, and the band values match the same query without the transform measure + +#### Scenario: Banded dimension with change and change_pct +- WHEN the same banded dimension is combined with `change(amount:sum)` or `change_pct(amount:sum)` +- THEN the derived values equal the hand-computed difference (or ratio) between the group's bucket and its previous bucket + +#### Scenario: Banded dimension with a running total +- WHEN the same banded dimension is combined with `cumsum(amount:sum)` +- THEN each row carries the running total accumulated within its (band, other-dimension) group across time buckets + +#### Scenario: Bare partitioned aggregate as a dimension with a transform measure +- WHEN a query groups directly by `amount:sum(partition_by=city)` as a dimension and selects a transform measure +- THEN the query executes with correct values for both + +#### Scenario: Transform-root dimension with a transform measure +- WHEN a query groups by `rank(amount:sum(partition_by=city), direction='desc')` as a dimension and selects a transform measure +- THEN the producer-grain rank and the query-grain transform are both correct in one result + +#### Scenario: Alongside a partitioned measure +- WHEN a computed dimension over a partitioned aggregate, a partitioned measure (`partition_by=`), and a transform measure appear in one query +- THEN all three are correct by executed values, each equal to its value when queried alone + +#### Scenario: Adding a transform measure is cardinality-neutral +- WHEN a query with an aggregation-derived dimension (banded, bare, or transform-root) runs with and without an additional transform measure +- THEN both runs return the same rows and identical values in all shared columns + +### Requirement: Aggregation-derived dimensions coexist with windowed and ranked measures +An aggregation-derived dimension (banded, bare partitioned aggregate, or transform-root) SHALL be legal in the same query as bare windowed (`window=` without `partition_by=`) and bare `first`/`last` measures, with correct executed values, unchanged result cardinality, and each measure equal to its value when queried alone. + +#### Scenario: Banded dimension with a bare windowed measure +- WHEN a query groups by a dimension banding `amount:sum(partition_by=city)` and selects `amount:sum(window='1y')` over a month time dimension +- THEN both the band and the rolling total are correct by executed values in one result + +#### Scenario: Bare partitioned aggregate as a dimension with a bare last measure +- WHEN a query groups directly by `amount:sum(partition_by=city)` as a dimension and selects `amount:last` +- THEN the query executes with correct values for both + +#### Scenario: Transform-root dimension with a bare windowed or ranked measure +- WHEN a query groups by `rank(amount:sum(partition_by=city), direction='desc')` as a dimension and selects a bare windowed or bare `first`/`last` measure +- THEN the producer-grain rank and the measure are both correct in one result + +#### Scenario: Adding a bare windowed or ranked measure is cardinality-neutral +- WHEN a query with an aggregation-derived dimension runs with and without an additional bare windowed or `first`/`last` measure +- THEN both runs return the same rows and identical values in all shared columns + +#### Scenario: A dual-role aggregate coexists with a bare windowed measure +- WHEN the same partitioned aggregate appears inside a computed dimension and as a selected measure, alongside a bare windowed measure +- THEN all three values are correct and the dimension's grain treatment of the shared aggregate is unaffected by its measure role + +### Requirement: An aggregate expression shared by a computed dimension and another position evaluates per position +When the same explicitly grained aggregate expression — a partitioned aggregate, a re-aggregation, a transform over them, or a cross-model re-aggregation — appears inside a computed dimension AND in another position of the same query (measure, measure-typed filter conjunct, order target), each occurrence SHALL evaluate as that position defines it: inside the dimension at row scope, broadcast onto the rows its grain determines; elsewhere at query grain (Axiom 13). The shared expression SHALL be computed once (one producer) and the query SHALL execute with correct values or fail with a typed query error — never an internal placeholder, materialisation, hidden-slot, name-collision or join-back error. Filtering or ordering by the computed dimension's NAME uses its banded output; filtering or ordering by the underlying expression uses the expression's value. + +Oracles below use the DEV-1847 `sales` fixture with `R` = `avg(sum(amount, partition_by=[city, region]), partition_by=region)` (North 45, South 70, East 60, Gap 10, Void NULL), `rlevel` = `CASE WHEN R > 50 THEN 'hi' ELSE 'lo' END`, `tlevel` = `CASE WHEN rank(R, direction='desc') > 1 THEN 'top' ELSE 'rest' END`, and `tot` = `amount:sum`. + +#### Scenario: Re-aggregation in a dimension and a measure-typed filter +- WHEN a query over dimensions `[region, rlevel]` selects `tot` and filters on `R < amount:sum` +- THEN the rows are East/hi 180, Gap/lo 20, North/lo 90, South/hi 140 (Void's NULL fails the predicate), and the filter `R > amount:sum` returns zero rows without error + +#### Scenario: Re-aggregation in a dimension and an order target +- WHEN the same query is ordered by `R` descending +- THEN rows arrive South, East, North, Gap, then Void + +#### Scenario: Re-aggregation in a dimension and a measure +- WHEN the same query also selects `R` as a measure +- THEN `R` per row equals the per-region values above and `rlevel` agrees with it + +#### Scenario: Transform over a re-aggregation in a dimension +- WHEN a query over dimensions `[region, tlevel]` selects `tot` +- THEN South and Void (NULL rank) are `rest` and East, North and Gap are `top` + +#### Scenario: Transform over a re-aggregation in a dimension and elsewhere +- WHEN the `tlevel` query also selects `rank(R, direction='desc')` as a measure, or filters on `rank(R, direction='desc') > 1` or `rank(R, direction='desc') < 4`, or orders by `rank(R, direction='desc')` ascending +- THEN the rank values are South 1, East 2, North 3, Gap 4, Void NULL on every supported dialect; `rank(R, direction='desc') > 1` keeps East, North, Gap; `rank(R, direction='desc') < 4` keeps South, East, North; and the ascending order is South, East, North, Gap, with Void's NULL rank sorting per the dialect's NULL ordering (last outside T-SQL) + +#### Scenario: Two dimensions sharing a re-aggregation +- WHEN a query declares both `tlevel` and `rlevel` as dimensions and selects `tot` and `R` +- THEN the rows are South (rest, hi), East (top, hi), North (top, lo), Gap (top, lo), Void (rest, lo) with `tot` unchanged, and no internal name reaches the user + +#### Scenario: Ordering by a transform shared with a dimension +- WHEN a computed dimension is `CASE WHEN rank(amount:sum(partition_by=region), direction='desc') > 1 THEN 'top' ELSE 'rest' END` and the query orders by `rank(amount:sum(partition_by=region), direction='desc')` ascending +- THEN rows arrive East, South, North, Gap, with Void's NULL rank sorting per the dialect's NULL ordering (last outside T-SQL), and ordering by the dimension's name instead sorts by its banded value + +#### Scenario: Windowed transform over a re-aggregation in a dimension with a filter +- WHEN a monthly query declares the dimension `cumsum(min(, partition_by=region))` and filters on that dimension +- THEN it fails at plan time with the `TimeAxisError` naming `cumsum`, exactly as the same transform over `amount:sum(partition_by=region)` does + +#### Scenario: Cross-model re-aggregation in a dimension and a measure-typed filter +- WHEN a `corders` query over dimensions `[customers.regions.name, cl]`, with `cl` banding `avg(sum(amount, partition_by=customer_id), partition_by=customers.regions.name)` at 40, filters on that re-aggregation `< amount:sum` +- THEN the only row is North/lo with `amount:sum` 70 + +### Requirement: Expressions over a computed dimension's whole aggregate evaluate as the dimension's value +When a computed dimension's whole expression is a partitioned aggregate, a re-aggregation or a transform, that expression — alone or inside arithmetic or a scalar call — in measure position (partitioned aggregate, re-aggregation) or order position (all three) SHALL evaluate as the dimension's value per result cell and execute on every supported dialect, never with an internal placeholder, render or partition-key error. The shared aggregate SHALL be computed once and attached once. + +Oracles use the DEV-1847 `sales` fixture with `P` = `amount:sum(partition_by=region)` (North 90, South 140, East 180, Gap 20, Void NULL), `R` = `avg(sum(amount, partition_by=[city, region]), partition_by=region)` (North 45, South 70, East 60, Gap 10, Void NULL) and `tot` = `amount:sum`. + +#### Scenario: Arithmetic over a partitioned-aggregate dimension as a measure +- **WHEN** a query over dimensions `[region, rd]`, with `rd` = `P`, declares the measures `P + 1` and `P * 2 + amount:sum` +- **THEN** `P + 1` is North 91, South 141, East 181, Gap 21, Void NULL and `P * 2 + amount:sum` is North 270, South 420, East 540, Gap 60, Void NULL + +#### Scenario: Arithmetic over a re-aggregation dimension as a measure +- **WHEN** a query over dimensions `[region, rd]`, with `rd` = `R`, declares the measures `R + 1` and `R * 2 + amount:sum` +- **THEN** `R + 1` is North 46, South 71, East 61, Gap 11, Void NULL and `R * 2 + amount:sum` is North 180, South 280, East 300, Gap 40, Void NULL + +#### Scenario: Arithmetic over an aggregate dimension as an order target +- **WHEN** the `rd` = `P` query orders by `P + 1` descending, and the `rd` = `R` query orders by `R + 1` descending +- **THEN** the first arrives East, South, North, Gap, Void and the second South, East, North, Gap, Void + +#### Scenario: Arithmetic over a transform dimension as an order target +- **WHEN** a query over dimensions `[region, rk]`, with `rk` = `rank(P, direction='desc')`, orders by `rank(P, direction='desc') + 1` descending +- **THEN** rows arrive in descending `rk` order + +#### Scenario: A finer-grained aggregate dimension read as a measure +- **WHEN** a query over dimensions `[region, x]`, with `x` = `amount:sum(partition_by=[city, region])`, declares the measures `amount:sum(partition_by=[city, region])` and `amount:sum(partition_by=[city, region]) + 1` +- **THEN** on every row the first equals `x` and the second equals `x` plus 1 diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/measure-naming/spec.md b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/measure-naming/spec.md new file mode 100644 index 00000000..cbf6ca7b --- /dev/null +++ b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/measure-naming/spec.md @@ -0,0 +1,43 @@ +## MODIFIED Requirements + +### Requirement: Unnamed formula measures derive sanitized identifier keys + +The result-column key of an unnamed measure whose formula is not a plain +aggregate reference (an arithmetic composite, a transform, or a mix with +literals) SHALL be derived by sanitizing the canonical formula text into a bare +identifier under the product-wide expression-name convention: lowercase, every +run of non-alphanumeric characters collapsed to one `_`, leading/trailing `_` +stripped, a leading digit guarded, names over 48 characters folded to +`__`, and no `__` in the result. The SQL projection alias +SHALL use the same derived name. In the canonical formula text a rank-family +`direction` SHALL appear as its bare normalised value (`asc` / `desc`), never as +`direction=...`. + +#### Scenario: Arithmetic composite + +- **WHEN** an unnamed measure `logo_churn:sum / logo_bop:sum` is queried on model `mart` +- **THEN** its result key is `mart.logo_churn_sum_logo_bop_sum` + +#### Scenario: Transform formula + +- **WHEN** an unnamed measure `time_shift(cmrr_eop:sum, -1, 'year')` is queried on model `mart` +- **THEN** its result key is `mart.time_shift_cmrr_eop_sum_1_year` + +#### Scenario: Rank direction spelled as its bare value + +- **WHEN** the unnamed measures `rank(sum(a), direction='desc')`, + `rank(sum(a), direction='Ascending')` and + `rank(sum(a), partition_by=r, direction='asc')` are queried on model `o` +- **THEN** their result keys are `o.rank_a_sum_desc`, `o.rank_a_sum_asc` and + `o.rank_a_sum_partition_by_r_asc`, while `ntile(sum(a), n=4)` keeps + `o.ntile_a_sum_n_4` + +#### Scenario: Formatting-insensitive derivation + +- **WHEN** the same formula is written with different spacing (`logo_churn:sum/logo_bop:sum`) +- **THEN** it derives the identical result key + +#### Scenario: Long formulas hash-fold + +- **WHEN** an unnamed formula's sanitized name exceeds 48 characters +- **THEN** the key folds to the `__` form deterministically diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/partitioned-aggregates/spec.md b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/partitioned-aggregates/spec.md new file mode 100644 index 00000000..13aff8e8 --- /dev/null +++ b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/partitioned-aggregates/spec.md @@ -0,0 +1,834 @@ +## MODIFIED Requirements + +### Requirement: Partitioned aggregates nested inside transforms +A transform SHALL accept a partitioned aggregate as its input when used as a measure — rank-family transforms and temporal transforms (`time_shift`, `change`, `change_pct`, `lag`, `lead`, `cumsum`, `consecutive_periods`) alike. The transform evaluates at its operand grain — the attached aggregate's `partition_by=`, else the query grain (Axiom 11.1) — and the measure consumer broadcasts the result onto the query grain (Axiom 11.4); it MUST never fail with an internal error. + +#### Scenario: Running total of partition-grain values +- WHEN a query selects dimensions `[region, city, month(ordered_at)]` and the measure `cumsum(revenue:sum(partition_by=[region, ordered_at]))` +- THEN each row's value is the cumulative sum across months, within the row's non-time dimensions, of the attached region-month totals, verified by executed values + +#### Scenario: Ranking result rows by an attached total +- WHEN a query selects the measure `rank(revenue:sum(partition_by=region), direction='desc')` +- THEN result rows are ranked by their attached region total at the query grain + +#### Scenario: Change over a partitioned aggregate executes +- WHEN a query selects `change(amount:sum(partition_by=region))` or `change_pct(amount:sum(partition_by=region))` over a month time dimension +- THEN the query executes with the hand-computed bucket-over-previous-bucket difference (or ratio) of the attached value, instead of failing with an internal rendering error + +### Requirement: Grain-union broadcasting across consumption contexts +An arithmetic expression combining aggregates at different declared partition grains SHALL be well-defined at the union of those grains: each aggregate is computed at its own declared grain and broadcast over the grain keys it lacks. The expression SHALL be consumable at any grain refining the union — as a measure, the query grain — with every operand broadcast to the consuming rows; combining aggregates at different grains is never, by itself, an error. A transform over such an expression used as a measure SHALL evaluate at the query grain over the broadcast operands. Filter conjuncts referencing such expressions apply after attachment and MUST NOT alter surviving rows' values. + +#### Scenario: Mixed-grain arithmetic as a measure +- WHEN a query over dimensions `[region, city]` selects the measure `a:sum(partition_by=region) - b:sum(partition_by=city)` +- THEN each row's value is its region total minus its city total, by executed values + +#### Scenario: Same-grain partitioned arithmetic as a measure +- WHEN a query selects the measure `a:sum(partition_by=region) - b:sum(partition_by=region)` (the degenerate union of two identical grains) +- THEN each row's value is the difference of its two broadcast region totals, by executed values + +#### Scenario: Plain and partitioned aggregates mix in one expression +- WHEN a query selects the measure `amount:sum - amount:sum(partition_by=region)` +- THEN each row's value is its query-grain total minus its broadcast region total, by executed values + +#### Scenario: Transform over mixed-grain arithmetic as a measure +- WHEN a query selects the measure `rank(a:sum(partition_by=region) - b:sum(partition_by=city), direction='desc')` +- THEN result rows are ranked at the query grain by the broadcast difference, and adding the measure changes no other column's values + +#### Scenario: Filter over mixed-grain arithmetic +- WHEN a query filters on `a:sum(partition_by=region) - b:sum(partition_by=city) > 0` +- THEN only qualifying rows remain and every surviving value equals the unfiltered query's value for that row + +### Requirement: Re-aggregation consumes attached operands as datasets +A partitioned aggregate, an explicitly grained transform, or a composite of them +SHALL be a legal aggregation source: the outer aggregation consumes the operand +dataset's cells per `queries/semantics` › Second-order aggregation over attached +values. A transform is a constituent like a partitioned aggregate, typed at the union of +its inner aggregates' effective grains — each inner's explicit `partition_by=`, else +the query grain (its dimensions and time buckets), a windowed inner contributing the +query's active time bucket (per `queries/computed-dimensions` › Transforms inside +dimension expressions) — and evaluated at that grain, the query's active time bucket +reaching the constituent's own producer so a windowed inner resolves it; a time-ordered +transform constituent whose grain does not contain its time axis SHALL fail with the same +time-axis error a dimension-position transform raises, the axis being named in +`partition_by=` exactly as in dimension position (a top-level measure transform is +unchanged and keeps evaluating at the query grain over the attached value); an inner +aggregate homed on a joined model whose `partition_by=` names the host's time axis — a +key reachable from the inner's own root only across a fanning join hop — SHALL fail with +the partition-key attributability error in every mode, associate included, the +mode-invariant input-safety rule for a partition key whose closure fans from the inner's +host (Axiom 8): not a deferred shape and not a boundary a mode resolves, though a future +change (DEV-1941) would let associate mode compute it by distinct-entity association per +bucket; an +axis-collapsing transform constituent (`first`, `last`) is typed at that union minus +its time axis, realised as its axis-preserving evaluation followed by an exact +per-partition pick, so the axis resolves per `to_many_handling` like any dimension +the operand grain lacks; a transform with no explicitly grained inner aggregate types +at the query grain and follows the degenerate rule. The outer +aggregation SHALL support the plain scalar aggregation family — +`sum`, `avg`, `min`, `max`, `count`, `count_distinct`, `median`, +parametric aggregations, and model-defined custom aggregations; `count` counts +the operand's cells with a non-null value and `count_distinct` its distinct +values. The outer aggregation MAY declare its own `partition_by=`, its keys judged +against the operand dataset per › A re-aggregation's outer grain is judged against +its operand dataset, and its value behaves as a normal attached value in every consumer context — +measure, arithmetic or transform input, ORDER BY target, filter-only reference, +and computed dimension (with an explicit outer grain, per the dimension +grain-self-containment rule). `first`/`last` over an aggregated first argument +keep their transform dispatch. The outer aggregation's parameters — explicit, +positional, or defaulted by the aggregation definition — follow +`queries/semantics` › Aggregation parameters are typed by the home dataset's +grain against the operand dataset's grain: an aggregate grained within the +operand grain, a grained transform whose result grain the operand grain determines +(riding the carrier as a constituent exactly like a transform source constituent), +or a column that grain determines, is picked once per cell and +read by the outer aggregation, and its partition keys are exempt from the +combined-consumer partition-key rule exactly as the source's constituents are; +a population-row column against a coarser cell grain, a definition default +naming such a column, or an aggregate or transform grained outside the operand grain +is a typed error naming the parameter and the remedy. The outer aggregation SHALL +reject, with typed errors naming the combination and the remedy: `window=` or +ranked (`first`/`last`) aggregation over an attached operand, and a +measure-local `filter=` on the outer aggregation. + +#### Scenario: Count and parametric outer aggregations +- **WHEN** a query over `[region]` selects + `count(sum(amount, partition_by=[city, region]))` and + `percentile(sum(amount, partition_by=[city, region]), p=0.9)` +- **THEN** each region row carries the number of its city cells with a non-null + total and the 0.9-percentile of those totals, by executed values + +#### Scenario: Grained transform constituent executes through the carrier +- **WHEN** a query over a month time dimension selects + `sum(cumsum(amount:sum(partition_by=[region, ordered_at])) - 1)` +- **THEN** it executes with the hand-computed sum over regions of running totals + minus one per cell on SQLite and DuckDB, the plan carries exactly one producer for + the transform at its `(region, month)` grain inside the carrier, the emitted + statement has one flat `WITH`, scopes are closed, and no placeholder leaks + +#### Scenario: Every transform family executes as a constituent +- **WHEN** a query over a month time dimension selects, over + `amount:sum(partition_by=[region, ordered_at])`, a `change`, a `lag`, a + `consecutive_periods` and a `first`/`last` constituent under `sum` +- **THEN** each executes with hand-computed values on SQLite and DuckDB: the + shift family through its self-join series, `lag` and `consecutive_periods` per + cell, and `first`/`last` at the collapsed `(region)` grain + +#### Scenario: Windowed inner under a transform constituent fails closed +- **WHEN** a query over a month time dimension selects + `sum(rank(amount:sum(window='90d', partition_by=region), direction='desc'))` +- **THEN** it no longer fails with the windowed time-dimension error — it executes per + the next scenario; the former fail-closed pin is retired + +#### Scenario: Windowed inner under a transform constituent executes +- **WHEN** a query over a month time dimension selects + `sum(rank(amount:sum(window='90d', partition_by=region), direction='desc'))` +- **THEN** it executes with hand-computed values on SQLite and DuckDB — exactly one + result row per bucket, every value non-NULL — the plan carries exactly one nested + producer for the windowed inner grained by the query's active bucket, that exact + bucket key is among the producer's projected grain and join keys, the emitted + statement has one flat `WITH`, scopes are closed, and no placeholder leaks — never + the former windowed time-dimension error + +#### Scenario: A pure re-aggregation counts operand cells, not base rows +- **WHEN** a query over a month time dimension selects + `sum(rank(amount:sum(window='90d', partition_by=region), direction='desc'))` over a source with several + base rows per (region, month) cell +- **THEN** the outer aggregation counts each operand cell once — its home is the operand + dataset (Axiom 2.4), so the producer joins at the query grain as a second-order + re-aggregation, never a row-grain attach that would multiply by the base-row count; + the mixed `sum(amount * min(X, partition_by=region))` over the same rows instead counts + every base row, since its row leaf homes it on the model rows + +#### Scenario: Cross-model grained inner naming a host time axis is a permanent boundary +- **WHEN** a query rooted at `orders` over a month time dimension selects + `sum(cumsum(customers.spend:sum(partition_by=[customers.tier, ordered_at])))` +- **THEN** under every mode — the default (broadcast), error AND associate — it fails at + plan time with the partition-key attributability error naming `ordered_at` and the + remedy: the inner is homed at `customers`, and `ordered_at` is an `orders` column + reachable from `customers` only across the fanning `customers → orders` hop, so it is + a mode-invariant input-safety error (Axiom 8) — never a duplicated or misgrained + result, and never a deferral wording. The value is well-defined under distinct-entity + association (a future change, DEV-1941, would compute it), so this is the boundary a + fanning-crossing time key hits today, not a fundamental impossibility. + +#### Scenario: A to-one cross-model partition key on a local-homed inner stays legal +- **WHEN** a query rooted at `orders` over a month time dimension selects + `sum(cumsum(amount:sum(partition_by=[customers.tier, ordered_at])))` +- **THEN** it compiles: `customers.tier` is determined from `orders` over the to-one + hop, so the constituent is grained at `(customers.tier, month)` — the boundary above + is specific to a target-homed inner naming a host axis, not to cross-model partition + keys + +#### Scenario: Transform constituent without its time axis fails cleanly +- **WHEN** a query over a month time dimension selects + `sum(cumsum(amount:sum(partition_by=region)))` +- **THEN** it fails with the time-axis error directing the author to include the time + key in `partition_by=`, the same error the dimension-position form raises, and + never returns duplicated or misgrained rows + +#### Scenario: Time transform without a time dimension fails as a constituent +- **WHEN** a query with no `time_dimensions` selects + `sum(cumsum(amount:sum(partition_by=[region, ordered_at])))` +- **THEN** it fails with the same unambiguous-time-dimension error a top-level + `cumsum` raises + +#### Scenario: Operand-grain parameter executes +- **WHEN** a query over `[region]` selects + `weighted_avg(sum(amount, partition_by=[city, region]), weight=count(id, partition_by=[city, region]))`, + and separately the custom `wavg(sum(amount, partition_by=[city, region]), weight=count(id, partition_by=[city, region]))` + with the weight passed positionally +- **THEN** each region row carries the row-count-weighted average of its city totals, + by hand-computed executed values on SQLite and DuckDB, the keyword and positional + spellings identical, and the emitted SQL carries the parameter as a column of the + operand carrier — one producer relation for the operand, no duplicate + +#### Scenario: Parameter partition keys need not be query dimensions +- **WHEN** the outer aggregation's parameter is an aggregate grained at the operand grain + and that grain's keys are not query dimensions +- **THEN** the query plans and executes without the combined-consumer partition-key + error, exactly as the source's constituents are exempt + +#### Scenario: Transform over a re-aggregated value +- **WHEN** a query selects `rank(avg(sum(amount, partition_by=[city, region])), direction='desc')` +- **THEN** result rows are ranked at the query grain by the attached + re-aggregated value, and no other column's values change + +#### Scenario: Explicit outer grain broadcasts per the combined rules +- **WHEN** a query over `[region, product]` selects + `avg(sum(amount, partition_by=[city, region]), partition_by=region)` +- **THEN** the per-region average broadcasts across `product` exactly as any + explicit-grain partitioned measure does, with no implicit-broadcast warning + +#### Scenario: Filter on the re-aggregated value prunes only +- **WHEN** a query filters on `avg(sum(amount, partition_by=[city, region])) > 100` +- **THEN** only qualifying result rows remain and every surviving value equals + the unfiltered query's value for that row + +#### Scenario: Row-phase filters reach the inner producer +- **WHEN** the query carries a row-level filter conjunct +- **THEN** it restricts the inner producer's population per the established + producer filter routing, and the re-aggregated value reflects it + +#### Scenario: Row filters bound the operand dataset's cells +- **WHEN** a row filter removes every operand row of a union-grain cell and the + operand is consumed through a NULL-restoring composite (e.g. `coalesce(…, 0)`) +- **THEN** the operand dataset excludes that cell — the composite cannot + fabricate it — by executed values + +#### Scenario: Column-reference outer parameter fails closed +- **WHEN** the outer aggregation carries a parameter the operand grain does not + determine — explicit (`wavg(sum(amount, partition_by=[city, region]), weight=id)`) + or defaulted by its aggregation definition to such a column +- **THEN** it fails at plan time with a typed error naming the parameter, the grain, + and the remedy — never invalid SQL, a render-time failure, or a silently wrong value + +#### Scenario: Outer window and outer filter fail closed +- **WHEN** a query selects `sum(sum(amount, partition_by=[city, region]), window='90d')` + or gives the outer aggregation a measure-local `filter=` +- **THEN** each fails with a typed error naming the unsupported combination and + the remedy — never a silently wrong value + +#### Scenario: First and last keep transform dispatch +- **WHEN** a query selects `last(sum(amount, partition_by=[city, region]))` +- **THEN** it is the `last` transform over the aggregated series, unchanged + +#### Scenario: Transform-valued outer parameter rides the carrier +- **WHEN** a query over `[region]` selects + `weighted_avg(sum(amount, partition_by=[city, region]), weight=rank(count(id, partition_by=[city, region]), direction='desc'))` +- **THEN** the rank of each city cell's row count (across all cells: Alpha/North 1; + Alpha/South, NULL/Gap and Xi/Void 2; every other cell 5) is a constituent of the + operand carrier, and each region carries the rank-weighted average of its city + totals — North 55, South 580 / 7, East 60, Gap 64 / 7, Void NULL — by executed + values on SQLite and DuckDB, the emitted SQL scope-closed + +#### Scenario: Transform-valued outer parameter outside the operand grain fails closed +- **WHEN** the outer parameter is `rank(count(id, partition_by=product), direction='desc')` — a grain + `[product]` the operand grain `[city, region]` does not determine +- **THEN** the query fails at plan time with the typed determination error naming the + parameter and the grain, never a scope leak or a value + +### Requirement: Mixed sources carry the full expression-source surface +An aggregation source mixing row-level references with attached values SHALL +behave as a row-level expression source: everything legal for a plain +expression source is legal for it, and nothing more. Row leaves MAY be host-model +or joined-model columns, homed per `queries/semantics` › Home dataset of a +row-level aggregation source; an explicitly grained transform is an attached +constituent exactly like a partitioned aggregate, and so is a re-aggregation — an +aggregate over attached values, hand-written or produced by the `first`/`last` +collapse — evaluated at its own grain and broadcast per partition onto the source's +rows, an empty grain broadcasting its one value onto every row. The outer aggregation +SHALL support the plain scalar family, `count` (base rows with a non-null +operand value) and `count_distinct`, parametric and model-defined custom +aggregations — including multi-input built-ins and column-reference parameters +resolvable at row scope — and its own `partition_by=` and `window=`, each +resolved by the same rules as over a plain expression source. `first`/`last` +over the mixed expression SHALL keep the existing not-supported-over-an- +expression error. The shape SHALL be legal in measure, filter (typing as a +measure: pruning result rows without altering surviving values), and ORDER BY +positions, and as a computed dimension when grain-self-contained (the outer and +every inner aggregate explicitly grained). No producer or grouping step may +group by the row leaf or the attached value themselves — they feed the outer +aggregation only — and the mixed shape SHALL never be compiled through the +fully-attached carrier (whose cell-over-cell value differs). Row leaves keep +every existing expression-source restriction; attached constituents may be +cross-model, windowed, grained transforms, or themselves attached-input +aggregations. Discovery SHALL treat an aggregation that +owns attached inputs as opaque below its inputs (source and parameters): those +inputs belong to it — row-attached when it evaluates inline, owned by its own +producer when it is itself a producer answer — and are never discovered as +consumers of the enclosing level; its partition keys are not inputs and stay +visible to the enclosing level, so an attach-carrying computed dimension in +its `partition_by=` still gets the outer attach the grain join needs. + +#### Scenario: Outer partition_by over a mixed source +- **WHEN** a query over `[region, product]` selects + `sum(quantity * avg(unit_price, partition_by=product), partition_by=region)` +- **THEN** every row of a region carries that region's row-weighted total, + broadcast exactly as any explicit-grain partitioned measure, by executed + values + +#### Scenario: Outer window over a mixed source +- **WHEN** a query with a month time dimension selects + `sum(quantity * avg(unit_price, partition_by=product), window='90d')` +- **THEN** each bucket carries the trailing-90-day row-weighted total as of that + bucket, by executed values + +#### Scenario: Windowed constituent inside a mixed source +- **WHEN** a query with a month time dimension selects + `sum(qty * sum(revenue, window='90d'))` +- **THEN** the windowed inner is exactly one nested producer at the bucket + grain, row-attached into the outer aggregation's input relation — never an + attach of the enclosing level — and each bucket carries the hand-computed + row-weighted value, by executed values + +#### Scenario: Transform constituent inside a mixed source +- **WHEN** a query over `[region]` selects + `sum(quantity * rank(avg(unit_price, partition_by=product), direction='desc'))` +- **THEN** the transform is exactly one nested producer at its `(product)` grain, + row-attached into the outer aggregation's input relation, and each region carries + the hand-computed row-weighted value, by executed values — never the former + nested-transform rejection + +#### Scenario: Collapsing constituent mixed with a row leaf fails closed +- **WHEN** a query over a month time dimension selects + `sum(amount * last(amount:sum(partition_by=[region, ordered_at])))` +- **THEN** it no longer fails with the collapsing-transform error — it executes per the + next scenario; the former fail-closed pin is retired + +#### Scenario: Collapsing transform constituent inside a mixed source +- **WHEN** a query over a month time dimension selects `sum(amount * last(X))` with + `X = amount:sum(partition_by=[region, ordered_at])` +- **THEN** it executes with the hand-computed values Jan 375 / Feb 825 / Mar 900 on + SQLite and DuckDB: `last(X)` collapses to one value per region, broadcast onto each + row of that region, multiplied by the row's `amount` and summed per month — never + the former fail-closed collapse error + +#### Scenario: Hand-written re-aggregation constituent inside a mixed source +- **WHEN** a query over a month time dimension selects + `sum(amount * min(X, partition_by=region))` +- **THEN** it executes with hand-computed values on SQLite and DuckDB, the plan carries + exactly one producer for the re-aggregation at its `(region)` grain, row-attached on + `region` and never attached at the enclosing level, the emitted statement has one + flat `WITH`, scopes are closed, no placeholder leaks, and the query's row count is + unchanged — never the internal grain-cover assertion + +#### Scenario: Empty-grain re-aggregation constituent broadcasts one value +- **WHEN** a query over a month time dimension selects + `sum(amount * last(amount:sum(partition_by=ordered_at)))` +- **THEN** the collapsed constituent has an empty grain — one value — attached with no + join keys onto every row, and the query executes with hand-computed values on both + engines with unchanged cardinality, never an empty join predicate + +#### Scenario: Re-aggregation constituent as an attached parameter +- **WHEN** a query over a month time dimension selects + `weighted_avg(amount, weight=min(X, partition_by=region))` +- **THEN** the parameter is attached at its `(region)` grain through the same path as a + source constituent and the query executes with hand-computed values on both engines + +#### Scenario: Mixed re-aggregation root combined with a coarser measure +- **WHEN** a query over `[region]` with a month time dimension selects + `sum(amount * min(X, partition_by=region)) + amount:sum(partition_by=region)` +- **THEN** it executes with hand-computed values on both engines, the coarser term + broadcast across months exactly as any explicit-grain partitioned measure, with + unchanged cardinality + +#### Scenario: Mixed collapse in filter and order positions +- **WHEN** `sum(amount * last(X))` appears only in a filter or only as an ORDER BY target +- **THEN** the filter types as a measure — pruning result rows with surviving values + unchanged — and the order sorts by the same value the measure form returns + +#### Scenario: The mode axis is not bypassed by an attached re-aggregation +- **WHEN** a query selecting `sum(amount * min(X, partition_by=region))` also groups by + a dimension the source's home does not determine +- **THEN** under broadcast mode the value repeats across that dimension's cells with + the self-announcing warning, and under error mode the query fails with the mode + error — exactly as a mixed source with a plain attached aggregate + +#### Scenario: Joined-model row leaf inside a mixed source +- **WHEN** a query rooted at `orders` over `[status]` selects + `sum(customers.discount * avg(amount, partition_by=status))` +- **THEN** the source is homed at `orders`, the attached average is row-attached per + order, and each status carries the hand-computed value, by executed values + +#### Scenario: Parametric outer with a row-valued parameter +- **WHEN** a query selects + `wavg(quantity * avg(unit_price, partition_by=product), weight=qty)` (a + weighted average whose weight is a row column) +- **THEN** it executes with the hand-computed row-weighted average, never a + column-parameter rejection + +#### Scenario: Custom and multi-input aggregations over a mixed source +- **WHEN** a model-defined custom aggregation or a two-input built-in (e.g. + `corr(quantity * avg(unit_price, partition_by=product), qty)`) consumes a + mixed source +- **THEN** each executes with correct values through its own rendering path + +#### Scenario: Count semantics over a mixed source +- **WHEN** a query selects `count(quantity * avg(unit_price, partition_by=product))` +- **THEN** each cell counts its base rows whose operand value is non-null + +#### Scenario: Ranked aggregation over a mixed source keeps the expression error +- **WHEN** a query selects `first(quantity * avg(unit_price, partition_by=product))` +- **THEN** it fails with the existing error that `first` is not supported over + an expression — never a mixing error and never wrong values + +#### Scenario: Filter and order positions +- **WHEN** the mixed measure appears only in a filter + (`sum(quantity * avg(unit_price, partition_by=product)) > 100`) or only as an + ORDER BY target +- **THEN** the filter types as a measure — pruning result rows with surviving + values unchanged — and the order sorts by the same value the measure form + returns + +#### Scenario: Grain-self-contained dimension position +- **WHEN** a query groups by a computed dimension banding + `sum(quantity * avg(unit_price, partition_by=product), partition_by=region)` +- **THEN** rows group by the band with correct executed values and unchanged + cardinality, and the plan carries one attach for the banded aggregation with + the inner `avg` nested inside its producer — no separate top-level attach for + the inner + +#### Scenario: Adding a mixed measure is cardinality-neutral +- **WHEN** any supported query runs with and without an additional mixed-source + measure +- **THEN** both runs return the same rows and identical values in all shared + columns, and the emitted SQL leaks no internal placeholder names + +#### Scenario: Never the fully-attached carrier +- **WHEN** the emitted SQL for a mixed-source measure is inspected +- **THEN** no relation groups by the attached value or the row leaf; the outer + aggregation consumes base rows with the attached value joined on the + constituent's complete grain + +#### Scenario: A producer-bound mixed aggregation owns its inners +- **WHEN** a mixed aggregation is itself a producer answer (it carries its own + `partition_by=`, `window=`, or is cross-model) and the plan is inspected +- **THEN** its inner constituents appear only inside that producer's plan, + never as attaches of the enclosing level + +#### Scenario: Partition keys stay visible through an opaque root +- **WHEN** a mixed aggregation declares `partition_by=` on an attach-carrying + computed dimension of the query +- **THEN** that dimension's own attach is still planned at the enclosing level + and the aggregation's producer joins back on it, with executed values equal + to the same query spelled with a plain dimension + +#### Scenario: One producer per distinct attached input +- **WHEN** the same attached aggregate appears twice — as a source constituent + and as a parameter of one aggregation, or as an input of two different + row-attach aggregations +- **THEN** the plan carries exactly one producer and one attach for it, every + occurrence substitutes to that attach, total routing holds after + substitution, and the executed values are correct + +#### Scenario: A re-aggregation used both standalone and as a mixed constituent is deferred +- **WHEN** one query selects both `min(X, partition_by=region)` on its own and + `sum(amount * min(X, partition_by=region))` — the same re-aggregation standalone + (combined phase) and as a mixed row-level constituent (row phase) — over `[region]` + and a month time dimension, in measure, filter, or order position +- **THEN** it is no longer deferred — never the former checker error — and executes on + every engine with the values each use has alone: the + standalone value is the region minimum broadcast onto every month cell (North 10, + South 5, NULL where the region has no non-null cell) and the mixed value is the + per-cell `amount` times that minimum, summed (North 100 / 200 / 300, South 25 / 75); + the emitted statement carries exactly one producer relation for the re-aggregation + and one for its nested operand, and adding either measure changes no other value + +### Requirement: Attached parameters on row-level sources +An aggregation over a row-level source whose parameter — keyword or +positional; a definition default is Mode-A text and cannot carry an attached +value — is an attached value (an aggregate or a grained transform) SHALL +compile when the aggregation's operating grain determines the parameter. An +aggregate is determined by a grain iff that grain determines each of its +partition keys: a grain member, a column reached from a grain member over +provably to-one join hops, or an aggregate-valued key whose own grain is so +determined; an expression-valued partition key is determined only as an exact +grain member; a transform is determined iff its result grain is — the union of its +inner aggregates' grains (each inner's explicit `partition_by=`, else the query's +dimensions and time buckets, a windowed inner contributing the query's active time +bucket), minus its time axis for `first`/`last`. A transform parameter is classified, +normalised, lowered and checked exactly as a transform source constituent, in the +keyword and positional positions alike: a time-ordered transform parameter whose +grain does not contain its time axis SHALL fail with the same time-axis error in +every position and mode; its own `partition_by=` is exempt from the +combined-consumer partition-key rule exactly as the source's constituents are; the +association and trailing-window kernels pick it once per cell exactly like an +aggregate-valued parameter; and a transform nested inside an attached aggregate +parameter resolves bottom-up like any nested attached input. The +parameter's producer is computed at the parameter's OWN home — the deepest +dataset determining the parameter's own inputs, resolved bottom-up — at its own +declared grain, and its value attached into the aggregation's input relation +per home row of the aggregation, in every `to_many_handling` mode, null-safely +on the producer's complete grain; the attachment is well-defined only when the +home determines every member of the parameter's resolved grain, judged by one +home-determination rule on the plain and association paths alike; the mode +governs the aggregation's own unattributable dimensions and, inside the +parameter's own producer, its explicit-partition-key rule — never where the +parameter is computed. The source alone decides that the aggregation runs over rows: a +source with any row-level leaf, or with no attached constituent at all (a +literal), is row grain; a parameter never changes that, and an attached +parameter beside a mixed source is attached by the same mechanism as the +source's constituents. A cross-model attached parameter on a local root +attaches through a target-rooted producer like any cross-model attached +constituent, subject to the existing input-safety rules. A NULL grain-key value +forms its own cell and attaches null-safely, per the established null rules. A +parameter the operating grain does not determine SHALL keep its typed +rejection. A parameter whose own inputs are unsafe — a partition key fanning +from the parameter's own home, or a dependency no dialect can analyse — SHALL +fail closed with the existing typed error in every mode; the enclosing +aggregation never inspects the parameter's interior. Outer parameters on +fully-attached (re-aggregation) sources keep their existing rules. The shape +SHALL be legal in measure, filter (typing as a measure) and ORDER BY positions. + +#### Scenario: Associate-mode attached parameter executes +- **WHEN** a query rooted at `orders` over `[status]` under + `to_many_handling: "associate"` selects + `customers.spend:weighted_avg(weight=sum(amount, partition_by=customers.regions.name))` +- **THEN** each status cell carries the average of its distinct associated + customers' spend, weighted by each customer's region total, by hand-computed + executed values on SQLite and DuckDB + +#### Scenario: A NULL-region entity weights by the NULL cell +- **WHEN** an associated customer has no region +- **THEN** its weight is the NULL-region cell's total — the NULL key forms its + own cell and attaches null-safely, so an order with no customer counts in + that same cell — by executed values + +#### Scenario: Ordinary-mode attached parameter executes +- **WHEN** a locally-rooted query selects + `weighted_avg(amount, weight=sum(amount, partition_by=region))` with `region` + determined per row +- **THEN** it executes with the row-attached per-region weight, by executed + values + +#### Scenario: Attached parameter beside a mixed source +- **WHEN** a query over `[region]` selects + `weighted_avg(quantity * avg(unit_price, partition_by=product), weight=sum(amount, partition_by=region))` +- **THEN** both attached inputs are row-attached into the aggregation's input + relation, the executed value equals the hand-computed row-weighted average, + and the emitted SQL leaks no placeholder — never a render-time failure + +#### Scenario: Literal source with an attached parameter is row grain +- **WHEN** a query over `[region]` selects the model-defined + `wsum(1, weight=sum(amount, partition_by=region))` +- **THEN** it aggregates over the population rows with the region total + attached per row — each region equals its row count times its total — by + executed values, never a re-aggregation over an empty grain + +#### Scenario: Default-mode twin of the associate shape +- **WHEN** the associate-mode query above runs under the default + `to_many_handling` +- **THEN** it executes: the parameter's producer is rooted at `orders` (its own + home) grouped by the customer's region, attached per customer row inside the + `customers`-rooted producer, and the customers-rooted weighted average over + every customer — the orderless one weighted by its region's total, the + region-less one by the NULL-region cell — is broadcast identically to both + `status` cells (33780 / 407 on the reference dataset) with the usual broadcast + warning naming `status`; under `to_many_handling: "error"` the query fails + with the mode's refusal naming the dimension, never a parameter error + +#### Scenario: Every mode agrees on attributable dimensions +- **WHEN** the same aggregation is selected by `customers.tier` — a dimension + the home determines — under `broadcast`, `associate` and `error` +- **THEN** all three modes return identical hand-computed values on SQLite and + DuckDB (gold 63.75, silver 138.33, bronze 40 on the reference dataset, the + orphan order's NULL tier NULL) with no broadcast or association warning + +#### Scenario: Cross-model attached parameter on a local root +- **WHEN** a query rooted at `orders` selects + `amount:weighted_avg(weight=sum(customers.spend, partition_by=customers.regions.name))` +- **THEN** the parameter attaches through its target-rooted producer per order + row and the query executes with hand-computed values; a parameter whose + path crosses a fanning or unproven hop fails with the existing typed + input-safety error, never wrong values + +#### Scenario: Recursively nested attached parameters +- **WHEN** a query rooted at `orders` selects + `customers.spend:weighted_avg(weight=weighted_avg(amount, weight=sum(customers.regions.pop, partition_by=customers.regions.name), partition_by=customers.regions.name))` + — three homes: `customers` for the outer, `orders` for the per-region + weighted average of order amounts, `regions` for the innermost population sum +- **THEN** each level's producer is rooted at its own home and attached one + level up by its grain, and the query executes under the default mode with the + hand-computed value broadcast to every `status` cell (the region-less + customer's NULL innermost weight excludes it), on SQLite and DuckDB + +#### Scenario: Ranked transform as the attached parameter +- **WHEN** a query rooted at `orders` selects + `customers.spend:weighted_avg(weight=rank(sum(amount, partition_by=customers.regions.name), direction='desc'))` + — the region cells ranked by their order-amount total, the NULL-name region + forming its own ranked cell +- **THEN** the transform is the attached input at its result grain, and the query + executes under the default mode by `status` (broadcast, warned) and under every + mode by `customers.tier` with identical hand-computed values — 945 / 14 on both + `status` cells; gold 475 / 8, silver 97.5, bronze 40, the orphan order's NULL tier + NULL by tier — on SQLite and DuckDB + +#### Scenario: Transform parameter whose grain the home does not determine fails closed +- **WHEN** a query rooted at `orders` over a month time dimension on `ordered_at` + selects + `customers.spend:weighted_avg(weight=cumsum(sum(amount, partition_by=[customers.regions.name, ordered_at])))` +- **THEN** the query fails in every mode with the typed determination error + naming the parameter — `customers` does not determine the order month in the + transform's grain — never a multiplied or broadcast value + +#### Scenario: Windowed aggregation with an attached parameter +- **WHEN** a query rooted at `orders` over a month time dimension on + `customers.signup_at` selects + `customers.spend:weighted_avg(window='1y', weight=sum(amount, partition_by=customers.regions.name))` +- **THEN** each signup-month bucket carries the trailing-window weighted average + over the customers signed up in the window, each weighted by its region's + total, identical under every mode with no warning (100, 125, 28080 / 267, + 33780 / 407 on the reference dataset; the orphan order's NULL bucket NULL); + in a filter or as an ORDER BY target the same value prunes or sorts the + buckets, with no windowed column in the response + +#### Scenario: Custom aggregation with a windowed attached parameter +- **WHEN** a model defines `wsum` as `SUM({value} * {weight})` on `customers` + and a query rooted at `orders` over a month time dimension on + `customers.signup_at` selects + `customers.spend:wsum(window='1y', weight=sum(amount, partition_by=customers.regions.name))` +- **THEN** each signup-month bucket carries the trailing-window sum, over the + customers signed up in the window, of spend times the region's order total, + identical under every mode with no warning (10000, 25000, 28080, 33780 on the + reference dataset; the orphan order's NULL bucket NULL) — the same values as + the constituent form, the custom definition resolving on the source owner and + the attached weight read on each interval row + +#### Scenario: Unanalysable dependency inside an attached parameter fails closed +- **WHEN** an attached parameter's own source names a derived column whose + definition no supported dialect can parse +- **THEN** the query fails at plan time in every mode with the analyzability + error naming that column — the parameter's own producer fails closed even + though the enclosing aggregation never inspects it + +#### Scenario: Attached parameter whose own partition key fans from its own home fails closed +- **WHEN** a query rooted at `orders` selects + `amount:weighted_avg(weight=sum(customers.regions.pop, partition_by=customers.regions.bad_pop))` + — the parameter's home is `regions` and `bad_pop` crosses the fanning + `regions → region_events` hop from it +- **THEN** the query fails in every mode with the existing mode-invariant + partition-key error naming `bad_pop` and the hop `region_events`, never a + multiplied value + +#### Scenario: Attached parameter keyed by a host column keeps the mode-aware rule +- **WHEN** a query rooted at `orders` over `[status]` selects + `amount:weighted_avg(weight=sum(customers.spend, partition_by=status))` — `status` + is a plain host column, unattributable only from the parameter's home + `customers` +- **THEN** under `broadcast` and `error` the query fails with the explicit + partition-key error naming `status` and `customers`; under `associate` the + parameter associates `status` per cell (per `queries/attribution-modes`) and + each order is weighted by its cell's distinct-customer spend total — 82 / 7 for + `ok`, 85 / 3 for `new` on the reference dataset — by executed values + +#### Scenario: Attached parameter in filter and order positions +- **WHEN** the ordinary-mode measure or the mixed-plus-parameter measure above + appears only in a filter, only as a raw ORDER BY formula, or is ordered by + the name of its projected measure +- **THEN** the filter types as a measure — pruning result rows with surviving + values unchanged — and each order form sorts by the same value the measure + form returns + +#### Scenario: Partition-key kinds of a parameter aggregate +- **WHEN** an associate-mode parameter aggregate is grained by an aggregate-valued + key whose own grain the entity determines, or by an expression key that is not + a grain member +- **THEN** the first executes with the parameter read once per entity and the + second fails with the typed determination error naming the parameter + +#### Scenario: Undetermined attached parameter stays rejected +- **WHEN** the aggregation's operating grain does not determine the attached + parameter — e.g. `customers.spend:weighted_avg(weight=sum(customers.spend, partition_by=status))` + rooted at `orders`, whose home `customers` does not determine the grain member + `status` — under any `to_many_handling` mode, by an attributable + (`customers.tier`) or an unattributable (`status`) dimension +- **THEN** the query fails with the typed determination error naming the + parameter, never wrong values; the plain and association paths refuse alike + +#### Scenario: Positional transform parameter folds onto the declared name +- **WHEN** a query rooted at `orders` over `[customers.tier]` selects + `customers.spend:weighted_avg(rank(sum(amount, partition_by=customers.regions.name), direction='desc'))` +- **THEN** it binds to the same aggregation identity as the `weight=` spelling and + returns identical result keys and values (gold 475 / 8, silver 97.5, bronze 40) + +#### Scenario: Associate-mode transform parameter +- **WHEN** the ranked-transform shape above is selected by `status` under + `to_many_handling: "associate"` +- **THEN** each status cell aggregates its distinct associated customers weighted by + their region's rank — `ok` 700 / 9 (customers 1, 2, 3, 5, 6), `new` 330 / 4 + (customers 1, 2, 4) — with the association warning, never NULL, on SQLite and DuckDB + +#### Scenario: Local-root transform parameter +- **WHEN** a query rooted at `sales` selects + `weighted_avg(amount, weight=rank(sum(amount, partition_by=region), direction='desc'))` by `region`, + by `[region, city]` with the inner grained by `city`, and with no dimensions +- **THEN** it executes with the row-attached rank of the row's cell — by region North + 22.5, South 140 / 3, East 60, Gap 20 / 3, Void NULL; by region and city Alpha/North + 10, Beta/North 60, Alpha/South 20, Gamma/South 100, Delta/East 50, Epsilon/East 50, + Zeta/East 80, NULL/Gap 6, Kappa/Gap 8, Xi/Void NULL; globally 810 / 33, the + NULL-total Void cell carrying a NULL rank and so no weight, on SQLite and DuckDB alike + +#### Scenario: Collapsing transform parameter drops the axis +- **WHEN** a query rooted at `orders` over an `ordered_at` month time dimension selects + `customers.spend:weighted_avg(weight=last(sum(amount, partition_by=[customers.regions.name, ordered_at])))` +- **THEN** the parameter is typed at `[customers.regions.name]` — each region's + latest-month total (North 12, South 15, NULL 47) — so the query executes: under the + default mode 8165 / 128 on every month with the broadcast warning naming the month, + and by `[customers.tier]` and month gold 3285 / 54, silver 3000 / 27, bronze 40 on + each of the tier's months with the same warning; never a determination error + +#### Scenario: Time-ordered transform parameter without its axis fails closed +- **WHEN** a query rooted at `orders` over an `ordered_at` month time dimension selects + `customers.spend:weighted_avg(weight=cumsum(sum(amount, partition_by=customers.regions.name)))`, + as a measure or only as a filter +- **THEN** it fails in every mode with the time-axis error naming the `partition_by=` + remedy, never a value + +#### Scenario: Time-ordered transform parameter over a determined axis executes +- **WHEN** a query rooted at `orders` over a `customers.signup_at` month time dimension + selects + `customers.spend:weighted_avg(weight=cumsum(sum(amount, partition_by=[customers.regions.name, customers.signup_at])))` +- **THEN** it executes in every mode with no warning: 100, 150, 1900 / 45, 5700 / 140 + for January to April 2024, the NULL bucket NULL; as a filter `> 50` it keeps January + and February with every other value unchanged, and as a raw ORDER BY descending it + orders February, January, March, April + +#### Scenario: Transform over an ungrained inner types at the query grain +- **WHEN** a query rooted at `orders` selects + `customers.spend:weighted_avg(weight=rank(sum(amount), direction='desc'))` +- **THEN** by `customers.tier` the inner is grained at `[customers.tier]` and the query + executes in every mode with no warning (gold 61.25, silver 115, bronze 40, NULL tier + NULL); by `status` the query fails in every mode with the typed determination error + naming `weight`, since `customers` does not determine `status` + +#### Scenario: Windowed inner joins the bucket to the parameter's grain +- **WHEN** a query rooted at `orders` over an `ordered_at` month time dimension selects + `customers.spend:weighted_avg(weight=rank(sum(amount, window='1y', partition_by=customers.regions.name), direction='desc'))` +- **THEN** it fails in every mode with the typed determination error naming `weight` — + the order-month bucket is in the transform's grain and `customers` does not + determine it + +#### Scenario: Windowed aggregation with a transform parameter +- **WHEN** a query rooted at `orders` over a `customers.signup_at` month time dimension + selects + `customers.spend:weighted_avg(window='1y', weight=rank(sum(amount, partition_by=customers.regions.name), direction='desc'))` +- **THEN** each signup-month bucket carries the trailing-window weighted average over + the customers signed up in the window, each weighted by its region's rank — 100, + 125, 510 / 7, 945 / 14, the NULL bucket NULL — identical under every mode with no + warning, and the trailing-window producer picks the transform once per interval row + +#### Scenario: Transform parameter in filter and order positions +- **WHEN** the ranked-transform shape above appears only as a filter `> 50` by + `customers.tier`, or only as a raw ORDER BY formula descending +- **THEN** the filter keeps gold and silver with every other value unchanged, and the + order is silver, gold, bronze + +#### Scenario: Transform parameter plan shape +- **WHEN** the ranked-transform shape above is planned under the default and error modes +- **THEN** the `customers`-rooted attach uses the plain kernel (not an association + kernel), the emitted SQL is scope-closed with no placeholder leak, and adding the + measure changes neither the row count nor any other column + +#### Scenario: Nested transform parameter inside an attached aggregate parameter +- **WHEN** a query rooted at `orders` selects + `customers.spend:weighted_avg(weight=weighted_avg(amount, weight=rank(sum(amount, partition_by=customers.regions.name), direction='desc'), partition_by=customers.regions.name))` +- **THEN** each level resolves bottom-up — the innermost rank over the region cells, + the middle weighted average per region (North 50 / 3, South 10, NULL 23.5), the + outer over customers — and the query executes: 7556.67 / 103.5 broadcast to both + `status` cells under the default mode with the warning, and by `customers.tier` + under every mode gold 3316.67 / 53.33, silver 123.75, bronze 40, NULL tier NULL + +#### Scenario: Cross-model transform parameter on a local root +- **WHEN** a query rooted at `orders` selects + `amount:weighted_avg(weight=rank(sum(customers.spend, partition_by=customers.regions.name), direction='desc'))` +- **THEN** the parameter's producer is rooted at `customers` grouped by region (spend + North 280, South 195, NULL 40 → ranks 1, 2, 3) and attached per order row, the + orphan order taking the NULL cell; the query executes in every mode with no warning — + 281 / 16 globally, `ok` 116 / 11 and `new` 33 by `status` + +#### Scenario: Transform parameter with its own partition_by outside the query dimensions +- **WHEN** a query rooted at `orders` selects + `customers.spend:weighted_avg(weight=rank(sum(amount, partition_by=[customers.regions.name, customers.tier]), partition_by=customers.regions.name, direction='desc'))` + with no dimensions, and by `customers.tier` +- **THEN** the transform's own partition key is exempt from the combined-consumer + partition-key rule exactly as a source constituent's is, and the query executes in + every mode with no warning: 760 / 11 globally; gold 61.25, silver 115, bronze 40 by + tier + +### Requirement: Transform partition keys bind like aggregate partition keys +A rank-family transform's own `partition_by=` SHALL bind exactly as an aggregation's +`partition_by=` does: each element — singly or in a list — is a column reference, a +dotted joined-model reference, or the name of a computed dimension declared in the +query, which resolves to that dimension's value; the same binding applies in the +measure, aggregation-parameter, filter, order-target and computed-dimension positions. +A name that is no declared computed dimension and no column SHALL fail as an unknown +reference; an element that is neither a column reference nor a computed-dimension name +SHALL fail with one error naming the construct — `aggregation` or `transform ''` — +and the offending kind. An attach-carrying computed dimension (one whose expression +contains an aggregate) binds the same way; its filter and order positions execute, while +its measure and aggregation-parameter positions fail closed with a planner error, never +an unknown-reference error (target behaviour, DEV-1960: they execute like the +aggregation twin). + +Values below are on the sales graph with `ureg` declared as the computed dimension +`upper(region)` and `spend_band` banding `sum(amount, partition_by=[city, region]) > 45`. + +#### Scenario: Measure position +- **WHEN** a query over `[ureg, city]` selects `rank(sum(amount), partition_by=ureg, direction='desc')` +- **THEN** each city is ranked by its total within its upper-cased region — EAST: Zeta 1, Delta 2, Epsilon 2; NORTH: Beta 1, Alpha 2; SOUTH: Gamma 1, Alpha 2; GAP: the NULL city 1, Kappa 2; VOID: Xi NULL (its total is NULL) — and the bound partition grain equals that of `sum(amount, partition_by=ureg)` + +#### Scenario: Aggregation-parameter position +- **WHEN** a query over `[ureg]` selects `weighted_avg(amount, weight=rank(sum(amount, partition_by=[ureg, city]), partition_by=ureg, direction='desc'))` +- **THEN** each region's rows are weighted by their city's rank within the region: EAST 56, NORTH 120/7, SOUTH 36, GAP 7, VOID NULL + +#### Scenario: Filter position +- **WHEN** a query over `[ureg, city]` filters `rank(sum(amount), partition_by=ureg, direction='desc') <= 1` +- **THEN** exactly the rank-1 rows survive: EAST/Zeta, NORTH/Beta, SOUTH/Gamma, GAP/NULL (VOID/Xi's NULL rank fails the predicate) + +#### Scenario: Order position +- **WHEN** a query over `[ureg, city]` orders by `rank(sum(amount), partition_by=ureg, direction='desc')` ascending +- **THEN** the four rank-1 rows precede every rank-2 row + +#### Scenario: Computed-dimension position with a member key +- **WHEN** a query declares `ureg` and a second computed dimension `rank(sum(amount, partition_by=[city, ureg]), partition_by=ureg, direction='desc')` named `r`, selecting `sum(amount)` +- **THEN** rows group by `(ureg, r)`: EAST r=1 80 and r=2 100, NORTH r=1 60 and r=2 30, SOUTH r=1 100 and r=2 40, GAP r=1 12 and r=2 8, VOID r=NULL NULL + +#### Scenario: Mixed list of a column and a computed dimension +- **WHEN** a measure names `rank(sum(amount), partition_by=[ureg, product], direction='desc')` +- **THEN** the bound partition grain is `{upper(region), product}`, identical to the grain `sum(amount, partition_by=[ureg, product])` binds + +#### Scenario: Non-column element names the construct +- **WHEN** a measure names `rank(sum(amount), partition_by=sum(amount), direction='desc')` or `sum(amount, partition_by=sum(amount))` +- **THEN** binding fails with "transform 'rank' partition_by must resolve to a column reference; got AggregateKey." or "aggregation partition_by must resolve to a column reference; got AggregateKey." respectively + +#### Scenario: Undeclared name stays unknown +- **WHEN** a query over `[city]` (no `ureg` dimension) selects `rank(sum(amount), partition_by=ureg, direction='desc')` +- **THEN** binding fails with the unknown-reference error naming `ureg` + +#### Scenario: Attach-carrying computed dimension in filter and order positions +- **WHEN** a query over `[spend_band, city]` filters `rank(sum(amount), partition_by=spend_band, direction='desc') <= 1` +- **THEN** exactly the rows hi/Gamma (100) and lo/Alpha (70) survive, and the same expression as an ascending order target sorts those two rows first + +#### Scenario: Attach-carrying computed dimension in measure and parameter positions fails closed +- **WHEN** a query over `[spend_band, city]` selects `rank(sum(amount), partition_by=spend_band, direction='desc')`, or a query over `[spend_band]` selects `weighted_avg(amount, weight=rank(sum(amount), partition_by=spend_band, direction='desc'))` +- **THEN** the key binds to the dimension's value and planning fails with a planner error, never an unknown-reference error (target behaviour, DEV-1960: both execute like `sum(amount, partition_by=spend_band)`) diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/semantics/spec.md b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/semantics/spec.md new file mode 100644 index 00000000..e0b9a75a --- /dev/null +++ b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/semantics/spec.md @@ -0,0 +1,231 @@ +## MODIFIED Requirements + +### Requirement: Second-order aggregation over attached values +An aggregation whose source operand resolves entirely to attached values — +partitioned aggregates, or explicitly grained transforms over them, directly or +combined through arithmetic and scalar functions — SHALL aggregate over the +operand dataset's cells, never over the query's population rows. The operand +dataset is typed by the union of its constituents' grains: an aggregate +constituent at its declared `partition_by=` grain; a transform constituent at the +union of its inner aggregates' grains — each inner's explicit `partition_by=`, else +the query's dimensions and time buckets — where a windowed inner's grain always +includes the query's time bucket whether or not its `partition_by=` names it, and an +axis-collapsing transform (`first`, `last`) at that union minus its time axis; a +constituent with no declared grain is typed at the query's dimensions. Its rows are the distinct union-grain cells of the row-filtered +population; each constituent's value attaches null-safely at its own grain, a cell +a constituent lacks contributes NULL, and no constituent adds or removes cells. The +outer aggregation partitions those cells by the query dimensions attributable to +the operand dataset per Axiom 1 (Determination): a dimension is attributable iff +the grain determines it — a grain member, or a field reached from a grain member +over provably to-one join hops (a foreign-key grain field thus determines its +referenced model's fields and any column further along a to-one chain; an +entity-key grain field additionally determines all of its own model's columns). +A grain field does not determine its own model's other columns when the grain +does not fix that model's key — a foreign key does not identify the many-side +row — and an expression grain field (time bucket, computed dimension) determines +only itself — as a grain member it pins nothing further. Determination is closed +under row-level combination (Axiom 2.2): a dimension combining determined operands +through arithmetic, comparison, scalar functions or conditionals — a literal being +determined by every grain, a time bucket when the grain determines its column, and an +embedded aggregate when the grain determines its `partition_by=` members — is itself +attributable, whatever its spelling. +A derived column is determined when the grain determines every column its definition +reads (value and filter), recursively — exactly as its inline expression would be. +Unattributable dimensions resolve per `to_many_handling` exactly as for +model-rooted aggregates: broadcast with a self-announcing warning naming the +dimension and the remedy, per-cell association, or a clear error. Adding a +re-aggregated measure MUST NOT change the result row count or any other +column's values. + +#### Scenario: Average of city totals per region +- **WHEN** a query over dimensions `[region]` selects the measure + `avg(sum(amount, partition_by=[city, region]))` +- **THEN** each region row carries the unweighted average of that region's city + totals, by executed values, distinguishable from the row-count-weighted value + +#### Scenario: Grained transform constituent aggregates the transform's cells +- **WHEN** a query over a month time dimension selects + `sum(cumsum(amount:sum(partition_by=[region, ordered_at])) - 1)` +- **THEN** each month carries the sum over regions of that region's running total + minus one per cell, by hand-computed executed values on SQLite and DuckDB + +#### Scenario: Ungrained transform constituent is identity plus a warning +- **WHEN** a query selects `sum(cumsum(amount:sum))` over a month time dimension +- **THEN** the value equals `cumsum(amount:sum)` per cell and the response carries + the degenerate-re-aggregation warning, exactly as `sum(sum(amount))` does + +#### Scenario: Ungrained inner of a mixed operand types at the query grain +- **WHEN** a query over a month time dimension selects + `sum(rank(amount:sum(partition_by=[region, ordered_at]) - amount:sum, direction='desc'))` +- **THEN** the ungrained inner is the month total, computed at the query grain and + broadcast onto the `(region, month)` cells before ranking — never re-evaluated per + region — by hand-computed executed values distinguishable from the per-cell + evaluation + +#### Scenario: Collapsing transform constituent drops the time axis +- **WHEN** a query over a month time dimension selects + `sum(last(amount:sum(partition_by=[region, ordered_at])))` +- **THEN** the constituent is typed at `(region)`: every month carries the sum over + regions of each region's most recent monthly total, the response warns that the + month dimension is broadcast, and a region absent from a month still counts — + distinguishable from summing the `(region, month)` cells present in that month + +#### Scenario: Collapsing and preserving constituents share one operand dataset +- **WHEN** the same query selects + `sum(cumsum(amount:sum(partition_by=[region, ordered_at])) - last(amount:sum(partition_by=[region, ordered_at])))` +- **THEN** the operand dataset is the `(region, month)` cells, the collapsed value + broadcasts onto them, each month is attributable through the preserving + constituent, and the value is correct with no warning + +#### Scenario: Composite operand keeps the population's cells +- **WHEN** the operand combines aggregates at `[city, region]` and `[region]` + grains and some union-grain cell has no value for one constituent (e.g. a + measure-local filter eliminates its rows) +- **THEN** the operand dataset has exactly the population's distinct + `(city, region)` cells, the region-grain value broadcast onto them, and the + missing value contributes NULL to that cell without removing it + +#### Scenario: Outer dimension attributed through a to-one chain +- **WHEN** the inner grain is an entity key (e.g. `customer_id`) and a query + dimension is reached from it over a provably to-one join chain +- **THEN** the outer aggregation partitions the inner cells exactly by that + dimension, with no broadcast warning + +#### Scenario: Outer dimension seeded by a nested-path entity key +- **WHEN** the inner grain contains a joined model's unique key + (e.g. `sum(amount, partition_by=customers.id)` rooted at `orders`) and a query + dimension is a column of that model or reached from it over a to-one hop + (`customers.region_id`, `customers.regions.name`) +- **THEN** the outer aggregation partitions the cells exactly by that dimension with + no broadcast warning, by executed values + +#### Scenario: A foreign-key grain field determines its to-one target +- **WHEN** the inner grain contains a foreign-key column + (e.g. `sum(amount, partition_by=customers.region_id)` rooted at `orders`) and a + query dimension is a field of the model that key points at over the provably + to-one hop (`customers.regions.name`) +- **THEN** the outer aggregation partitions the cells exactly by that dimension with + no broadcast warning, by executed values (Axiom 1: the fixed key value pins the + to-one target row) + +#### Scenario: A non-key grain field does not determine its own model's siblings +- **WHEN** the inner grain contains a joined model's foreign-key column + (e.g. `partition_by=customers.region_id`, which does not identify a customer) and + a query dimension is another column of that same model reached only by + identifying its row (`customers.id`) +- **THEN** the dimension is not attributable and resolves per `to_many_handling` + +#### Scenario: Unattributable outer dimension broadcasts with a warning +- **WHEN** a query over dimensions `[region]` selects + `avg(sum(amount, partition_by=city))` under the default mode +- **THEN** every region row carries the global average of city totals and the + response warns, naming `region`, the reason it is not attributable, and the + remedy (add it to the inner `partition_by=`) + +#### Scenario: Unattributable outer dimension associates on request +- **WHEN** the same query runs under `to_many_handling: "associate"` +- **THEN** each region cell aggregates the distinct city cells associated with + it through the population — a city value co-occurring with two regions counts + in both — by executed values + +#### Scenario: Unattributable outer dimension refuses under error mode +- **WHEN** the same query runs under `to_many_handling: "error"` +- **THEN** it fails with a clear error naming the measure, the dimension, and + the remedy — never wrong numbers + +#### Scenario: Degenerate re-aggregation is identity plus a warning +- **WHEN** a query selects `avg(sum(amount))` (operand grain equals the outer + grain) +- **THEN** the value equals `sum(amount)` per cell and the response carries a + degenerate-re-aggregation warning naming both grains and the + `partition_by=` remedy + +#### Scenario: Row-level expression over grain members partitions exactly +- **WHEN** a query over dimensions `[region, city == 'Alpha']` selects + `avg(sum(amount, partition_by=[city, region]))` under each `to_many_handling` mode +- **THEN** each `(region, city == 'Alpha')` row carries the average of exactly its own + city cells — `(North, true)` 30, `(North, false)` 60, `(South, true)` 40, + `(South, false)` 100, `(East, false)` 60, Gap's NULL-city cell 12, `(Gap, false)` 8, + `(Void, false)` NULL — by executed values on SQLite and DuckDB, with no broadcast or + association warning and no error + +#### Scenario: Expression over a to-one-determined dimension matches its plain spelling +- **WHEN** the operand is `sum(amount, partition_by=customer_id)` rooted at `corders` and + the query dimension is `customers.regions.name == 'North'` +- **THEN** the `true` cell carries 35 and the `false` cell 100 — the values the plain + `customers.regions.name` dimension gives North and South — by executed values, with no + warning + +#### Scenario: Aggregate-carrying expression dimension grained by a determined key +- **WHEN** the same operand is grouped by the dimension + `sum(amount, partition_by=customers.regions.name) > 80` +- **THEN** the `false` cell carries 35 and the `true` cell 100, by executed values, with no + warning — identical to grouping by the bare aggregate dimension + +#### Scenario: Expression over an undetermined column still resolves per mode +- **WHEN** a query over dimensions `[region, is_p]`, with `is_p` defined as + `product == 'P'`, selects `avg(sum(amount, partition_by=[city, region]))` +- **THEN** under the default mode each region's value repeats across `is_p` and the + broadcast warning names `is_p`, the reason that the operand grain does not determine + it, and the `partition_by=` remedy; under `to_many_handling: "error"` the query fails + naming `is_p` + +#### Scenario: Derived column over grain members partitions like its inline spelling +- **WHEN** the model declares a derived column `city_upper` defined as `UPPER(city)` and a + query over dimensions `[region, city_upper]` selects + `avg(sum(amount, partition_by=[city, region]))` +- **THEN** each row carries the average of exactly its own city cells, identical to the + query over `[region, upper(city)]`, by executed values on SQLite and DuckDB, with no warning + +### Requirement: Row-grain aggregation sources +An aggregation whose source operand combines row-level column references with +attached values — partitioned aggregates or explicitly grained transforms, directly +or through arithmetic and scalar functions — SHALL aggregate over the row-filtered +population rows of its home dataset, never over the attached operands' cells. The +operand types at row grain: the union of a row leaf's grain with any attached +constituent's grain is row grain, the finest. Each attached constituent is computed +at its own declared grain (an aggregate at its `partition_by=` grain, a transform at +the union of its inner aggregates' grains, minus its time axis for `first`/`last`; +a constituent with no declared grain is typed at the query's dimensions) and its +value is broadcast onto each population +row null-safely: a row whose constituent lacks a value carries NULL for that +constituent, and the attachment never adds or removes rows. Per-row weighting is +the defined meaning of the shape and the broadcast SHALL NOT warn. The outer +aggregation evaluates at its consumer grain exactly as over any row-level +expression. Adding such a measure MUST NOT change the result row count or any +other column's values. + +#### Scenario: Row-weighted value distinguishable from pure re-aggregation +- **WHEN** a query over dimensions `[region]` selects the measure + `sum(quantity * avg(unit_price, partition_by=product))` +- **THEN** each region row carries the sum, over that region's base rows, of the + row's `quantity` times its product's average unit price, by executed values, + distinguishable from the pure re-aggregation + `sum(avg(unit_price, partition_by=product))` and from + `sum(quantity * unit_price)` + +#### Scenario: Transform constituent inside a mixed source +- **WHEN** a query over dimensions `[region]` selects the measure + `sum(quantity * rank(avg(unit_price, partition_by=product), direction='desc'))` +- **THEN** each region row carries the sum over its base rows of `quantity` times + the rank of the row's product among products by average unit price, by executed + values, with unchanged cardinality + +#### Scenario: Missing constituent value is NULL on a surviving row +- **WHEN** some base row's cell has no value for an attached constituent and the + operand restores it through a NULL-restoring composite (e.g. + `coalesce(avg(unit_price, partition_by=product), 0) * quantity`) +- **THEN** that row still contributes to the outer aggregation with the restored + value — the population row exists and only its constituent value was NULL, + unlike a fully-attached source, whose carrier excludes the absent cell + +#### Scenario: Ungrained inner constituent types at the query's dimensions +- **WHEN** a query selects `sum(quantity * avg(unit_price))` with no + `partition_by=` on the inner aggregate +- **THEN** the inner value is computed at the query's dimensions, broadcast onto + each row, and weighted per row, with no degenerate-re-aggregation warning + +#### Scenario: Row filters bound both the population and the constituents +- **WHEN** the query carries a row-level filter conjunct +- **THEN** it restricts both the population rows the outer aggregation consumes + and each attached constituent's producer, and the executed value reflects both diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/transforms/spec.md b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/transforms/spec.md new file mode 100644 index 00000000..27d531ab --- /dev/null +++ b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/transforms/spec.md @@ -0,0 +1,352 @@ +## ADDED Requirements + +### Requirement: Rank-family ordering direction + +`rank` and `dense_rank` SHALL take a required keyword argument `direction` whose +value is a string literal `asc`, `desc`, `ascending` or `descending` (any case, +surrounding whitespace ignored), normalised to `asc` / `desc`, and SHALL order +their window by the inner value ascending for `asc` and descending for `desc`. +`direction` SHALL combine with `partition_by=` and SHALL accept any orderable +inner, numeric or not. `ntile` and `percent_rank` SHALL reject `direction` and +SHALL always order ascending, so `ntile` bucket 1 holds the lowest values and a +higher value never gets a lower `percent_rank`. A missing, unrecognised, +non-literal or forbidden `direction` SHALL fail before any SQL runs with a +`TransformArgumentError` (a `QueryTypeError`), in every position (measure, +filter, order, computed dimension, aggregation parameter, saved `ModelMeasure`); +the missing-direction message SHALL show both +`direction='asc'` (lowest first) and `direction='desc'` (highest first). The +importer formula validator SHALL apply the identical rule with the identical +error. `rank(x, direction='asc')` and `rank(x, direction='desc')` SHALL be +distinct values that never deduplicate into one. + +Values below use the DEV-1847 `sales` fixture, whose region totals are North +90, South 140, East 180, Gap 20 and Void NULL. + +#### Scenario: Ascending rank puts the lowest value first + +- **WHEN** a query over `[region]` selects `rank(sum(amount), direction='asc')` +- **THEN** the window orders the inner ascending and the ranks are Gap 1, North 2, + South 3, East 4, Void NULL on SQLite and DuckDB + +#### Scenario: Descending rank puts the highest value first + +- **WHEN** a query over `[region]` selects `rank(sum(amount), direction='desc')` +- **THEN** the window orders the inner descending and the ranks are East 1, South 2, + North 3, Gap 4, Void NULL + +#### Scenario: dense_rank takes the same direction + +- **WHEN** a query over `[region]` selects `dense_rank(sum(amount), direction='asc')` +- **THEN** the ranks are Gap 1, North 2, South 3, East 4, Void NULL + +#### Scenario: Direction synonyms normalise + +- **WHEN** a query selects `rank(sum(amount), direction=' Descending ')` +- **THEN** it binds to the identical value as `rank(sum(amount), direction='desc')` + +#### Scenario: Non-numeric inner ranks ascending + +- **WHEN** a query over `[region]` selects `rank(min(city), direction='asc')` +- **THEN** the ranks are North 1 and South 1 (both `Alpha`), East 3 (`Delta`), Gap 4 + (`Kappa`), Void 5 (`Xi`) + +#### Scenario: Direction combines with partition_by + +- **WHEN** a query over `[region, city]` selects + `rank(sum(amount), partition_by=region, direction='asc')` +- **THEN** each city ranks lowest-first within its region: East Delta 1, Epsilon 1, + Zeta 3; North Alpha 1, Beta 2; South Alpha 1, Gamma 2; Gap Kappa 1, the NULL city + 2; Void Xi NULL + +#### Scenario: Both directions in one query stay distinct + +- **WHEN** one query selects both `rank(sum(amount), direction='asc')` and + `rank(sum(amount), direction='desc')` unnamed +- **THEN** the result carries two columns with the ascending and descending ranks + above, never one deduplicated column + +#### Scenario: Missing direction fails naming both spellings + +- **WHEN** a query selects, filters on, or orders by `rank(sum(amount))` or + `dense_rank(sum(amount), partition_by=region)`, or queries a saved + `ModelMeasure` whose formula is `rank(sum(amount))` +- **THEN** it fails with a `TransformArgumentError` naming the transform and showing + `direction='asc'` (lowest first) and `direction='desc'` (highest first), and no SQL + runs + +#### Scenario: Unrecognised or non-literal direction fails + +- **WHEN** a query selects `rank(sum(amount), direction='up')` or + `rank(sum(amount), direction=region)` +- **THEN** it fails with a `TransformArgumentError` listing the accepted values + +#### Scenario: ntile and percent_rank reject direction + +- **WHEN** a query selects `ntile(sum(amount), n=2, direction='desc')` or + `percent_rank(sum(amount), direction='asc')` +- **THEN** it fails with a `TransformArgumentError` stating that the transform always + orders ascending and takes no `direction` + +#### Scenario: ntile and percent_rank order ascending + +- **WHEN** a query over `[region]` selects `ntile(sum(amount), n=2)` and + `percent_rank(sum(amount))` +- **THEN** `ntile` is Gap 1, North 1, South 2, East 2, Void NULL and `percent_rank` is + Gap 0, North 1/3, South 2/3, East 1, Void NULL + +#### Scenario: Importer validation shares the rule + +- **WHEN** the importer formula validator parses `rank(sum(amount))`, + `rank(sum(amount), direction='sideways')` or `ntile(sum(amount), n=4, direction='asc')` +- **THEN** each fails with the same `TransformArgumentError` the query binder raises + for the same formula + +#### Scenario: Window ordering is pinned across dialects + +- **WHEN** `rank` with each direction, `dense_rank`, `ntile` and `percent_rank` are + rendered for postgres, sqlite, duckdb, tsql and bigquery +- **THEN** each window orders the inner by the stated direction (`ASC` for `ntile` / + `percent_rank`) and the generated SQL matches recorded golden baselines + +### Requirement: Rank-family NULL inputs rank NULL + +For `rank`, `dense_rank`, `ntile` and `percent_rank`, a row whose inner value is +NULL SHALL get a NULL result. NULL rows SHALL NOT take a rank position or an +`ntile` bucket, nor count in `percent_rank`'s denominator, within each partition; +the non-NULL rows SHALL rank exactly as if the NULL rows were absent. The result +SHALL be identical on every supported dialect, independent of the dialect's +native NULL ordering. + +#### Scenario: Mixed NULL and non-NULL inners + +- **WHEN** a query over `[region]` selects `rank(sum(amount), direction='desc')`, + `percent_rank(sum(amount))` and `ntile(sum(amount), n=2)` on the `sales` fixture +- **THEN** Void (NULL total) gets NULL for all three, and the other regions get the + values of the ordering-direction scenarios above, `percent_rank`'s denominator + counting four rows, on SQLite and DuckDB + +#### Scenario: An all-NULL partition ranks NULL without disturbing others + +- **WHEN** a query over `[region, city]` selects + `dense_rank(sum(amount), partition_by=region, direction='desc')` +- **THEN** Void Xi (NULL total) is NULL, and every other region's cities rank as they + would without Void + +#### Scenario: A NULL row inside a partition takes no position + +- **WHEN** a query over `[region, city]` selects + `rank(city, partition_by=region, direction='asc')` +- **THEN** Gap's NULL city is NULL and Kappa is 1; East Delta 1, Epsilon 2, Zeta 3 + +#### Scenario: A filter on rank drops NULL-ranked rows + +- **WHEN** a query over `[region]` filters `rank(sum(amount), direction='asc') <= 5` +- **THEN** East, Gap, North and South survive and Void does not + +#### Scenario: NULL handling does not depend on the dialect's NULL ordering + +- **WHEN** the rank family is rendered for tsql, whose native ordering puts NULLs + first on `ASC` +- **THEN** the emitted SQL nulls the result for a NULL inner and keeps NULL rows out of + the non-NULL rows' window, exactly as on postgres + +### Requirement: Stored rank calls without a direction load as descending + +A persisted model, query or memory whose stored schema version predates this +change SHALL load with every `rank(` / `dense_rank(` call lacking a top-level +`direction=` in its Mode-B fields rewritten to carry `direction='desc'`, +preserving its pre-change ordering. The Mode-B fields are `ModelMeasure.formula` +and a query's `measures`, `filters`, `dimensions`, `time_dimensions`, `order` and +`main_time_dimension`, including queries nested in `source_queries`, an inline +query `source_model`, and `Memory.query`. Mode-A SQL (`Column.sql`, model +`filters`, `Column.filter`, aggregation templates) and `ntile` / `percent_rank` +calls SHALL never be rewritten. The rewrite SHALL apply only to a payload read +from storage or one that declares an explicit schema version older than the +current one; a payload without a version, or at the current version, SHALL be +left as written, so a bare call in it fails with the missing-direction error. The +rewrite SHALL be idempotent, SHALL leave a formula it cannot tokenise +byte-identical, and a migrated model SHALL be persisted back at the current +version on first load. + +#### Scenario: A stored model measure keeps its descending meaning + +- **WHEN** a model stored at the previous version holds the measure + `rank(sum(amount))` and is loaded +- **THEN** the measure reads `rank(sum(amount), direction='desc')`, the stored document + is rewritten at the current version, and querying it returns the descending ranks + +#### Scenario: A stored query's every Mode-B field is rewritten + +- **WHEN** a stored query-backed model's `source_queries` entry holds bare + `rank(` / `dense_rank(` calls in a measure, a filter, an order item and a computed + dimension expression, nested inside other calls and colon syntax +- **THEN** every call gains `direction='desc'` and nothing else in the formulas changes + +#### Scenario: Unversioned legacy documents are rewritten + +- **WHEN** a stored model with no `version`, whose nested source query also has no + `version`, or a stored memory (YAML and SQLite) with no `version` or with an + unversioned `query`, holds bare `rank(` calls +- **THEN** they load with `direction='desc'` filled in + +#### Scenario: A fresh payload is never filled in + +- **WHEN** a query or model with no `version`, or at the current version, is + submitted through the API, MCP or Python with a bare `rank(sum(amount))` +- **THEN** it fails with the missing-direction `TransformArgumentError` + +#### Scenario: A payload declaring an old version is treated as legacy + +- **WHEN** a query submitted with an explicit older `version` holds `rank(sum(amount))` +- **THEN** it is filled in with `direction='desc'`, exactly as a stored document would be + +#### Scenario: Calls that need no rewrite are untouched + +- **WHEN** a stored document holds `rank(sum(amount), direction='asc')`, the text + `rank(` inside a string literal, an attribute call `x.rank(`, `ntile(sum(amount), n=4)`, + `percent_rank(sum(amount))`, or `dense_rank() over (order by id)` in a `Column.sql` +- **THEN** each is left byte-identical + +#### Scenario: The rewrite is idempotent + +- **WHEN** an already-migrated document is migrated again +- **THEN** it is unchanged + +#### Scenario: An untokenisable formula still loads + +- **WHEN** a stored model's measure formula cannot be tokenised +- **THEN** the formula is left byte-identical and the model still loads + +## MODIFIED Requirements + +### Requirement: Transforms reject grain-refining row-level leaves + +A transform other than the aggregation-dispatched `first` / `last`, used in +measure, filter, or order position, or as a constituent of an aggregation +source, SHALL reject with a typed plan-time error — before any SQL is +generated — any row-level (non-aggregate) leaf in its input that refines the +consumer grain, that is, a leaf that is not itself a projected query +dimension. The error SHALL name the transform, the offending leaf, and the +remedies (aggregate the leaf, e.g. `cumsum(weight:sum)`; project it as a query +dimension; or compute it in an earlier `source_queries` stage), and cite no +tracking issue. The rule applies uniformly to every such transform op — the +rank family and the shift family (`time_shift`, `change`, `change_pct`) +included. Leaves that are projected grain keys — plain or computed dimensions +— remain legal, evaluated at the query grain. The raw source column of a +bucketed time dimension is not a projected grain key (it refines the bucket). +`first` / `last` keep their aggregation dispatch, and the stricter +dimension-position rules are unchanged. + +#### Scenario: Bare grain-refining leaf rejected +- **WHEN** a query with a month time dimension and no `weight` dimension selects + the measure `cumsum(weight)` +- **THEN** it fails at plan time with the typed error naming the remedy — never + SQL whose base grain is inflated to one row per (bucket, weight-value) + +#### Scenario: Rank family is covered +- **WHEN** a query over `[store]` with a month time dimension selects the + measure `rank(qty, direction='desc')` +- **THEN** it fails with the same typed error, never a result carrying one row + per (store, month, qty-value) + +#### Scenario: Shift family is covered +- **WHEN** a query with a month time dimension and no `weight` dimension + selects `time_shift(weight, -1)`, `change(weight)` or `change_pct(weight)`, + or `time_shift(hi_rev, -1)` over the unprojected derived column `hi_rev` +- **THEN** each fails at plan time with the same typed error naming the + transform, the leaf and the remedies — never a result whose row count differs + from the same query without the measure + +#### Scenario: Predicate over an unprojected row column rejected +- **WHEN** a query selects the measure `consecutive_periods(weight > 0)` with + `weight` not a query dimension +- **THEN** it fails with the same typed error naming the aggregate-the-leaf + remedy + +#### Scenario: A projected grain key stays legal +- **WHEN** a query projects `weight` as a dimension and selects the measure + `rank(weight, direction='desc')` +- **THEN** it compiles at the query grain and executes with correct values — + no error, no extra result rows + +#### Scenario: A projected grain key stays legal under the shift family +- **WHEN** a query over `[store]` with a month time dimension selects + `time_shift(store, -1)` +- **THEN** it compiles at the query grain and executes with the operand's own + value per row — no error, no extra result rows + +#### Scenario: Attached values do not launder a row leaf +- **WHEN** a query selects the measure + `cumsum(weight * avg(unit_price, partition_by=product))` with `weight` not + projected +- **THEN** it fails with the same typed error — a transform does not collapse + row grain, unlike an aggregation + +#### Scenario: Row leaf under a transform inside an aggregation source rejected +- **WHEN** a query selects the measure `sum(cumsum(weight) - 1)` with `weight` not a + query dimension +- **THEN** it fails at plan time with the same typed error naming the transform and + the aggregate-the-leaf remedy — an enclosing aggregation does not launder the + transform's row leaf + +#### Scenario: Projected grain key under a transform inside a source stays legal +- **WHEN** a query over `[region]` selects the measure `sum(rank(region, direction='desc'))` +- **THEN** it compiles: the transform types at the query grain and the aggregation is + the degenerate identity with the degenerate-re-aggregation warning, never an error + +### Requirement: Rank-family partition keys are operand-grain members +A rank-family transform (`rank`, `dense_rank`, `percent_rank`, `ntile`) partitions its +operand's cells, so every key in its own `partition_by=` SHALL be a member of the +transform's operand grain, in every position. The operand grain is the union over the +transform's input: an aggregate contributes its explicit `partition_by=` keys, else the +query grain (its dimensions and time buckets), plus the query's active time bucket when +it is windowed; a nested transform contributes its own operand grain, minus its time +axis when it is `first` or `last`; a composite contributes the union of its operands, +with a projected row-level leaf contributing itself; an input with no aggregate (only +row-level leaves or literals) is the query grain, since it is evaluated once per +query-grain cell. A non-member key SHALL fail at plan time with an error naming +the transform, the key, the operand grain and the remedy (add the key to the inner +aggregate's `partition_by=`, or partition by a member). A key that is no query dimension +at all keeps the existing "not a query dimension" error, and an ungrained inner aggregate +in dimension position keeps the existing grain-self-containment error; both take +precedence over the membership error. + +#### Scenario: Non-member query dimension as a measure +- **WHEN** a query over `[city, region, product]` selects `rank(sum(amount, partition_by=[city, region]), partition_by=product, direction='desc')` +- **THEN** planning fails with an error naming `rank`, `product`, the operand grain `city, region` and the `partition_by=` remedy — it never executes by widening the grain + +#### Scenario: Non-member query dimension in a filter +- **WHEN** the same query filters `rank(sum(amount, partition_by=[city, region]), partition_by=product, direction='desc') <= 2` +- **THEN** planning fails with the same error + +#### Scenario: Non-member key in dimension position, plain column +- **WHEN** a query over `[region]` declares the dimension `rank(sum(amount, partition_by=[city, product]), partition_by=region, direction='desc')` +- **THEN** planning fails with the same error naming `region` and the grain `city, product`, never with an internal producer-slot error + +#### Scenario: Non-member key in dimension position, computed-dimension name +- **WHEN** a query declares `ureg` = `upper(region)` and the dimension `rank(sum(amount, partition_by=[city, region]), partition_by=ureg, direction='desc')` +- **THEN** planning fails with the same error naming the key and the grain `city, region`, never with an internal error + +#### Scenario: Member key executes +- **WHEN** a query over `[city, region, product]` selects `rank(sum(amount, partition_by=[city, region]), partition_by=region, direction='desc')` +- **THEN** each row carries its (city, region) total's rank within the region: East Zeta 1, Delta 2, Epsilon 2; North Beta 1, Alpha 2; South Gamma 1, Alpha 2; Gap the NULL city 1, Kappa 2; Void Xi NULL (its total is NULL) + +#### Scenario: Ungrained inner keeps the query-dimension rule +- **WHEN** a query over the banded dimension alone selects `rank(sum(amount), partition_by=region, direction='desc')` +- **THEN** planning fails with the existing "partition_by column 'region' is not a query dimension" error listing the available dimensions; over `[region, band]` the same measure executes because the ungrained inner is grained at the query grain and `region` is a member + +#### Scenario: Windowed inner admits the active bucket +- **WHEN** a monthly query selects `rank(sum(amount, window='1y', partition_by=customers.regions.name), partition_by=ordered_at, direction='desc')` +- **THEN** the partition key passes the operand-grain rule as the query's month bucket, a member contributed by the windowed inner + +#### Scenario: Nested collapsing transform drops its axis +- **WHEN** a monthly query selects `rank(last(sum(amount, partition_by=[customers.regions.name, ordered_at])), partition_by=ordered_at, direction='desc')` +- **THEN** planning fails with the operand-grain error naming `ordered_at` and the grain `customers.regions.name`; with `partition_by=customers.regions.name` the key passes the rule + +#### Scenario: Aggregate-free input takes the query grain +- **WHEN** a query over `[city, region, product]` selects `rank(city, partition_by=region, direction='desc')` +- **THEN** it executes, ranking each row's city value descending within its region: East Zeta 1, Epsilon 2, Delta 3; North Beta 1, Alpha 2; South Gamma 1, Alpha 2; Gap Kappa 1, the NULL city NULL; Void Xi 1 + +#### Scenario: Residue error precedes the membership rule +- **WHEN** a query over `[region]` declares the dimension `rank(sum(amount), partition_by=region, direction='desc')` +- **THEN** planning fails with the existing grain-self-containment error ("must declare partition_by= explicitly"), not the operand-grain error diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/tasks.md b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/tasks.md new file mode 100644 index 00000000..e0ce0814 --- /dev/null +++ b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/tasks.md @@ -0,0 +1,116 @@ +## 1. Tests first (pr-tests stage) + +Expected values for the NULL-inner cases come from raw-row oracles over the `sales` +fixture (Void's total is NULL). The oracle used at plan time reproduces the corpus's +old `810 / 43` and gives `810 / 33` under NULL→NULL. + +- [ ] 1.1 Direction syntax and binding tests: every scenario of "Rank-family ordering + direction" (asc/desc/synonyms/non-numeric/partition_by/distinct keys/missing/invalid/ + forbidden/ntile-percent_rank ascending), executed on SQLite and DuckDB. Verify: they + fail against the current code. +- [ ] 1.2 Importer-parity tests: identical kwargs through `core/formula.py` + `parse_formula` and the binder raise the same `TransformArgumentError`. Verify: they fail + now. +- [ ] 1.3 NULL-semantics tests: every scenario of "Rank-family NULL inputs rank NULL", + covering all four functions with mixed-NULL, all-NULL-partition and partitioned + inputs on SQLite and DuckDB, plus T-SQL / postgres emission. Verify: they fail now. +- [ ] 1.4 Golden emission tests for the rank family on postgres, sqlite, duckdb, tsql and + bigquery, recorded after implementation. Verify: the golden files exist and are pinned. +- [ ] 1.5 Naming tests: the "Rank direction spelled as its bare value" scenario. Verify: + they fail now. +- [ ] 1.6 Migration tests: every scenario of "Stored rank calls without a direction load + as descending": + - stored v12 model with write-back to v13; + - `source_queries` across all Mode-B fields, nested calls and colon syntax; + - unversioned model with an unversioned nested query, and an inline-query + `source_model`; + - YAML and SQLite memories, unversioned and with an unversioned `query`; + - fresh payloads and current-version payloads still error; + - an explicit old version is filled in; + - untouched cases (string literal, `.rank(`, explicit direction, `ntile`/`percent_rank`, + `Column.sql`); + - idempotence; + - an untokenisable formula still loads. + Plus unit tests of the stored-only gate in `migrate()`. Verify: they fail now. +- [ ] 1.7 Mechanical fill of the ~98 existing test files that spell bare `rank(` / + `dense_rank(`: add `direction='desc'`. Approved test-logic changes, each limited to + what the plan dictates: + - expected values where an inner is NULL become NULL (e.g. the Void cell; `_dev1946` / + `_dev1919` oracles; the global `810 / 43` → `810 / 33`); + - unnamed rank result keys gain `_desc` / `_asc`; + - `ntile` / `percent_rank` expected values flip to ascending. + List every file whose expected values changed in the pr-tests handoff. Verify: + `poetry run pytest -m "not integration"` shows only intended failures. +- [ ] 1.8 Codex-review the tests against the specs. + +## 2. Core rule and error + +- [ ] 2.1 Add `TransformArgumentError(QueryTypeError)` to `slayer/core/errors.py`. + Verify: the error tests from 1.1 can import it. +- [ ] 2.2 Move the direction synonym table out of `core/query.py` into a core module that + `OrderItem` and the new rule both import, and add the core direction validator + (required / forbidden / literal / normalise / raise). Verify: `OrderItem` tests still + pass. +- [ ] 2.3 `core/formula.py`: route `_parse_transform_kwargs` through the validator, with + `direction` admitted for `rank` / `dense_rank`. Verify: 1.2 passes. + +## 3. Binding, naming, emission + +- [ ] 3.1 `engine/binding.py`: call the validator in `_bind_transform_params`, store + `("direction", ...)` in `TransformKey.kwargs`, and move the other transform-kwarg + `ValueError`s onto `TransformArgumentError`. Verify: 1.1 binding/error scenarios pass. +- [ ] 3.2 Render `direction` as its bare value in the canonical formula text used for + derived keys. Verify: 1.5 passes. +- [ ] 3.3 `sql/generator.py`: emit `ORDER BY v ASC|DESC` per `direction` for `rank` / + `dense_rank` and `ASC` for `ntile` / `percent_rank`, all wrapped in the NULL→NULL shape + (design decision 5) as sqlglot AST. Verify: 1.1 and 1.3 pass on SQLite and DuckDB, and + the T-SQL emission test passes. +- [ ] 3.4 Record the golden baselines and re-bless any existing golden SQL that changed + only by the CASE wrapper, `ASC` or the direction. Review each re-blessed diff. Verify: 1.4 + and the golden suites pass. + +## 4. Lazy migration + +- [ ] 4.1 `storage/migrations.py`: add the `stored_only` registration flag and its gate in + `migrate()`. Verify: the gate unit tests from 1.6 pass. +- [ ] 4.2 Add the token-level rewrite function in `storage` (stdlib `tokenize` only). + Verify: the rewrite edge-case tests pass. +- [ ] 4.3 Register the stored-only steps `SlayerModel` 12→13, `SlayerQuery` 4→5 and + `Memory` 2→3, including the nested-query stamping (design decision 2), and bump + `CURRENT_VERSIONS`. Verify: the 1.6 model/query/memory scenarios pass. +- [ ] 4.4 Stamp `version: 1` on unversioned stored dicts in `_migrate_and_refine_on_load` + and the YAML / SQLite memory load sites. Verify: the unversioned-legacy scenarios pass. + +## 5. Agent-facing text, docs, examples + +- [ ] 5.1 Update the rank-family line of the `query` tool description in + `slayer/mcp/server.py`, the suggestion strings in `slayer/sql/window_detect.py` and + `slayer/core/errors.py`, and `slayer/memories/help_content` if it spells a rank call. + Verify: grep finds no bare `rank(` / `dense_rank(` in `slayer/` outside migrations and + tests. +- [ ] 5.2 Update the docs: `docs/concepts/formulas.md` (function table and rank section: + direction, ascending `ntile` / `percent_rank`, NULL→NULL, `_asc` / `_desc` keys), + `queries.md`, `models.md`, `references.md`, `docs/database-support.md`, + `docs/dbt/dbt_import.md`, `docs/osi/osi_import.md`, and the `01_dynamic`, + `05_joined_measures`, `07_aggregations` and `15_duckdb` example pages. Verify: grep + finds no bare rank call in `docs/`. +- [ ] 5.3 Update `examples/` (`embedded`, `clickhouse`, `snowflake`, `verify_common.py`, + `comparisons/matrix.yaml` + `probes.yaml`). Verify: grep is clean and the matrix / + probe checks in the unit suite pass. +- [ ] 5.4 Re-execute every edited notebook in place + (`jupyter nbconvert --to notebook --execute --inplace`). Verify: committed outputs are + fresh and the notebook suite passes. +- [ ] 5.5 Write the release-notes entry in an untracked `RELEASE_NOTES_*.md`: required + direction, the `ntile` / `percent_rank` flip, NULL→NULL, renamed unnamed rank keys, lazy + migration. Never stage it. + +## 6. Gates + +- [ ] 6.1 `poetry run pytest -m "not integration"` is fully green. +- [ ] 6.2 The integration suite with the CI invocation from CLAUDE.md is green + (Postgres locally). +- [ ] 6.3 `poetry run ruff check slayer/ tests/` and `poetry run basedpyright` (no new + errors vs the baseline) are clean. +- [ ] 6.4 `uvx --no-build --from living-architecture==0.2.1 la-arch-check` is clean. +- [ ] 6.5 `openspec validate dev-2040-rank-family-ordering-required-direction-on-rankdense-rank --strict` + passes. From d01c6404077912ccc9f3816dd05043c806af129f Mon Sep 17 00:00:00 2001 From: Egor Kraev Date: Sat, 3 Oct 2026 15:42:55 +0200 Subject: [PATCH 02/11] Failing tests for rank-family ordering: required direction on rank/dense_rank, ascending ntile/percent_rank, NULL inputs rank NULL, lazy stored-only migration New: tests/test_rank_direction.py, tests/test_rank_direction_migration.py, tests/test_rank_direction_golden_sql.py (baseline recorded after implementation), tests/_rank_direction_fixtures.py. Existing rank calls gain direction='desc'; NULL-inner expected values become NULL; rank-family SQL pins check the window shape structurally. Spec/design: inline ModelExtension measures are migrated too. --- .../design.md | 3 +- .../specs/queries/transforms/spec.md | 3 +- .../tasks.md | 14 +- tests/_dev1832_fixtures.py | 4 +- tests/_dev1835_fixtures.py | 8 +- tests/_dev1837_fixtures.py | 12 +- tests/_dev1839_fixtures.py | 22 +- tests/_dev1919_fixtures.py | 2 +- tests/_dev1946_fixtures.py | 44 +- tests/_dev1953_fixtures.py | 6 +- tests/_rank_direction_fixtures.py | 135 ++++++ tests/integration/test_integration.py | 4 +- .../integration/test_integration_snowflake.py | 4 +- tests/perf/compare/corpus.py | 6 +- tests/perf/test_bench.py | 2 +- tests/test_dev1450fix_group2_correctness.py | 2 +- tests/test_dev1712_order_only_hidden_slots.py | 18 +- ..._dev1733_order_only_transform_composite.py | 27 +- tests/test_dev1739_guards.py | 2 +- tests/test_dev1824_computed_dim_execution.py | 2 +- tests/test_dev1824_golden_sql.py | 2 +- tests/test_dev1824_partitioned_execution.py | 2 +- tests/test_dev1824_remaining_guards.py | 2 +- tests/test_dev1832_fixtures_smoke.py | 3 - tests/test_dev1832_golden_sql.py | 4 +- tests/test_dev1832_transform_source.py | 21 +- tests/test_dev1835_grain_prune.py | 2 +- tests/test_dev1835_semantic_pins.py | 2 +- tests/test_dev1836_matrix_flip.py | 4 +- .../test_dev1837_dimension_measure_matrix.py | 4 +- tests/test_dev1839_golden_sql.py | 6 +- tests/test_dev1839_guards.py | 10 +- .../test_dev1842_binder_measure_resolution.py | 2 +- tests/test_dev1847_consumers.py | 2 +- tests/test_dev1850_keyless_grain.py | 4 +- tests/test_dev1859_golden_sql.py | 4 +- tests/test_dev1859_transform_row_leaf.py | 16 +- tests/test_dev1894_query_type_errors.py | 2 +- tests/test_dev1903_opacity.py | 2 +- tests/test_dev1903_producer_flag.py | 2 +- tests/test_dev1903_transform_inputs.py | 4 +- tests/test_dev1911_fanning_partition_key.py | 2 +- tests/test_dev1946_transform_parameter.py | 4 +- tests/test_dev1953_partition_alias.py | 41 +- tests/test_dev1953_partition_membership.py | 54 +-- tests/test_dev1958_row_leaf_ban.py | 2 + tests/test_dev1964_dual_phase_consumption.py | 28 +- tests/test_dev1967_stage_grain.py | 2 +- tests/test_dev1976_dimension_values.py | 18 +- tests/test_dev2013_expression_attribution.py | 2 +- tests/test_distinct_dimension_values.py | 4 +- tests/test_error_messages.py | 2 +- tests/test_functional_agg_positions.py | 2 +- tests/test_functional_aggregations.py | 4 +- tests/test_memories_resolver_typed.py | 2 +- tests/test_projection_trim.py | 32 +- tests/test_query_backed_typed_expansion.py | 8 +- tests/test_rank_direction.py | 440 ++++++++++++++++++ tests/test_rank_direction_golden_sql.py | 57 +++ tests/test_rank_direction_migration.py | 434 +++++++++++++++++ tests/test_reagg_outer_grain_attribution.py | 2 +- tests/test_schema_drift_typed.py | 4 +- tests/test_sql_generator.py | 109 ++--- tests/test_sql_predicate.py | 2 +- tests/test_syntax.py | 22 +- tests/test_transforms_planner.py | 14 +- 66 files changed, 1374 insertions(+), 337 deletions(-) create mode 100644 tests/_rank_direction_fixtures.py create mode 100644 tests/test_rank_direction.py create mode 100644 tests/test_rank_direction_golden_sql.py create mode 100644 tests/test_rank_direction_migration.py diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/design.md b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/design.md index 9fb388fa..2206a8a6 100644 --- a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/design.md +++ b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/design.md @@ -69,7 +69,8 @@ finding 6, rejected). - Applied to: `measures[].formula` for models; for queries, string or dict `measures` (`formula`), `filters`, `dimensions` (string or `expression`), - `time_dimensions` (string or `dimension`), `order[].column`, `main_time_dimension`. + `time_dimensions` (string or `dimension`), `order[].column`, `main_time_dimension`, + and `source_model.measures[].formula` when the `source_model` is an inline extension. 4. **One direction rule in core.** - A core function validates a rank-family call's `direction`: required for `rank` / `dense_rank`, forbidden for `ntile` / `percent_rank`, string-literal only. diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/transforms/spec.md b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/transforms/spec.md index 27d531ab..442f387a 100644 --- a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/transforms/spec.md +++ b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/transforms/spec.md @@ -158,7 +158,8 @@ change SHALL load with every `rank(` / `dense_rank(` call lacking a top-level preserving its pre-change ordering. The Mode-B fields are `ModelMeasure.formula` and a query's `measures`, `filters`, `dimensions`, `time_dimensions`, `order` and `main_time_dimension`, including queries nested in `source_queries`, an inline -query `source_model`, and `Memory.query`. Mode-A SQL (`Column.sql`, model +query `source_model`, the measures of an inline `ModelExtension` `source_model`, +and `Memory.query`. Mode-A SQL (`Column.sql`, model `filters`, `Column.filter`, aggregation templates) and `ntile` / `percent_rank` calls SHALL never be rewritten. The rewrite SHALL apply only to a payload read from storage or one that declares an explicit schema version older than the diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/tasks.md b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/tasks.md index e0ce0814..4fc8fa50 100644 --- a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/tasks.md +++ b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/tasks.md @@ -4,21 +4,21 @@ Expected values for the NULL-inner cases come from raw-row oracles over the `sal fixture (Void's total is NULL). The oracle used at plan time reproduces the corpus's old `810 / 43` and gives `810 / 33` under NULL→NULL. -- [ ] 1.1 Direction syntax and binding tests: every scenario of "Rank-family ordering +- [x] 1.1 Direction syntax and binding tests: every scenario of "Rank-family ordering direction" (asc/desc/synonyms/non-numeric/partition_by/distinct keys/missing/invalid/ forbidden/ntile-percent_rank ascending), executed on SQLite and DuckDB. Verify: they fail against the current code. -- [ ] 1.2 Importer-parity tests: identical kwargs through `core/formula.py` +- [x] 1.2 Importer-parity tests: identical kwargs through `core/formula.py` `parse_formula` and the binder raise the same `TransformArgumentError`. Verify: they fail now. -- [ ] 1.3 NULL-semantics tests: every scenario of "Rank-family NULL inputs rank NULL", +- [x] 1.3 NULL-semantics tests: every scenario of "Rank-family NULL inputs rank NULL", covering all four functions with mixed-NULL, all-NULL-partition and partitioned inputs on SQLite and DuckDB, plus T-SQL / postgres emission. Verify: they fail now. - [ ] 1.4 Golden emission tests for the rank family on postgres, sqlite, duckdb, tsql and bigquery, recorded after implementation. Verify: the golden files exist and are pinned. -- [ ] 1.5 Naming tests: the "Rank direction spelled as its bare value" scenario. Verify: +- [x] 1.5 Naming tests: the "Rank direction spelled as its bare value" scenario. Verify: they fail now. -- [ ] 1.6 Migration tests: every scenario of "Stored rank calls without a direction load +- [x] 1.6 Migration tests: every scenario of "Stored rank calls without a direction load as descending": - stored v12 model with write-back to v13; - `source_queries` across all Mode-B fields, nested calls and colon syntax; @@ -32,7 +32,7 @@ old `810 / 43` and gives `810 / 33` under NULL→NULL. - idempotence; - an untokenisable formula still loads. Plus unit tests of the stored-only gate in `migrate()`. Verify: they fail now. -- [ ] 1.7 Mechanical fill of the ~98 existing test files that spell bare `rank(` / +- [x] 1.7 Mechanical fill of the ~98 existing test files that spell bare `rank(` / `dense_rank(`: add `direction='desc'`. Approved test-logic changes, each limited to what the plan dictates: - expected values where an inner is NULL become NULL (e.g. the Void cell; `_dev1946` / @@ -41,7 +41,7 @@ old `810 / 43` and gives `810 / 33` under NULL→NULL. - `ntile` / `percent_rank` expected values flip to ascending. List every file whose expected values changed in the pr-tests handoff. Verify: `poetry run pytest -m "not integration"` shows only intended failures. -- [ ] 1.8 Codex-review the tests against the specs. +- [x] 1.8 Codex-review the tests against the specs. ## 2. Core rule and error diff --git a/tests/_dev1832_fixtures.py b/tests/_dev1832_fixtures.py index f168f1e4..7365de0f 100644 --- a/tests/_dev1832_fixtures.py +++ b/tests/_dev1832_fixtures.py @@ -438,8 +438,8 @@ async def make_exec_engine( ("South", "2024-02"): 95.0} #: sum(rank(amount:sum(window='90d', partition_by=region))) by month: the trailing-90d #: per-region sum (North 10/30/60, South 5/20, West NULL), ranked DESC over the -#: (region, month) cells (NULLs last) → North 4/2/1, South 5/3, West 6; summed per month. -WINDOWED_INNER_BY_MONTH = {"2024-01": 9.0, "2024-02": 11.0, "2024-03": 1.0} +#: (region, month) cells → North 4/2/1, South 5/3, West NULL; summed per month. +WINDOWED_INNER_BY_MONTH = {"2024-01": 9.0, "2024-02": 5.0, "2024-03": 1.0} #: sum(cumsum(amount:sum(partition_by=[customers.tier, ordered_at]))) rooted at orders, #: by month — the to-one cross-model partition key (customers.tier is determined from #: orders): amount:sum per (tier, month), cumsum per tier over months, summed per month. diff --git a/tests/_dev1835_fixtures.py b/tests/_dev1835_fixtures.py index c46047b0..33195895 100644 --- a/tests/_dev1835_fixtures.py +++ b/tests/_dev1835_fixtures.py @@ -40,11 +40,11 @@ CHANGE_OVER_LAST / CHANGE_PCT_OVER_LAST change(amount:last): (N,Feb)=30-20=10; change_pct: 10/20=0.5; rest NULL. UNION_WM_RANK dimension ``rank(amount:sum(window='90d', partition_by=region) - - amount:sum(partition_by=region))``, month TD. Union grain = + - amount:sum(partition_by=region), direction='desc')``, month TD. Union grain = (region, bucket); diffs w90-total: (N,Jan)=-70 (N,Feb)=0 (S,Jan)=-25 (S,Mar)=0 (NULL,Mar)=0 → RANK() desc 5/1/4/1/1. UNION_RK_RANK dimension ``rank(amount:last(partition_by=region) - - amount:sum(partition_by=city))``. Union grain = (region, city); + amount:sum(partition_by=city), direction='desc')``. Union grain = (region, city); region-last broadcast (30/25/60) minus city totals (30/40/30/50/60): 0/-10/0/-25/0 → RANK() desc 1/4/1/5/1. ORDER_BY_W90_DESC (region, month) keys ordered by the hidden windowed value @@ -204,10 +204,10 @@ # --------------------------------------------------------------------------- # UNION_WM_DIM = ( "rank(amount:sum(window='90d', partition_by=region) - " - "amount:sum(partition_by=region))" + "amount:sum(partition_by=region), direction='desc')" ) UNION_RK_DIM = ( - "rank(amount:last(partition_by=region) - amount:sum(partition_by=city))" + "rank(amount:last(partition_by=region) - amount:sum(partition_by=city), direction='desc')" ) UNION_WM_RANK = { ("North", "2024-01"): 5, ("North", "2024-02"): 1, diff --git a/tests/_dev1837_fixtures.py b/tests/_dev1837_fixtures.py index a28357ed..61753d4d 100644 --- a/tests/_dev1837_fixtures.py +++ b/tests/_dev1837_fixtures.py @@ -11,9 +11,9 @@ D-expr ``region, lower(city) AS lc`` (scalar expression) D-band ``region, BAND35 AS band`` (banded row attach) D-bare ``region, amount:sum(partition_by=city) AS ct`` (bare row attach) -D-rank ``region, rank(amount:sum(partition_by=region)) AS rr`` (transform root) +D-rank ``region, rank(amount:sum(partition_by=region), direction='desc') AS rr`` (transform root) D-mixed ``region, city, MIXED_DIM AS mr`` (union-grain transform root, DEV-1839: - rank(region_total - city_total) → (N,CityA)=1 (N,CityB)=3 (N,NULL)=1 + rank(region_total - city_total, direction='desc') → (N,CityA)=1 (N,CityB)=3 (N,NULL)=1 (S,CityC)=4 (NULL,CityD)=4; monthly series per group = D-expr's) Group grains (m = ``amount:sum``, from the DEV-1739 rows) @@ -40,7 +40,7 @@ * ``cumsum`` accumulates within the full dimension grain. * ``consecutive_periods(amount:sum > 28)`` counts existing buckets, resetting on a failed predicate (25 fails, 30/40/60/70 pass). -* ``rank(amount:sum)`` (measure context) ranks the whole result DESC, RANK() +* ``rank(amount:sum, direction='desc')`` (measure context) ranks the whole result DESC, RANK() ties; no time dimension in those cells. The fixed M-part × temporal-transform shape (``partitioned-aggregates`` delta): @@ -74,9 +74,9 @@ dev1837_models = dev1824_models -RANK_DIM = "rank(amount:sum(partition_by=region))" +RANK_DIM = "rank(amount:sum(partition_by=region), direction='desc')" BARE_DIM = "amount:sum(partition_by=city)" -MIXED_DIM = "rank(amount:sum(partition_by=region) - amount:sum(partition_by=city))" +MIXED_DIM = "rank(amount:sum(partition_by=region) - amount:sum(partition_by=city), direction='desc')" CP_PRED = "consecutive_periods(amount:sum > 28)" #: Dimension-family query dimensions, in projection order. @@ -98,7 +98,7 @@ "change_pct": "change_pct(amount:sum)", "cumsum": "cumsum(amount:sum)", "consecutive_periods": CP_PRED, - "rank": "rank(amount:sum)", + "rank": "rank(amount:sum, direction='desc')", } #: Transform ops that need the month time dimension (rank is timeless). TD_TRANSFORM_OPS = frozenset(TRANSFORM_FORMULAS) - {"rank"} diff --git a/tests/_dev1839_fixtures.py b/tests/_dev1839_fixtures.py index 78121279..7c21fc2e 100644 --- a/tests/_dev1839_fixtures.py +++ b/tests/_dev1839_fixtures.py @@ -12,16 +12,16 @@ CITY_TOTAL (N,CityA)=30 (N,CityB)=40 (N,NULL)=30 (S,CityC)=50 (NULL,CityD)=60 (city values are region-unique, so partition_by=city matches). -MIXED_RANK rank(region_total - city_total) over the (region, city) union: +MIXED_RANK rank(region_total - city_total, direction='desc') over the (region, city) union: diffs 70 / 60 / 70 / 0 / 0 → RANK() desc 1 / 3 / 1 / 4 / 4. -KEYLESS_RANK rank(region_total / grand_total) over the region union: +KEYLESS_RANK rank(region_total / grand_total, direction='desc') over the region union: shares 100/210, 50/210, 60/210 → North=1 NULL=2 South=3. (A keyless divisor misgrained to region makes every share 1.0 and every rank tie at 1.) -SUBSET_RANK rank(citypair_total - region_total): the (region, city)-grain +SUBSET_RANK rank(citypair_total - region_total, direction='desc'): the (region, city)-grain aggregate is exactly the union, region is a strict subset; diffs -70 / -60 / -70 / 0 / 0 → ranks 4 / 3 / 4 / 1 / 1. -NESTED_RANK rank(cumsum(region_month_total) - city_total) over the +NESTED_RANK rank(cumsum(region_month_total) - city_total, direction='desc') over the (region, city, month) union. cumsum at its own (region, month) grain (months within region): (N,Jan)=30 (N,Feb)=100 (S,Jan)=25 (S,Mar)=50 (NULL,Mar)=60. Union-row values: (N,CityA,Jan)=0 @@ -32,7 +32,7 @@ DUAL_MEASURE MIXED_RANK's expression as a MEASURE at the (region, city, channel) query grain: broadcast diffs 70×3, 60, 0×3 → ranks 1/1/1/4/5/5/5. -SAMEGRAIN_RANK rank(amount_region + ok_region): 200 / 75 / 120 → +SAMEGRAIN_RANK rank(amount_region + ok_region, direction='desc'): 200 / 75 / 120 → North=1 NULL=2 South=3 (ok_amount region totals 100 / 25 / 60). MEASURE_DIFF region_total - city_total per (region, city) row: 70/60/70/0/0. SAMEGRAIN_DIFF amount_region - ok_region per region: N=0 S=25 NULL=0. @@ -66,21 +66,21 @@ # --------------------------------------------------------------------------- # # Dimension expressions under test. # --------------------------------------------------------------------------- # -MIXED_RANK = "rank(amount:sum(partition_by=region) - amount:sum(partition_by=city))" -KEYLESS_RANK = "rank(amount:sum(partition_by=region) / amount:sum(partition_by=[]))" +MIXED_RANK = "rank(amount:sum(partition_by=region) - amount:sum(partition_by=city), direction='desc')" +KEYLESS_RANK = "rank(amount:sum(partition_by=region) / amount:sum(partition_by=[]), direction='desc')" SUBSET_RANK = ( - "rank(amount:sum(partition_by=[region, city]) - amount:sum(partition_by=region))" + "rank(amount:sum(partition_by=[region, city]) - amount:sum(partition_by=region), direction='desc')" ) NESTED_RANK = ( "rank(cumsum(amount:sum(partition_by=[region, ordered_at])) - " - "amount:sum(partition_by=city))" + "amount:sum(partition_by=city), direction='desc')" ) EXPLICIT_PART_RANK = ( "rank(amount:sum(partition_by=region) - amount:sum(partition_by=city), " - "partition_by=region)" + "partition_by=region, direction='desc')" ) SAMEGRAIN_RANK = ( - "rank(amount:sum(partition_by=region) + ok_amount:sum(partition_by=region))" + "rank(amount:sum(partition_by=region) + ok_amount:sum(partition_by=region), direction='desc')" ) MEASURE_DIFF = "amount:sum(partition_by=region) - amount:sum(partition_by=city)" SAMEGRAIN_DIFF = ( diff --git a/tests/_dev1919_fixtures.py b/tests/_dev1919_fixtures.py index deb60eb5..1cd3c223 100644 --- a/tests/_dev1919_fixtures.py +++ b/tests/_dev1919_fixtures.py @@ -35,7 +35,7 @@ "weight=sum(customers.regions.pop, partition_by=customers.regions.name), " "partition_by=customers.regions.name))") RANKED = ("customers.spend:weighted_avg(" - "weight=rank(sum(amount, partition_by=customers.regions.name)))") + "weight=rank(sum(amount, partition_by=customers.regions.name), direction='desc'))") WINDOWED_PARAM = ("customers.spend:weighted_avg(window='1y', " "weight=sum(amount, partition_by=customers.regions.name))") WINDOWED_CUSTOM_PARAM = ("customers.spend:wsum(window='1y', " diff --git a/tests/_dev1946_fixtures.py b/tests/_dev1946_fixtures.py index 1796c003..0c753db1 100644 --- a/tests/_dev1946_fixtures.py +++ b/tests/_dev1946_fixtures.py @@ -1,5 +1,5 @@ """Shared fixtures for DEV-1946 — a grained transform as an aggregation -parameter (``weight=rank(...)``, ``weight=cumsum(...)``). +parameter (``weight=rank(..., direction='desc')``, ``weight=cumsum(...)``). Raw-row oracles over the DEV-1840 orders→customers→regions graph and the DEV-1847 sales graph; every constant the delta scenarios cite is re-derived @@ -35,38 +35,38 @@ # Formula constants (one per delta scenario shape). # --------------------------------------------------------------------------- # RANKED_POSITIONAL = ("customers.spend:weighted_avg(" - "rank(sum(amount, partition_by=customers.regions.name)))") + "rank(sum(amount, partition_by=customers.regions.name), direction='desc'))") LAST_PARAM = ("customers.spend:weighted_avg(weight=last(sum(amount, " "partition_by=[customers.regions.name, ordered_at])))") DET_CUMSUM = ("customers.spend:weighted_avg(weight=cumsum(sum(amount, " "partition_by=[customers.regions.name, customers.signup_at])))") NOAXIS_CUMSUM = ("customers.spend:weighted_avg(weight=cumsum(sum(amount, " "partition_by=customers.regions.name)))") -UNGRAINED_RANK = "customers.spend:weighted_avg(weight=rank(sum(amount)))" +UNGRAINED_RANK = "customers.spend:weighted_avg(weight=rank(sum(amount), direction='desc'))" WINDOWED_INNER = ("customers.spend:weighted_avg(weight=rank(sum(amount, " - "window='1y', partition_by=customers.regions.name)))") + "window='1y', partition_by=customers.regions.name), direction='desc'))") WINDOWED_OUTER = ("customers.spend:weighted_avg(window='1y', " - "weight=rank(sum(amount, partition_by=customers.regions.name)))") + "weight=rank(sum(amount, partition_by=customers.regions.name), direction='desc'))") NESTED = ("customers.spend:weighted_avg(weight=weighted_avg(amount, " - "weight=rank(sum(amount, partition_by=customers.regions.name)), " + "weight=rank(sum(amount, partition_by=customers.regions.name), direction='desc'), " "partition_by=customers.regions.name))") XMODEL_LOCAL = ("amount:weighted_avg(weight=rank(sum(customers.spend, " - "partition_by=customers.regions.name)))") + "partition_by=customers.regions.name), direction='desc'))") OWN_PARTITION = ("customers.spend:weighted_avg(weight=rank(" "sum(amount, partition_by=[customers.regions.name, customers.tier]), " - "partition_by=customers.regions.name))") + "partition_by=customers.regions.name, direction='desc'))") #: refused at bind: a transform is not a valid first/last ranking key. LAST_RANK_KEY = ("customers.spend:last(rank(sum(amount, " - "partition_by=customers.regions.name)))") + "partition_by=customers.regions.name), direction='desc'))") #: refused at bind (non-goal): an expression-valued parameter. EXPR_ARG = "weighted_avg(amount, weight=quantity * 2)" -LOCAL_SALES = "weighted_avg(amount, weight=rank(sum(amount, partition_by=region)))" -LOCAL_SALES_CITY = "weighted_avg(amount, weight=rank(sum(amount, partition_by=city)))" +LOCAL_SALES = "weighted_avg(amount, weight=rank(sum(amount, partition_by=region), direction='desc'))" +LOCAL_SALES_CITY = "weighted_avg(amount, weight=rank(sum(amount, partition_by=city), direction='desc'))" REAGG_RANK_COUNT = ("weighted_avg(sum(amount, partition_by=[city, region]), " - "weight=rank(count(id, partition_by=[city, region])))") + "weight=rank(count(id, partition_by=[city, region]), direction='desc'))") REAGG_RANK_PRODUCT = ("weighted_avg(sum(amount, partition_by=[city, region]), " - "weight=rank(count(id, partition_by=product)))") + "weight=rank(count(id, partition_by=product), direction='desc'))") # --------------------------------------------------------------------------- # # Raw-row projections. @@ -93,24 +93,23 @@ def _wavg(pairs) -> Optional[float]: def _rank_desc(totals: Dict) -> Dict: - """Descending dense-competition rank; a NULL total ranks last.""" - nonnull = sum(1 for x in totals.values() if x is not None) + """Descending competition rank; a NULL total ranks NULL.""" def r(v): if v is None: - return 1 + nonnull + return None return 1 + sum(1 for x in totals.values() if x is not None and x > v) return {k: r(v) for k, v in totals.items()} def _rank_amount() -> Dict[Optional[str], int]: - """rank(sum(amount, partition_by=region name)) — North 1, NULL 2, South 3.""" + """rank(sum(amount, partition_by=region name), direction='desc') — North 1, NULL 2, South 3.""" return _rank_desc(region_amount_totals()) def _rank_spend() -> Dict[Optional[str], int]: - """rank(sum(customers.spend, partition_by=region name)) — North 1, South 2, NULL 3.""" + """rank(sum(customers.spend, partition_by=region name), direction='desc') — North 1, South 2, NULL 3.""" return _rank_desc(region_spend_totals()) @@ -215,7 +214,7 @@ def windowed_outer_by_signup_month() -> Dict[Optional[str], Optional[float]]: def _nested_middle() -> Dict[Optional[str], Optional[float]]: - """weighted_avg(amount, weight=rank(region), partition_by=region) per region — + """weighted_avg(amount, weight=rank(region, direction='desc'), partition_by=region) per region — the rank is constant per region so this is the plain order-amount mean: North 50/3, South 10, NULL 23.5.""" rank = _rank_amount() @@ -255,7 +254,7 @@ def xmodel_by_status() -> Dict[str, Optional[float]]: def _own_rank() -> Dict[Tuple[Optional[str], Optional[str]], int]: - """rank(sum(amount, partition_by=[region, tier]), partition_by=region) — + """rank(sum(amount, partition_by=[region, tier]), partition_by=region, direction='desc') — descending within each region over its (region, tier) cells.""" cell: Dict[Tuple[Optional[str], Optional[str]], float] = defaultdict(float) for _i, c, _s, a, _m in _ORD: @@ -311,8 +310,7 @@ def local_sales_by_region() -> Dict[str, Optional[float]]: def local_sales_global() -> Optional[float]: - """LOCAL_SALES with no dimensions — 810/43 (Void rows weight the denominator, - contribute no numerator).""" + """LOCAL_SALES with no dimensions — 810/33 (Void's NULL rank weights nothing).""" rank = _rank_desc(_sales_totals(1)) return _wavg([(a, rank[r]) for _i, r, _c, _p, a, *_x in _SALES_ROWS_WIDE]) @@ -435,7 +433,7 @@ def verify_oracles() -> None: _approx_map(local_sales_by_region(), {"North": 22.5, "South": 140 / 3, "East": 60.0, "Gap": 20 / 3, "Void": None}) - _approx(local_sales_global(), 810 / 43) + _approx(local_sales_global(), 810 / 33) _approx_map(local_sales_by_region_city(), { ("North", "Alpha"): 10.0, ("North", "Beta"): 60.0, ("South", "Alpha"): 20.0, ("South", "Gamma"): 100.0, diff --git a/tests/_dev1953_fixtures.py b/tests/_dev1953_fixtures.py index 5398618e..86761b09 100644 --- a/tests/_dev1953_fixtures.py +++ b/tests/_dev1953_fixtures.py @@ -25,8 +25,8 @@ def cell_totals(key: Callable[[tuple], Tuple]) -> Dict[Tuple, Optional[float]]: return acc -def rank_within(totals: Dict[Tuple, Optional[float]]) -> Dict[Tuple, int]: - """Descending competition rank within ``k[0]``; NULL ranks last.""" +def rank_within(totals: Dict[Tuple, Optional[float]]) -> Dict[Tuple, Optional[int]]: + """Descending competition rank within ``k[0]``; a NULL total ranks NULL.""" groups: Dict = defaultdict(dict) for k, v in totals.items(): groups[k[0]][k] = v @@ -34,7 +34,7 @@ def rank_within(totals: Dict[Tuple, Optional[float]]) -> Dict[Tuple, int]: for cells in groups.values(): nonnull = [v for v in cells.values() if v is not None] for k, v in cells.items(): - out[k] = (1 + len(nonnull)) if v is None else 1 + sum(1 for x in nonnull if x > v) + out[k] = None if v is None else 1 + sum(1 for x in nonnull if x > v) return out diff --git a/tests/_rank_direction_fixtures.py b/tests/_rank_direction_fixtures.py new file mode 100644 index 00000000..a588ea95 --- /dev/null +++ b/tests/_rank_direction_fixtures.py @@ -0,0 +1,135 @@ +"""Shared fixtures for rank-family ordering direction and NULL-input semantics over the sales graph.""" + +from __future__ import annotations + +from collections import defaultdict +from collections.abc import Mapping +from typing import Any, Callable, Dict, List, Optional, Tuple + +import sqlglot +from pydantic import BaseModel +from sqlglot import exp +from sqlglot.expressions.core import Expression + +from tests._dev1847_fixtures import _SALES_ROWS_WIDE + +RANK_FNS = (exp.Rank, exp.DenseRank, exp.PercentRank, exp.Ntile) +DIALECTS = ["postgres", "sqlite", "duckdb", "tsql", "bigquery"] + +#: sum(amount) by region; Void's two rows are all-NULL. +REGION_TOTALS = {"North": 90.0, "South": 140.0, "East": 180.0, "Gap": 20.0, "Void": None} +RANK_ASC = {"Gap": 1, "North": 2, "South": 3, "East": 4, "Void": None} +RANK_DESC = {"East": 1, "South": 2, "North": 3, "Gap": 4, "Void": None} +NTILE2 = {"Gap": 1, "North": 1, "South": 2, "East": 2, "Void": None} +PERCENT_RANK = {"Gap": 0.0, "North": 1 / 3, "South": 2 / 3, "East": 1.0, "Void": None} +#: rank(min(city), direction='asc') by region: Alpha, Alpha, Delta, Kappa, Xi. +MIN_CITY_RANK_ASC = {"North": 1, "South": 1, "East": 3, "Gap": 4, "Void": 5} +#: rank(sum(amount), partition_by=region, direction='asc') over [region, city]. +CITY_RANK_ASC = { + ("East", "Delta"): 1, ("East", "Epsilon"): 1, ("East", "Zeta"): 3, + ("North", "Alpha"): 1, ("North", "Beta"): 2, + ("South", "Alpha"): 1, ("South", "Gamma"): 2, + ("Gap", "Kappa"): 1, ("Gap", None): 2, + ("Void", "Xi"): None, +} +#: dense_rank(sum(amount), partition_by=region, direction='desc') over [region, city]. +CITY_DENSE_DESC = { + ("East", "Zeta"): 1, ("East", "Delta"): 2, ("East", "Epsilon"): 2, + ("North", "Beta"): 1, ("North", "Alpha"): 2, + ("South", "Gamma"): 1, ("South", "Alpha"): 2, + ("Gap", None): 1, ("Gap", "Kappa"): 2, + ("Void", "Xi"): None, +} +#: rank(city, partition_by=region, direction='asc') over [region, city]. +CITY_NAME_RANK_ASC = { + ("East", "Delta"): 1, ("East", "Epsilon"): 2, ("East", "Zeta"): 3, + ("North", "Alpha"): 1, ("North", "Beta"): 2, + ("South", "Alpha"): 1, ("South", "Gamma"): 2, + ("Gap", "Kappa"): 1, ("Gap", None): None, + ("Void", "Xi"): 1, +} + + +def cell_totals(key: Callable[[tuple], Tuple]) -> Dict[Tuple, Optional[float]]: + """sum(amount) per ``key(row)``; an all-NULL cell is NULL.""" + acc: Dict[Tuple, Optional[float]] = {} + for row in _SALES_ROWS_WIDE: + k, a = key(row), row[4] + prev = acc.get(k) + acc[k] = prev if a is None else (prev or 0.0) + a + return acc + + +def rank_within(values: Mapping[Tuple, Any], *, descending: bool, dense: bool = False) -> Dict[Tuple, Optional[int]]: + """Competition (or dense) rank within ``k[0]``; a NULL value ranks NULL.""" + groups: Dict = defaultdict(dict) + for k, v in values.items(): + groups[k[0]][k] = v + out: Dict[Tuple, Optional[int]] = {} + for cells in groups.values(): + nonnull = [v for v in cells.values() if v is not None] + for k, v in cells.items(): + if v is None: + out[k] = None + continue + ahead = [x for x in nonnull if (x > v if descending else x < v)] + out[k] = 1 + (len(set(ahead)) if dense else len(ahead)) + return out + + +class RankWindow(BaseModel): + """The ordering-relevant shape of one emitted rank-family window.""" + + fn: str + order_sql: str + descending: bool + partition_sql: List[str] + null_flag: bool + null_guarded: bool + + +def _is_null_test(node: Expression, target: str, dialect: str) -> bool: + return isinstance(node, exp.Is) and isinstance(node.expression, exp.Null) and ( + node.this.sql(dialect=dialect) == target) + + +def _is_null_flag(node: Expression, target: str, dialect: str) -> bool: + """``CASE WHEN IS NULL THEN 1 ELSE 0 END``.""" + if not isinstance(node, exp.Case) or len(node.args.get("ifs") or []) != 1: + return False + [branch] = node.args["ifs"] + default = node.args.get("default") + return (_is_null_test(branch.this, target, dialect) + and branch.args.get("true") is not None and branch.args["true"].sql() == "1" + and default is not None and default.sql() == "0") + + +def _is_null_guard(window: exp.Window, target: str, dialect: str) -> bool: + """The window is the ELSE of ``CASE WHEN IS NULL THEN NULL ELSE END``.""" + case = window.parent + if not isinstance(case, exp.Case) or case.args.get("default") is not window: + return False + ifs = case.args.get("ifs") or [] + return (len(ifs) == 1 and _is_null_test(ifs[0].this, target, dialect) + and isinstance(ifs[0].args.get("true"), exp.Null)) + + +def rank_windows(sql: str, *, dialect: str) -> List[RankWindow]: + """Every rank-family window in ``sql``, in document order.""" + out: List[RankWindow] = [] + for window in sqlglot.parse_one(sql, read=dialect).find_all(exp.Window): + if not isinstance(window.this, RANK_FNS): + continue + [ordered] = window.args["order"].expressions + target = ordered.this.sql(dialect=dialect) + parts = window.args.get("partition_by") or [] + flags = [p for p in parts if _is_null_flag(p, target, dialect)] + out.append(RankWindow( + fn=type(window.this).__name__.upper(), + order_sql=target, + descending=bool(ordered.args.get("desc")), + partition_sql=[p.sql(dialect=dialect) for p in parts if p not in flags], + null_flag=len(flags) == 1, + null_guarded=_is_null_guard(window, target, dialect), + )) + return out diff --git a/tests/integration/test_integration.py b/tests/integration/test_integration.py index 4eede72a..9635dae4 100644 --- a/tests/integration/test_integration.py +++ b/tests/integration/test_integration.py @@ -2642,7 +2642,7 @@ async def test_filter_on_windowed_column_sqlite_raises(planets_env): """End-to-end: filtering on a `Column.sql` with a window function used to auto-promote to a post-aggregation outer WHERE (DEV-1336). DEV-1369 removes that escape hatch — users must use rank-family transforms - (`rank() <= 3`) or factor the windowed column into a + (`rank(, direction='desc') <= 3`) or factor the windowed column into a multi-stage `source_queries` model instead. The engine raises a clear error with that suggestion.""" engine = planets_env @@ -3125,7 +3125,7 @@ async def test_dense_rank_partition_by_customer_executes(integration_env): measures=[ ModelMeasure(formula="amount:sum"), ModelMeasure( - formula="dense_rank(amount:sum, partition_by=customer_id)", + formula="dense_rank(amount:sum, partition_by=customer_id, direction='desc')", name="amt_rank", ), ], diff --git a/tests/integration/test_integration_snowflake.py b/tests/integration/test_integration_snowflake.py index 5984ac06..10fc85ad 100644 --- a/tests/integration/test_integration_snowflake.py +++ b/tests/integration/test_integration_snowflake.py @@ -474,8 +474,8 @@ def test_rank_transforms(sf_storage_with_models) -> None: dimensions=[ColumnRef(name="status")], measures=[ ModelMeasure(formula="quantity:sum"), - ModelMeasure(formula="rank(quantity:sum)", name="qty_rank"), - ModelMeasure(formula="dense_rank(quantity:sum)", name="qty_dense_rank"), + ModelMeasure(formula="rank(quantity:sum, direction='desc')", name="qty_rank"), + ModelMeasure(formula="dense_rank(quantity:sum, direction='desc')", name="qty_dense_rank"), ModelMeasure(formula="ntile(quantity:sum, n=2)", name="qty_bucket"), ], ))) diff --git a/tests/perf/compare/corpus.py b/tests/perf/compare/corpus.py index 6480b1c2..3ee67363 100644 --- a/tests/perf/compare/corpus.py +++ b/tests/perf/compare/corpus.py @@ -221,7 +221,7 @@ def _month_oracle(aggs, post=None, **extra): time_dimensions=MONTH_TD, order=ASC_TIME), ordered=True, subset_100k=True), _entry("bench_rank_by_category", "bench", - _q(measures=["total_cost:sum", {"formula": "rank(total_cost:sum)", "name": "rnk"}], + _q(measures=["total_cost:sum", {"formula": "rank(total_cost:sum, direction='desc')", "name": "rnk"}], dimensions=["category"], order=[{"column": "total_cost_sum", "direction": "desc"}]), ordered=True), @@ -363,7 +363,7 @@ def _month_oracle(aggs, post=None, **extra): _entry("join_transform_rank_over_join", "joins", # transform layer (rank) stacked on an aggregate grouped by a JOINED # dim. Unordered: poor/whale tie on the adversarial sums. - _q(measures=["total_cost:sum", {"formula": "rank(total_cost:sum)", "name": "rnk"}], + _q(measures=["total_cost:sum", {"formula": "rank(total_cost:sum, direction='desc')", "name": "rnk"}], dimensions=["customers.segment"], order=[{"column": "total_cost_sum", "direction": "desc"}]), subset_100k=True), @@ -432,7 +432,7 @@ def _month_oracle(aggs, post=None, **extra): "aggs": [COUNT_STAR], "having": "_count > 2"}}), _entry("filter_rank_transform", "filters", _q(measures=["total_cost:sum"], dimensions=["category"], - filters=["rank(total_cost:sum) <= 3"], + filters=["rank(total_cost:sum, direction='desc') <= 3"], order=[{"column": "total_cost_sum", "direction": "desc"}]), ordered=True), _entry("filter_date_range", "filters", diff --git a/tests/perf/test_bench.py b/tests/perf/test_bench.py index 1dfbbb7b..fc303e80 100644 --- a/tests/perf/test_bench.py +++ b/tests/perf/test_bench.py @@ -88,7 +88,7 @@ def _execute(env: BenchEnv, loop: asyncio.AbstractEventLoop, **query_kwargs) -> "rank_by_category": dict( measures=[ ModelMeasure(formula="total_cost:sum"), - ModelMeasure(formula="rank(total_cost:sum)", name="rnk"), + ModelMeasure(formula="rank(total_cost:sum, direction='desc')", name="rnk"), ], dimensions=[ColumnRef(name="category")], order=[OrderItem(column=ColumnRef(name="total_cost_sum"), direction="desc")], diff --git a/tests/test_dev1450fix_group2_correctness.py b/tests/test_dev1450fix_group2_correctness.py index 4ca7ad72..c6243ef9 100644 --- a/tests/test_dev1450fix_group2_correctness.py +++ b/tests/test_dev1450fix_group2_correctness.py @@ -110,7 +110,7 @@ def test_partition_by_multi_column_parses_and_binds(): ) bundle = ResolvedSourceBundle(dialect="postgres", source_model=orders, referenced_models=[]) scope = ModelScope(source_model=orders) - parsed = _parse("rank(amount:sum, partition_by=[region, channel])") + parsed = _parse("rank(amount:sum, partition_by=[region, channel], direction='desc')") bound = bind_expr(parsed=parsed, scope=scope, bundle=bundle) assert isinstance(bound.value_key, TransformKey) assert bound.value_key.op == "rank" diff --git a/tests/test_dev1712_order_only_hidden_slots.py b/tests/test_dev1712_order_only_hidden_slots.py index e37e503a..0ee77989 100644 --- a/tests/test_dev1712_order_only_hidden_slots.py +++ b/tests/test_dev1712_order_only_hidden_slots.py @@ -1,4 +1,4 @@ -"""Order-only hidden slots (ORDER BY refs not declared as dims/measures) + the ``rank(partition_by=X)`` grain guard.""" +"""Order-only hidden slots (ORDER BY refs not declared as dims/measures) + the ``rank(partition_by=X, direction='desc')`` grain guard.""" from __future__ import annotations import re @@ -455,7 +455,7 @@ async def test_declared_transform_plus_hidden_agg_order(self, engine) -> None: query = SlayerQuery( source_model="orders", dimensions=[ColumnRef(name="status")], - measures=[ModelMeasure(formula="rank(amount:sum)", name="rk")], + measures=[ModelMeasure(formula="rank(amount:sum, direction='desc')", name="rk")], order=[OrderItem(column="id:count", direction="desc")], ) sql = await _sql(engine, query) @@ -671,7 +671,7 @@ async def test_partition_by_bare_dim_accepted(self, engine) -> None: query = SlayerQuery( source_model="orders", dimensions=[ColumnRef(name="status")], - measures=[ModelMeasure(formula="rank(amount:sum, partition_by=status)", name="rk")], + measures=[ModelMeasure(formula="rank(amount:sum, partition_by=status, direction='desc')", name="rk")], ) sql = await _sql(engine, query) assert "PARTITION BY" in sql.upper() @@ -681,7 +681,7 @@ async def test_partition_by_qualified_dim_accepted(self, engine) -> None: query = SlayerQuery( source_model="orders", dimensions=[ColumnRef(name="status")], - measures=[ModelMeasure(formula="rank(amount:sum, partition_by=orders.status)", name="rk")], + measures=[ModelMeasure(formula="rank(amount:sum, partition_by=orders.status, direction='desc')", name="rk")], ) sql = await _sql(engine, query) assert "PARTITION BY" in sql.upper() @@ -691,7 +691,7 @@ async def test_partition_by_dotted_joined_dim_accepted(self, engine) -> None: source_model="orders", dimensions=[ColumnRef(name="customers.region")], measures=[ModelMeasure( - formula="rank(amount:sum, partition_by=customers.region)", name="rk")], + formula="rank(amount:sum, partition_by=customers.region, direction='desc')", name="rk")], ) sql = await _sql(engine, query) assert "PARTITION BY" in sql.upper() @@ -702,7 +702,7 @@ async def test_partition_by_time_dimension_uses_bucket(self, engine) -> None: source_model="orders", time_dimensions=[TimeDimension(dimension="created_at", granularity="month")], measures=[ModelMeasure( - formula="rank(amount:sum, partition_by=created_at)", name="rk")], + formula="rank(amount:sum, partition_by=created_at, direction='desc')", name="rk")], ) sql = await _sql(engine, query) # (a) No raw bare-column projection of created_at alongside the bucket (grain widening). @@ -746,7 +746,7 @@ async def test_partition_by_non_dim_raises_rank(self, engine) -> None: dimensions=[ColumnRef(name="status")], measures=[ ModelMeasure(formula="amount:sum"), - ModelMeasure(formula="rank(amount:sum, partition_by=customer_id)", name="rk"), + ModelMeasure(formula="rank(amount:sum, partition_by=customer_id, direction='desc')", name="rk"), ], ) with pytest.raises(ValueError) as ei: @@ -766,7 +766,7 @@ async def test_partition_by_ambiguous_time_dim_granularity_raises(self, engine) TimeDimension(dimension="created_at", granularity="day"), ], measures=[ModelMeasure( - formula="rank(amount:sum, partition_by=created_at)", name="rk")], + formula="rank(amount:sum, partition_by=created_at, direction='desc')", name="rk")], ) with pytest.raises(ValueError) as ei: await _sql(engine, query) @@ -781,7 +781,7 @@ async def test_partition_by_same_granularity_time_dim_not_flagged_ambiguous(self TimeDimension(dimension="created_at", granularity="month"), ], measures=[ModelMeasure( - formula="rank(amount:sum, partition_by=created_at)", name="rk")], + formula="rank(amount:sum, partition_by=created_at, direction='desc')", name="rk")], ) sql = await _sql(engine, query) assert _outer_select_columns(sql) == ["orders.created_at", "orders.rk"] diff --git a/tests/test_dev1733_order_only_transform_composite.py b/tests/test_dev1733_order_only_transform_composite.py index 50ebc7bc..639dedae 100644 --- a/tests/test_dev1733_order_only_transform_composite.py +++ b/tests/test_dev1733_order_only_transform_composite.py @@ -303,7 +303,7 @@ async def test_rank_order_only_hidden_and_ordered(self, engine) -> None: source_model="orders", dimensions=[ColumnRef(name="status")], measures=[ModelMeasure(formula="amount:sum")], - order=[OrderItem(column="rank(amount:sum)", direction="desc")], + order=[OrderItem(column="rank(amount:sum, direction='desc')", direction="desc")], ) sql = await _sql(engine, query) assert _outer_select_columns(sql) == ["orders.status", "orders.amount_sum"], sql @@ -343,7 +343,7 @@ async def test_order_only_transform_alongside_declared_transform(self, engine) - source_model="orders", time_dimensions=_MONTH, measures=[ModelMeasure(formula="cumsum(amount:sum)", name="cs")], - order=[OrderItem(column="rank(amount:sum)", direction="desc")], + order=[OrderItem(column="rank(amount:sum, direction='desc')", direction="desc")], ) sql = await _sql(engine, query) assert _outer_select_columns(sql) == ["orders.created_at", "orders.cs"], sql @@ -354,7 +354,7 @@ async def test_order_only_transform_with_limit_and_offset(self, engine) -> None: source_model="orders", dimensions=[ColumnRef(name="status")], measures=[ModelMeasure(formula="amount:sum")], - order=[OrderItem(column="rank(amount:sum)", direction="desc")], + order=[OrderItem(column="rank(amount:sum, direction='desc')", direction="desc")], limit=2, offset=1, ) sql = await _sql(engine, query) @@ -370,7 +370,7 @@ async def test_order_only_transform_with_post_filter(self, engine) -> None: time_dimensions=_MONTH, measures=[ModelMeasure(formula="cumsum(amount:sum)", name="cs")], filters=["cs > 5"], - order=[OrderItem(column="rank(amount:sum)", direction="desc")], + order=[OrderItem(column="rank(amount:sum, direction='desc')", direction="desc")], ) sql = await _sql(engine, query) assert _outer_select_columns(sql) == ["orders.created_at", "orders.cs"], sql @@ -562,11 +562,12 @@ async def test_two_hidden_cumsums_get_distinct_aliases(self, engine) -> None: @pytest.mark.parametrize("op", ["rank", "lag", "lead"]) async def test_two_hidden_transforms_same_op_distinct_aliases(self, engine, op: str) -> None: + kw = ", direction='desc'" if op == "rank" else "" query = SlayerQuery( source_model="orders", time_dimensions=_MONTH, measures=[ModelMeasure( - formula=f"{op}(amount:sum) + {op}(id:count)", name="both", + formula=f"{op}(amount:sum{kw}) + {op}(id:count{kw})", name="both", )], ) sql = await _sql(engine, query) @@ -578,8 +579,8 @@ async def test_two_order_items_same_transform_op(self, engine) -> None: dimensions=[ColumnRef(name="status")], measures=[ModelMeasure(formula="*:count")], order=[ - OrderItem(column="rank(amount:sum)", direction="desc"), - OrderItem(column="rank(fee:sum)", direction="asc"), + OrderItem(column="rank(amount:sum, direction='desc')", direction="desc"), + OrderItem(column="rank(fee:sum, direction='desc')", direction="asc"), ], ) sql = await _sql(engine, query) @@ -1062,7 +1063,7 @@ async def test_order_only_transform_quotes_per_dialect( source_model="orders", dimensions=[ColumnRef(name="status")], measures=[ModelMeasure(formula="amount:sum")], - order=[OrderItem(column="rank(amount:sum)", direction="desc")], + order=[OrderItem(column="rank(amount:sum, direction='desc')", direction="desc")], ) resp = await eng.execute(query, dry_run=True) sql = resp.sql or "" @@ -1120,7 +1121,7 @@ async def test_top_n_by_order_only_transform(self, exec_engine) -> None: source_model="orders", dimensions=[ColumnRef(name="status")], measures=[ModelMeasure(formula="amount:sum")], - order=[OrderItem(column="rank(amount:sum)", direction="desc")], + order=[OrderItem(column="rank(amount:sum, direction='desc')", direction="desc")], ) resp = await exec_engine.execute(query) assert [r["orders.status"] for r in resp.data] == ["open", "paid"], resp.data @@ -1202,7 +1203,7 @@ async def test_hidden_transform_order_slot_stripped_from_response( source_model="orders", dimensions=[ColumnRef(name="status")], measures=[ModelMeasure(formula="amount:sum")], - order=[OrderItem(column="rank(amount:sum)", direction="desc")], + order=[OrderItem(column="rank(amount:sum, direction='desc')", direction="desc")], ) resp = await exec_engine.execute(query) assert resp.columns == ["orders.status", "orders.amount_sum"], resp.columns @@ -1255,7 +1256,7 @@ async def test_downstream_stage_order_only_transform(self, exec_engine) -> None: source_model="s1", dimensions=[ColumnRef(name="status")], measures=[ModelMeasure(formula="amt:sum", name="total")], - order=[OrderItem(column="rank(amt:sum)", direction="desc")], + order=[OrderItem(column="rank(amt:sum, direction='desc')", direction="desc")], ) resp = await exec_engine.execute(query=[inner, outer]) assert resp.columns == ["s1.status", "s1.total"], resp.columns @@ -1367,8 +1368,8 @@ async def test_order_only_transform_in_raw_rows_mode_rejected( source_model="orders", dimensions=[ColumnRef(name="status")], distinct_dimension_values=False, - order=[OrderItem(column="rank(amount:sum)", direction="desc")], + order=[OrderItem(column="rank(amount:sum, direction='desc')", direction="desc")], ) with pytest.raises(DistinctDimensionValuesError) as ei: await _sql(engine, query) - assert "rank(amount:sum)" in str(ei.value), ei.value + assert "rank(amount:sum, direction='desc')" in str(ei.value), ei.value diff --git a/tests/test_dev1739_guards.py b/tests/test_dev1739_guards.py index 66d16607..20fb9370 100644 --- a/tests/test_dev1739_guards.py +++ b/tests/test_dev1739_guards.py @@ -142,7 +142,7 @@ async def test_partition_by_rejected_on_non_rank_transforms(self, formula: str) async def test_partition_by_still_accepted_on_rank(self) -> None: sql = await gen(_q( dimensions=["region", "city"], - measures=[ModelMeasure(formula="rank(amount:sum, partition_by=region)")], + measures=[ModelMeasure(formula="rank(amount:sum, partition_by=region, direction='desc')")], )) upper = sql.upper() assert "RANK()" in upper diff --git a/tests/test_dev1824_computed_dim_execution.py b/tests/test_dev1824_computed_dim_execution.py index 83fc6fab..43df6878 100644 --- a/tests/test_dev1824_computed_dim_execution.py +++ b/tests/test_dev1824_computed_dim_execution.py @@ -39,7 +39,7 @@ async def exec_engine(request): yield engine -RANK_DIM = "rank(amount:sum(partition_by=region))" +RANK_DIM = "rank(amount:sum(partition_by=region), direction='desc')" class TestBandedDimension: diff --git a/tests/test_dev1824_golden_sql.py b/tests/test_dev1824_golden_sql.py index d7ea2cc8..bfb998b4 100644 --- a/tests/test_dev1824_golden_sql.py +++ b/tests/test_dev1824_golden_sql.py @@ -21,7 +21,7 @@ WORKING_PREFIX = "lift/" -RANK_DIM = "rank(amount:sum(partition_by=region))" +RANK_DIM = "rank(amount:sum(partition_by=region), direction='desc')" WBAND = ( "CASE WHEN amount:sum(window='90d', partition_by=region) > 50 THEN 1 ELSE 0 END" ) diff --git a/tests/test_dev1824_partitioned_execution.py b/tests/test_dev1824_partitioned_execution.py index 19b61185..34d9733d 100644 --- a/tests/test_dev1824_partitioned_execution.py +++ b/tests/test_dev1824_partitioned_execution.py @@ -195,7 +195,7 @@ async def test_rank_rows_by_attached_region_total(self, exec_engine) -> None: resp = await exec_engine.execute(q( dimensions=["region", "city"], measures=[ModelMeasure( - formula="rank(amount:sum(partition_by=region))", name="r", + formula="rank(amount:sum(partition_by=region), direction='desc')", name="r", )], )) by = rows_by(resp, "orders.region", "orders.city") diff --git a/tests/test_dev1824_remaining_guards.py b/tests/test_dev1824_remaining_guards.py index 9da49f8d..a967e647 100644 --- a/tests/test_dev1824_remaining_guards.py +++ b/tests/test_dev1824_remaining_guards.py @@ -145,7 +145,7 @@ async def test_transform_over_mixed_grain_aggregates_lifted(self) -> None: # Two grains in one transform union and broadcast (DEV-1839) — the # former fail-closed guard is gone. Executed ground truth lives in # tests/test_dev1839_union_dim_execution.py. - band = "rank(amount:sum(partition_by=region) - amount:sum(partition_by=city))" + band = "rank(amount:sum(partition_by=region) - amount:sum(partition_by=city), direction='desc')" query = q( dimensions=["region", "city", {"expression": band, "name": "rk"}], measures=[ModelMeasure(formula="amount:sum", name="s")], diff --git a/tests/test_dev1832_fixtures_smoke.py b/tests/test_dev1832_fixtures_smoke.py index 1a56f72c..18697db4 100644 --- a/tests/test_dev1832_fixtures_smoke.py +++ b/tests/test_dev1832_fixtures_smoke.py @@ -372,9 +372,6 @@ def test_windowed_inner_ranks(self): if v != prev: rank, prev = i, v ranks[key] = rank - for key, v in cells.items(): - if v is None: - ranks[key] = len(nonnull) + 1 got: dict = defaultdict(float) for (_region, month), rk in ranks.items(): got[month] += rk diff --git a/tests/test_dev1832_golden_sql.py b/tests/test_dev1832_golden_sql.py index 53b4d9a4..d4a9ace2 100644 --- a/tests/test_dev1832_golden_sql.py +++ b/tests/test_dev1832_golden_sql.py @@ -64,7 +64,7 @@ def _cases() -> dict: "lifted/mixed_transform": { "source": "sales", "mode": None, "kw": {"dimensions": ["region"], "measures": [{"formula": - "sum(quantity * rank(avg(unit_price, partition_by=product)))", + "sum(quantity * rank(avg(unit_price, partition_by=product), direction='desc'))", "name": "m"}]}}, # DEV-1928 — re-aggregation constituents in a mixed source; windowed inner. "lifted/mixed_reagg": { @@ -80,7 +80,7 @@ def _cases() -> dict: "lifted/windowed_inner": { "source": "monthly", "mode": None, "kw": {"time_dimensions": _MONTH_TD, "measures": [{"formula": - "sum(rank(amount:sum(window='90d', partition_by=region)))", + "sum(rank(amount:sum(window='90d', partition_by=region), direction='desc'))", "name": "m"}]}}, # positive/* — already supported; byte-identical SQL through the change. "positive/same_model_arith": { diff --git a/tests/test_dev1832_transform_source.py b/tests/test_dev1832_transform_source.py index 84f82378..e8d99a48 100644 --- a/tests/test_dev1832_transform_source.py +++ b/tests/test_dev1832_transform_source.py @@ -66,11 +66,12 @@ sales_q, status_key, ) +from tests._rank_direction_fixtures import rank_windows # Source formulas under test. GRAINED_CUMSUM = "sum(cumsum(amount:sum(partition_by=[region, ordered_at])) - 1)" UNGRAINED_CUMSUM = "sum(cumsum(amount:sum))" -RANK_MIXED = "sum(quantity * rank(avg(unit_price, partition_by=product)))" +RANK_MIXED = "sum(quantity * rank(avg(unit_price, partition_by=product), direction='desc'))" JOINED_ROWLEAF_MIXED = "sum(customers.discount * avg(amount, partition_by=status))" # Collapsing / family constituents over X = the monthly per-region total. @@ -90,7 +91,7 @@ WAVG_MIN_PARAM = f"weighted_avg(amount, weight=min({_X}, partition_by=region))" MIXED_COMBINED = (f"sum(amount * min({_X}, partition_by=region)) " "+ amount:sum(partition_by=region)") -WINDOWED_INNER = "sum(rank(amount:sum(window='90d', partition_by=region)))" +WINDOWED_INNER = "sum(rank(amount:sum(window='90d', partition_by=region), direction='desc'))" # Cross-model grained inner: the host-time-axis boundary (fanning hop), the legal # to-one key, and the non-time contrast that still associates. XMODEL_BOUNDARY = ("sum(cumsum(customers.spend:sum(" @@ -192,7 +193,7 @@ class TestDualGranularityQueryGrain: def test_ungrained_inner_is_grained_at_both_buckets(self, tds): elab = elaborate_query( query=monthly_q( - measures=[ModelMeasure(formula="sum(rank(amount:sum))", name="m")], + measures=[ModelMeasure(formula="sum(rank(amount:sum, direction='desc'))", name="m")], time_dimensions=tds), bundle=_monthly_bundle()) assert elab.prebound is not None @@ -209,7 +210,7 @@ async def test_rank_over_monthly_totals_by_executed_values(self, exec_backend): # per-column grain would rank the lone yearly total (1 everywhere). _, engine = exec_backend resp = await engine.execute(monthly_q( - measures=[ModelMeasure(formula="sum(rank(amount:sum))", name="m")], + measures=[ModelMeasure(formula="sum(rank(amount:sum, direction='desc'))", name="m")], time_dimensions=[*month_td(), _year_td()])) got = {month_key(row["monthly.ordered_at.month"]): row["monthly.m"] for row in resp.data} @@ -256,7 +257,7 @@ async def test_projected_grain_key_under_transform_legal(self, exec_backend): _, engine = exec_backend resp = await engine.execute(sales_q( dimensions=["region"], - measures=[ModelMeasure(formula="sum(rank(region))", name="m")])) + measures=[ModelMeasure(formula="sum(rank(region, direction='desc'))", name="m")])) got = {k[0]: v["sales.m"] for k, v in rows_by(resp, "sales.region").items()} assert set(got) == {"North", "South", "East", "Gap", "Void"} assert all(v is not None for v in got.values()) @@ -633,14 +634,14 @@ async def test_one_flat_with_scopes_closed_no_leak(self): assert_scope_closed(sql, dialect="duckdb") assert "__regroup__" not in sql - async def test_rank_pins_nulls_last_on_postgres(self): - # Rank ranks NULLs last on EVERY dialect: Postgres's native DESC is NULLS - # FIRST, so the NULL window cell would else take rank 1 and shift the rest. + async def test_rank_isolates_null_cells_on_postgres(self): + # Postgres's native DESC is NULLS FIRST; the NULL window cell must rank NULL, never 1. sql = await gen(monthly_q( measures=[ModelMeasure(formula=WINDOWED_INNER, name="m")], time_dimensions=month_td()), dialect="postgres") - assert "RANK() OVER (ORDER BY" in sql, sql - assert "DESC NULLS LAST" in sql, sql + [window] = rank_windows(sql, dialect="postgres") + assert window.fn == "RANK" and window.descending, sql + assert window.null_flag and window.null_guarded, sql class TestCrossModelGrainedInnerBoundary: diff --git a/tests/test_dev1835_grain_prune.py b/tests/test_dev1835_grain_prune.py index c637df79..c2eb2806 100644 --- a/tests/test_dev1835_grain_prune.py +++ b/tests/test_dev1835_grain_prune.py @@ -5,7 +5,7 @@ is constant within the raw dimensions already in the grain. It reads a key's free scalar columns via ``_scalar_free_columns``; if that helper fails to descend a ``TransformKey``'s input, a discriminating column inside e.g. -``rank(amount:sum(partition_by=region) + city)`` goes unseen and the axis is +``rank(amount:sum(partition_by=region) + city, direction='desc')`` goes unseen and the axis is wrongly pruned, collapsing the producer to ``region`` alone. """ diff --git a/tests/test_dev1835_semantic_pins.py b/tests/test_dev1835_semantic_pins.py index 3dc56508..48796f7b 100644 --- a/tests/test_dev1835_semantic_pins.py +++ b/tests/test_dev1835_semantic_pins.py @@ -170,7 +170,7 @@ async def test_rank_over_ranked(self, exec_backend) -> None: _, engine = exec_backend resp = await engine.execute(q( dimensions=["region"], - measures=[ModelMeasure(formula="rank(amount:last)", name="x")], + measures=[ModelMeasure(formula="rank(amount:last, direction='desc')", name="x")], )) got = {r["orders.region"]: int(r["orders.x"]) for r in resp.data} # REGION_LAST desc: NULL 60 → 1, North 30 → 2, South 25 → 3. diff --git a/tests/test_dev1836_matrix_flip.py b/tests/test_dev1836_matrix_flip.py index df5eae94..9d44bf69 100644 --- a/tests/test_dev1836_matrix_flip.py +++ b/tests/test_dev1836_matrix_flip.py @@ -117,7 +117,7 @@ async def test_rank_dim_with_cm_executes(self, exec_backend): """Transform-root dimension family × cross-model measure.""" _, engine = exec_backend resp = await engine.execute(q( - dimensions=[{"expression": "rank(amount:sum(partition_by=channel))", + dimensions=[{"expression": "rank(amount:sum(partition_by=channel), direction='desc')", "name": "rr"}], measures=[M, CM], )) @@ -135,7 +135,7 @@ async def test_mixed_dim_with_cm_executes(self, exec_backend): resp = await engine.execute(q( dimensions=[{ "expression": ("rank(amount:sum(partition_by=channel) - " - "amount:sum(partition_by=status))"), + "amount:sum(partition_by=status), direction='desc')"), "name": "mr", }], measures=[M, CM], diff --git a/tests/test_dev1837_dimension_measure_matrix.py b/tests/test_dev1837_dimension_measure_matrix.py index 541ac7a6..6e8378d4 100644 --- a/tests/test_dev1837_dimension_measure_matrix.py +++ b/tests/test_dev1837_dimension_measure_matrix.py @@ -388,7 +388,7 @@ async def test_streak_resets_and_recovers_within_banded_group( class TestExplicitTransformPartition: async def test_explicit_partition_by_on_transform_wins(self, exec_backend) -> None: - """``rank(amount:sum, partition_by=region)`` ranks within each region + """``rank(amount:sum, partition_by=region, direction='desc')`` ranks within each region regardless of the banded dimension (explicit keys take precedence over the auto-grain; only the rank family accepts a transform partition_by).""" _, engine = exec_backend @@ -397,7 +397,7 @@ async def test_explicit_partition_by_on_transform_wins(self, exec_backend) -> No measures=[ ModelMeasure(formula="amount:sum", name="m"), ModelMeasure( - formula="rank(amount:sum, partition_by=region)", name="x", + formula="rank(amount:sum, partition_by=region, direction='desc')", name="x", ), ], )) diff --git a/tests/test_dev1839_golden_sql.py b/tests/test_dev1839_golden_sql.py index 7b72ad20..ee834247 100644 --- a/tests/test_dev1839_golden_sql.py +++ b/tests/test_dev1839_golden_sql.py @@ -87,7 +87,7 @@ def _cases() -> dict: dimensions=[ "region", "city", {"expression": "rank(amount:sum(window='90d', partition_by=region)" - " - amount:sum(partition_by=city))", "name": "x"}, + " - amount:sum(partition_by=city), direction='desc')", "name": "x"}, ], time_dimensions=month_td(), measures=[s], @@ -96,7 +96,7 @@ def _cases() -> dict: dimensions=[ "region", "city", {"expression": "rank(amount:last(partition_by=region) - " - "amount:sum(partition_by=city))", "name": "x"}, + "amount:sum(partition_by=city), direction='desc')", "name": "x"}, ], measures=[s], ), @@ -114,7 +114,7 @@ def _cases() -> dict: "region", "city", {"expression": "rank(amount:sum(partition_by=region) - " "amount:sum(partition_by=city), " - "partition_by=channel)", "name": "x"}, + "partition_by=channel, direction='desc')", "name": "x"}, ], measures=[s], ), diff --git a/tests/test_dev1839_guards.py b/tests/test_dev1839_guards.py index 8819b622..fb50b1c9 100644 --- a/tests/test_dev1839_guards.py +++ b/tests/test_dev1839_guards.py @@ -40,7 +40,7 @@ class TestMixedWindowedRankedDeferred: async def test_mixed_windowed_grain_deferred(self) -> None: band = ( "rank(amount:sum(window='90d', partition_by=region) - " - "amount:sum(partition_by=city))" + "amount:sum(partition_by=city), direction='desc')" ) query = q( dimensions=["region", "city", {"expression": band, "name": "x"}], @@ -54,7 +54,7 @@ async def test_mixed_windowed_grain_deferred(self) -> None: async def test_mixed_first_last_grain_deferred(self) -> None: band = ( "rank(amount:last(partition_by=region) - " - "amount:sum(partition_by=city))" + "amount:sum(partition_by=city), direction='desc')" ) query = q( dimensions=["region", "city", {"expression": band, "name": "x"}], @@ -71,7 +71,7 @@ class TestTransformKwargAgainstUnion: async def test_kwarg_outside_union_fails_cleanly(self) -> None: band = ( "rank(amount:sum(partition_by=region) - " - "amount:sum(partition_by=city), partition_by=channel)" + "amount:sum(partition_by=city), partition_by=channel, direction='desc')" ) query = q( dimensions=["region", "city", {"expression": band, "name": "x"}], @@ -87,7 +87,7 @@ class TestUnchangedGuards: async def test_cross_model_inner_source_still_rejected(self) -> None: band = ( "rank(customers.spend:sum(partition_by=region) - " - "amount:sum(partition_by=city))" + "amount:sum(partition_by=city), direction='desc')" ) query = q( dimensions=["region", "city", {"expression": band, "name": "x"}], @@ -99,7 +99,7 @@ async def test_cross_model_inner_source_still_rejected(self) -> None: assert "__regroup__" not in str(ei.value) async def test_bare_inner_aggregate_still_rejected(self) -> None: - band = "rank(amount:sum - amount:sum(partition_by=city))" + band = "rank(amount:sum - amount:sum(partition_by=city), direction='desc')" query = q( dimensions=["region", "city", {"expression": band, "name": "x"}], measures=[ModelMeasure(formula="amount:sum", name="s")], diff --git a/tests/test_dev1842_binder_measure_resolution.py b/tests/test_dev1842_binder_measure_resolution.py index 5aadb981..d4263dc3 100644 --- a/tests/test_dev1842_binder_measure_resolution.py +++ b/tests/test_dev1842_binder_measure_resolution.py @@ -175,7 +175,7 @@ class TestTransformArgEligibilityDropped: async def test_transform_partition_by_measure_not_expanded(self) -> None: model = _model([_REV, _GRP]) with pytest.raises(ValueError): - await _gen(model, "rank(amount:sum, partition_by=grp)", + await _gen(model, "rank(amount:sum, partition_by=grp, direction='desc')", dimensions=["status"], time_dimensions=[{"dimension": "created_at", "granularity": "month"}]) diff --git a/tests/test_dev1847_consumers.py b/tests/test_dev1847_consumers.py index 88bb130b..7e65445c 100644 --- a/tests/test_dev1847_consumers.py +++ b/tests/test_dev1847_consumers.py @@ -45,7 +45,7 @@ async def test_rank_over_reaggregated_value(self, exec_engine): resp = await exec_engine.execute(sales_q( dimensions=["region"], measures=[reagg("avg", INNER_CR, name="acr"), - ModelMeasure(formula=f"rank(avg({INNER_CR}))", name="rnk")])) + ModelMeasure(formula=f"rank(avg({INNER_CR}), direction='desc')", name="rnk")])) by = {k[0]: v for k, v in region_key(resp).items()} # South has the largest per-region average -> rank 1 (descending). assert int(by["South"]["sales.rnk"]) == 1 diff --git a/tests/test_dev1850_keyless_grain.py b/tests/test_dev1850_keyless_grain.py index c2ec8b20..e60a98f9 100644 --- a/tests/test_dev1850_keyless_grain.py +++ b/tests/test_dev1850_keyless_grain.py @@ -107,12 +107,12 @@ def _dim(expression: str, name: str) -> dict: ), pytest.param( [_dim(LOCAL_BAND, "cband")], - [ModelMeasure(formula=f"rank({LOCAL_AGG})", name="rt")], + [ModelMeasure(formula=f"rank({LOCAL_AGG}, direction='desc')", name="rt")], None, None, "city", id="transform_input-local", ), pytest.param( [_dim(SPEND_BAND, "sband")], - [ModelMeasure(formula=f"rank({CM_AGG})", name="rt")], + [ModelMeasure(formula=f"rank({CM_AGG}, direction='desc')", name="rt")], None, None, "customers.tier", id="transform_input-cross_model", ), # Already-clean today (no computed dimension): the aggregate is consumed only diff --git a/tests/test_dev1859_golden_sql.py b/tests/test_dev1859_golden_sql.py index 2f4d32b4..37db01c2 100644 --- a/tests/test_dev1859_golden_sql.py +++ b/tests/test_dev1859_golden_sql.py @@ -30,8 +30,8 @@ "weight=sum(amount, partition_by=customers.regions.name))") _MIXED_TWIN = "sum(customers.spend * sum(amount, partition_by=customers.regions.name))" _RANKED_TRANSFORM = ("customers.spend:weighted_avg(" - "weight=rank(sum(amount, partition_by=customers.regions.name)))") -_LOCAL_RANKED_TRANSFORM = f"weighted_avg(amount, weight=rank({_REGION_SUM}))" + "weight=rank(sum(amount, partition_by=customers.regions.name), direction='desc'))") +_LOCAL_RANKED_TRANSFORM = f"weighted_avg(amount, weight=rank({_REGION_SUM}, direction='desc'))" def _cases() -> dict: diff --git a/tests/test_dev1859_transform_row_leaf.py b/tests/test_dev1859_transform_row_leaf.py index b14e18a9..03b6ea2b 100644 --- a/tests/test_dev1859_transform_row_leaf.py +++ b/tests/test_dev1859_transform_row_leaf.py @@ -44,6 +44,8 @@ def _call(op: str, inner: str) -> str: + if op in ("rank", "dense_rank"): + return f"{op}({inner}, direction='desc')" return f"ntile({inner}, n=4)" if op == "ntile" else f"{op}({inner})" @@ -113,7 +115,7 @@ async def test_bare_leaf_measure_via_engine(self, exec_engine): async def test_rank_family_covered(self, exec_engine): """Scenario: Rank family is covered.""" q = _q(dimensions=["store"], time_dimensions=month_td(), - measures=[ModelMeasure(formula="rank(qty)", name="t")]) + measures=[ModelMeasure(formula="rank(qty, direction='desc')", name="t")]) with pytest.raises(ValueError) as ei: await exec_engine.execute(q) _assert_leg_b_message(str(ei.value), "rank") @@ -130,7 +132,7 @@ async def test_predicate_over_unprojected_column(self, exec_engine): async def test_raw_time_source_column_refines_the_bucket(self, exec_engine): """The bucketed TD's raw source column is not a projected grain key.""" q = _q(time_dimensions=month_td(), - measures=[ModelMeasure(formula="rank(ordered_at)", name="t")]) + measures=[ModelMeasure(formula="rank(ordered_at, direction='desc')", name="t")]) with pytest.raises(ValueError) as ei: await exec_engine.execute(q) _assert_leg_b_message(str(ei.value), "rank") @@ -145,7 +147,7 @@ async def test_filter_position(self, exec_engine): async def test_order_position(self, exec_engine): q = _q(time_dimensions=month_td(), - order=[{"column": "rank(qty)", "direction": "desc"}], + order=[{"column": "rank(qty, direction='desc')", "direction": "desc"}], measures=[ModelMeasure(formula="revenue:sum", name="r")]) with pytest.raises(ValueError) as ei: await exec_engine.execute(q) @@ -154,11 +156,11 @@ async def test_order_position(self, exec_engine): class TestProjectedGrainKeyStaysLegal: async def test_rank_over_projected_dimension(self, exec_engine): - """Scenario: A projected grain key stays legal — rank(weight) with + """Scenario: A projected grain key stays legal — rank(weight, direction='desc') with weight a query dimension, verified sound today.""" resp = await exec_engine.execute(_q( dimensions=["weight"], - measures=[ModelMeasure(formula="rank(weight)", name="t")])) + measures=[ModelMeasure(formula="rank(weight, direction='desc')", name="t")])) by = rows_by(resp, "sales.weight") assert len(resp.data) == 2 assert int(by[(2.0,)]["sales.t"]) == 1 @@ -185,7 +187,7 @@ async def test_rank_over_projected_computed_dimension(self, exec_engine): wband = "CASE WHEN weight > 1 THEN 2 ELSE 1 END" resp = await exec_engine.execute(_q( dimensions=[{"expression": wband, "name": "wband"}], - measures=[ModelMeasure(formula=f"rank({wband})", name="t")])) + measures=[ModelMeasure(formula=f"rank({wband}, direction='desc')", name="t")])) by = rows_by(resp, "sales.wband") assert len(resp.data) == 2 assert int(by[(2,)]["sales.t"]) == 1 @@ -206,7 +208,7 @@ async def test_pure_aggregate_input_is_legal(self, exec_engine): async def test_all_projected_composite_is_legal(self, exec_engine): resp = await exec_engine.execute(_q( dimensions=["weight", "qty"], - measures=[ModelMeasure(formula="rank(weight * qty)", name="t")])) + measures=[ModelMeasure(formula="rank(weight * qty, direction='desc')", name="t")])) assert resp.data diff --git a/tests/test_dev1894_query_type_errors.py b/tests/test_dev1894_query_type_errors.py index 9cabe5a9..2833bcf6 100644 --- a/tests/test_dev1894_query_type_errors.py +++ b/tests/test_dev1894_query_type_errors.py @@ -100,7 +100,7 @@ def _bad_window() -> Tuple[SlayerQuery, List[SlayerModel]]: def _rank_partition() -> Tuple[SlayerQuery, List[SlayerModel]]: - return orders_q(dimensions=["region"], measures=[_m("rank(amount:sum, partition_by=city)", "r")]), f1739.dev1739_models() + return orders_q(dimensions=["region"], measures=[_m("rank(amount:sum, partition_by=city, direction='desc')", "r")]), f1739.dev1739_models() #: (id, case, class, location, has suggestion) diff --git a/tests/test_dev1903_opacity.py b/tests/test_dev1903_opacity.py index 60143efb..cee434f0 100644 --- a/tests/test_dev1903_opacity.py +++ b/tests/test_dev1903_opacity.py @@ -30,7 +30,7 @@ _REGION_SPEND = "sum(customers.spend, partition_by=customers.regions.name)" SOURCE = f"sum(amount * {_REGION_SPEND})" KWARG = f"amount:weighted_avg(weight={_REGION_SPEND})" -TRANSFORM = f"amount:weighted_avg(weight=rank({_REGION_SPEND}))" +TRANSFORM = f"amount:weighted_avg(weight=rank({_REGION_SPEND}, direction='desc'))" #: host column masked to North customers: its own closure crosses to regions. NORTH_OUTER = f"sum(north_amount * {_REGION_SPEND})" NORTH_INNER = "amount:weighted_avg(weight=sum(north_amount, partition_by=channel))" diff --git a/tests/test_dev1903_producer_flag.py b/tests/test_dev1903_producer_flag.py index 183b5524..e5720ea2 100644 --- a/tests/test_dev1903_producer_flag.py +++ b/tests/test_dev1903_producer_flag.py @@ -390,7 +390,7 @@ async def test_executes_at_the_band_grain(self, exec_engine, dimensions, expecte """Grained at ``spend_band`` alone (the nested aggregate is opaque): a constant weight per band.""" query = sales_q(dimensions=dimensions, measures=[ModelMeasure( - formula="weighted_avg(amount, weight=rank(sum(amount, partition_by=spend_band)))", + formula="weighted_avg(amount, weight=rank(sum(amount, partition_by=spend_band), direction='desc'))", name="w")]) resp = await exec_engine.execute(query) names = [d if isinstance(d, str) else d["name"] for d in dimensions] diff --git a/tests/test_dev1903_transform_inputs.py b/tests/test_dev1903_transform_inputs.py index 23eb2734..32093b15 100644 --- a/tests/test_dev1903_transform_inputs.py +++ b/tests/test_dev1903_transform_inputs.py @@ -84,7 +84,7 @@ def test_boolean_message(self, op): @pytest.mark.parametrize("formula", [ "time_shift(revenue:sum > 100, -1)", "cumsum(revenue:sum > 100)", - "rank(revenue:sum > 100)", + "rank(revenue:sum > 100, direction='desc')", ]) def test_boolean_input_accepted_elsewhere(self, formula): _check(formula) @@ -98,7 +98,7 @@ def test_planner_raises_the_same_message(self): class TestInnermostTransformNamed: @pytest.mark.parametrize("formula", [ - "first(cumsum(weight))", "last(cumsum(weight))", "rank(cumsum(weight))", + "first(cumsum(weight))", "last(cumsum(weight))", "rank(cumsum(weight), direction='desc')", "time_shift(cumsum(weight), -1)", "change(cumsum(weight))", ]) def test_consuming_transform_is_named(self, formula): diff --git a/tests/test_dev1911_fanning_partition_key.py b/tests/test_dev1911_fanning_partition_key.py index bb70b75a..174dcb50 100644 --- a/tests/test_dev1911_fanning_partition_key.py +++ b/tests/test_dev1911_fanning_partition_key.py @@ -163,7 +163,7 @@ class TestEveryPosition: async def test_transform_partition_set(self, engine, mode): await _assert_fails_naming_hop(engine, _regions_q( dimensions=["bad_pop"], - measures=[ModelMeasure(formula="rank(pop:sum, partition_by=bad_pop)", name="w")], + measures=[ModelMeasure(formula="rank(pop:sum, partition_by=bad_pop, direction='desc')", name="w")], to_many_handling=mode)) @pytest.mark.parametrize("mode", MODES) diff --git a/tests/test_dev1946_transform_parameter.py b/tests/test_dev1946_transform_parameter.py index 7cd9b67f..e1188eae 100644 --- a/tests/test_dev1946_transform_parameter.py +++ b/tests/test_dev1946_transform_parameter.py @@ -191,7 +191,7 @@ async def test_by_region_and_city(self, sales_engine): async def test_global(self, sales_engine): resp = await sales_engine.execute(sales_q(measures=[_m(LOCAL_SALES)])) - assert float(global_val(resp)) == pytest.approx(local_sales_global()) # 810/43 + assert float(global_val(resp)) == pytest.approx(local_sales_global()) # 810/33 class TestCollapsingLast: @@ -515,7 +515,7 @@ def test_transform_kwarg_binds_to_transform_key(self): def test_positional_equals_keyword_identity(self): scope, bundle = _sales_scope_bundle() - positional = "weighted_avg(amount, rank(sum(amount, partition_by=region)))" + positional = "weighted_avg(amount, rank(sum(amount, partition_by=region), direction='desc'))" kw = bind_expr(parse_expr(LOCAL_SALES), scope=scope, bundle=bundle) pos = bind_expr(parse_expr(positional), scope=scope, bundle=bundle) assert pos.value_key == kw.value_key diff --git a/tests/test_dev1953_partition_alias.py b/tests/test_dev1953_partition_alias.py index dcdf1d87..1abb1839 100644 --- a/tests/test_dev1953_partition_alias.py +++ b/tests/test_dev1953_partition_alias.py @@ -37,12 +37,12 @@ rank_within, ) -RANK_UREG = "rank(sum(amount), partition_by=ureg)" -RANK_BAND = "rank(sum(amount), partition_by=spend_band)" -PARAM_BAND = "rank(sum(amount, partition_by=[spend_band, city]), partition_by=spend_band)" +RANK_UREG = "rank(sum(amount), partition_by=ureg, direction='desc')" +RANK_BAND = "rank(sum(amount), partition_by=spend_band, direction='desc')" +PARAM_BAND = "rank(sum(amount, partition_by=[spend_band, city]), partition_by=spend_band, direction='desc')" PARAM_UREG = ("weighted_avg(amount, weight=rank(sum(amount, partition_by=[ureg, city]), " - "partition_by=ureg))") -DIM_R = {"expression": "rank(sum(amount, partition_by=[city, ureg]), partition_by=ureg)", + "partition_by=ureg, direction='desc'))") +DIM_R = {"expression": "rank(sum(amount, partition_by=[city, ureg]), partition_by=ureg, direction='desc')", "name": "r"} # Spec constants. @@ -51,15 +51,14 @@ ("NORTH", "Beta"): 1, ("NORTH", "Alpha"): 2, ("SOUTH", "Gamma"): 1, ("SOUTH", "Alpha"): 2, ("GAP", None): 1, ("GAP", "Kappa"): 2, - ("VOID", "Xi"): 1, + ("VOID", "Xi"): None, } PARAM_BY_UREG = {"EAST": 56.0, "NORTH": 120 / 7, "SOUTH": 36.0, "GAP": 7.0, "VOID": None} -FILTER_RANK1 = {("EAST", "Zeta"), ("NORTH", "Beta"), ("SOUTH", "Gamma"), ("GAP", None), - ("VOID", "Xi")} +FILTER_RANK1 = {("EAST", "Zeta"), ("NORTH", "Beta"), ("SOUTH", "Gamma"), ("GAP", None)} DIM_CELLS = { ("EAST", 1): 80.0, ("EAST", 2): 100.0, ("NORTH", 1): 60.0, ("NORTH", 2): 30.0, ("SOUTH", 1): 100.0, ("SOUTH", 2): 40.0, ("GAP", 1): 12.0, ("GAP", 2): 8.0, - ("VOID", 1): None, + ("VOID", None): None, } BAND_RANK1 = {("hi", "Gamma"): 100.0, ("lo", "Alpha"): 70.0} @@ -67,7 +66,7 @@ # --------------------------------------------------------------------------- # # Raw-row oracles. # --------------------------------------------------------------------------- # -def _ureg_city_ranks() -> Dict[Tuple, int]: +def _ureg_city_ranks() -> Dict[Tuple, Optional[int]]: return rank_within(cell_totals(lambda r: (r[1].upper(), r[2]))) @@ -79,8 +78,8 @@ def _weighted_avg(group: Callable[[tuple], str], per[group(row)].append((row[4], weight(row))) out = {} for g, pairs in per.items(): - num = [v * w for v, w in pairs if v is not None] - out[g] = sum(num) / sum(w for _v, w in pairs) if num else None + num = [v * w for v, w in pairs if v is not None and w is not None] + out[g] = sum(num) / sum(w for _v, w in pairs if w is not None) if num else None return out @@ -89,7 +88,7 @@ def _param_by_ureg() -> Dict[str, Optional[float]]: return _weighted_avg(lambda r: r[1].upper(), lambda r: rank[(r[1].upper(), r[2])]) -def _band_city_ranks() -> Dict[Tuple, int]: +def _band_city_ranks() -> Dict[Tuple, Optional[int]]: band = band_of() return rank_within(cell_totals(lambda r: (band[(r[2], r[1])], r[2]))) @@ -162,7 +161,7 @@ def test_alias_resolves_identically(self): def test_mixed_list(self): amap = _alias(name="ureg", expr="upper(region)") agg = _bind(formula="sum(amount, partition_by=[ureg, product])", alias_map=amap) - rank = _bind(formula="rank(sum(amount), partition_by=[ureg, product])", alias_map=amap) + rank = _bind(formula="rank(sum(amount), partition_by=[ureg, product], direction='desc')", alias_map=amap) want = Grain.of([amap["ureg"], ColumnKey(path=(), leaf="product")]) assert isinstance(rank, TransformKey) assert isinstance(agg, AggregateKey) @@ -175,7 +174,7 @@ def test_attach_carrying_alias_binds_to_dimension_value(self): assert rank.partition_keys == Grain.of([amap["spend_band"]]) @pytest.mark.parametrize(argnames="formula,message", argvalues=[ - ("rank(sum(amount), partition_by=sum(amount))", + ("rank(sum(amount), partition_by=sum(amount), direction='desc')", "'sum(amount)' is an expression.\n at transform 'rank' partition_by"), ("sum(amount, partition_by=sum(amount))", "'sum(amount)' is an expression.\n at aggregation partition_by"), @@ -187,7 +186,7 @@ def test_non_column_element_names_construct(self, formula, message): @pytest.mark.parametrize(argnames="formula", argvalues=[ "sum(amount, partition_by=upper(region))", - "rank(sum(amount), partition_by=[city, upper(region)])", + "rank(sum(amount), partition_by=[city, upper(region)], direction='desc')", ]) def test_expression_element_is_a_typed_partition_key_error(self, formula): with pytest.raises(PartitionKeyError, match=re.escape("'upper(region)' is an expression")): @@ -221,9 +220,11 @@ async def test_order(self, engine): dimensions=[UREG, "city"], measures=[AMOUNT], order=[{"column": RANK_UREG, "direction": "asc"}])) keys = [(r["sales.ureg"], r["sales.city"]) for r in resp.data] - n = len(FILTER_RANK1) - assert set(keys[:n]) == FILTER_RANK1 - assert all(MEASURE_RANKS[k] == 2 for k in keys[n:]) + pos = {MEASURE_RANKS[k]: [] for k in keys} + for i, k in enumerate(keys): + pos[MEASURE_RANKS[k]].append(i) + assert {k for k in keys if MEASURE_RANKS[k] == 1} == FILTER_RANK1 + assert max(pos[1]) < min(pos[2]) async def test_dimension_position_member_key(self, engine): resp = await engine.execute(sales_q(dimensions=[UREG, DIM_R], measures=[AMOUNT])) @@ -260,7 +261,7 @@ async def test_measure(self, engine): got = {k: v["sales.r"] for k, v in rows_by(resp, "sales.spend_band", "sales.city").items()} assert got == _band_city_ranks() - assert {k for k, r in got.items() if r <= 1} == set(BAND_RANK1) + assert {k for k, r in got.items() if r is not None and r <= 1} == set(BAND_RANK1) async def test_parameter(self, engine): resp = await engine.execute(sales_q( diff --git a/tests/test_dev1953_partition_membership.py b/tests/test_dev1953_partition_membership.py index c8545c70..c5531cfc 100644 --- a/tests/test_dev1953_partition_membership.py +++ b/tests/test_dev1953_partition_membership.py @@ -6,7 +6,7 @@ from __future__ import annotations -from typing import Dict, Tuple +from typing import Dict, Optional, Tuple import pytest @@ -34,9 +34,9 @@ from tests._dev1919_fixtures import ordered_month_td CRP = ["city", "region", "product"] -NONMEMBER = "rank(sum(amount, partition_by=[city, region]), partition_by=product)" -MEMBER = "rank(sum(amount, partition_by=[city, region]), partition_by=region)" -UNGRAINED = "rank(sum(amount), partition_by=region)" +NONMEMBER = "rank(sum(amount, partition_by=[city, region]), partition_by=product, direction='desc')" +MEMBER = "rank(sum(amount, partition_by=[city, region]), partition_by=region, direction='desc')" +UNGRAINED = "rank(sum(amount), partition_by=region, direction='desc')" LAST_INNER = "last(sum(amount, partition_by=[customers.regions.name, ordered_at]))" LAST_SHIFT = "last(time_shift(customers.regions.name, -1))" @@ -45,33 +45,33 @@ ("North", "Beta"): 1, ("North", "Alpha"): 2, ("South", "Gamma"): 1, ("South", "Alpha"): 2, ("Gap", None): 1, ("Gap", "Kappa"): 2, - ("Void", "Xi"): 1, + ("Void", "Xi"): None, } CITY_NAME_RANKS = { ("East", "Zeta"): 1, ("East", "Epsilon"): 2, ("East", "Delta"): 3, ("North", "Beta"): 1, ("North", "Alpha"): 2, ("South", "Gamma"): 1, ("South", "Alpha"): 2, - ("Gap", "Kappa"): 1, ("Gap", None): 2, + ("Gap", "Kappa"): 1, ("Gap", None): None, ("Void", "Xi"): 1, } REGION_BAND_RANKS = { ("North", "hi"): 1, ("North", "lo"): 2, ("South", "hi"): 1, ("South", "lo"): 2, - ("East", "hi"): 1, ("Gap", "lo"): 1, ("Void", "lo"): 1, + ("East", "hi"): 1, ("Gap", "lo"): 1, ("Void", "lo"): None, } -def _city_name_ranks() -> Dict[Tuple, int]: - """rank(city, partition_by=region): city values descending, NULL last.""" +def _city_name_ranks() -> Dict[Tuple, Optional[int]]: + """rank(city, partition_by=region, direction='desc'): city values descending, NULL ranks NULL.""" cells = {(r[1], r[2]) for r in _SALES_ROWS_WIDE} out = {} for region, city in cells: peers = {c for rg, c in cells if rg == region and c is not None} - out[(region, city)] = (1 + len(peers) if city is None + out[(region, city)] = (None if city is None else 1 + sum(1 for c in peers if c > city)) return out -def _region_band_ranks() -> Dict[Tuple, int]: +def _region_band_ranks() -> Dict[Tuple, Optional[int]]: band = band_of() return rank_within(cell_totals(lambda r: (r[1], band[(r[2], r[1])]))) @@ -122,7 +122,7 @@ async def test_filter(self, engine): async def test_dimension_position_plain_column(self, engine): dim = {"expression": "rank(sum(amount, partition_by=[city, product]), " - "partition_by=region)", "name": "r"} + "partition_by=region, direction='desc')", "name": "r"} query = sales_q(dimensions=["region", dim], measures=[AMOUNT]) with pytest.raises(ValueError) as ei: await engine.execute(query) @@ -130,7 +130,7 @@ async def test_dimension_position_plain_column(self, engine): async def test_dimension_position_computed_dimension(self, engine): dim = {"expression": "rank(sum(amount, partition_by=[city, region]), " - "partition_by=ureg)", "name": "r"} + "partition_by=ureg, direction='desc')", "name": "r"} query = sales_q(dimensions=[UREG, dim], measures=[AMOUNT]) with pytest.raises(ValueError) as ei: await engine.execute(query) @@ -154,7 +154,7 @@ async def test_order(self, engine): _assert_membership_error(msg=str(ei.value), key="'product'", grain="city, region") @pytest.mark.parametrize(argnames="op,extra", argvalues=[ - ("dense_rank", ""), ("percent_rank", ""), ("ntile", ", n=2"), + ("dense_rank", ", direction='desc'"), ("percent_rank", ""), ("ntile", ", n=2"), ]) async def test_every_rank_family_op(self, engine, op, extra): formula = f"{op}(sum(amount, partition_by=[city, region]){extra}, partition_by=product)" @@ -165,7 +165,7 @@ async def test_every_rank_family_op(self, engine, op, extra): async def test_composite_union_without_key(self, engine): formula = ("rank(sum(amount, partition_by=[city, region]) " - "+ sum(amount, partition_by=city), partition_by=product)") + "+ sum(amount, partition_by=city), partition_by=product, direction='desc')") query = sales_q(dimensions=CRP, measures=[_measure(formula)]) with pytest.raises(ValueError) as ei: await engine.execute(query) @@ -180,7 +180,7 @@ async def test_member_key_executes(self, engine): async def test_composite_union_member(self, engine): formula = ("rank(sum(amount, partition_by=[city, region]) " - "+ sum(amount, partition_by=[city, product]), partition_by=product)") + "+ sum(amount, partition_by=[city, product]), partition_by=product, direction='desc')") resp = await engine.execute(sales_q(dimensions=CRP, measures=[_measure(formula)])) assert resp.data @@ -200,14 +200,14 @@ async def test_ungrained_inner_over_region_and_band(self, engine): class TestAggregateFreeInput: async def test_leaf_input_takes_query_grain(self, engine): resp = await engine.execute(sales_q( - dimensions=CRP, measures=[_measure("rank(city, partition_by=region)")])) + dimensions=CRP, measures=[_measure("rank(city, partition_by=region, direction='desc')")])) for (city, region, _), row in rows_by(resp, *(f"sales.{d}" for d in CRP)).items(): assert row["sales.r"] == CITY_NAME_RANKS[(region, city)], (city, region) # Bind level only: execution is DEV-1962. def test_literal_input_takes_query_grain(self): query = sales_q(dimensions=["region"], - measures=[_measure("rank(1, partition_by=region)")]) + measures=[_measure("rank(1, partition_by=region, direction='desc')")]) bind_query_inputs(query=query, bundle=_sales_bundle()) @@ -240,42 +240,42 @@ def _bind_monthly(formula: str): class TestOperandGrainTimeAxis: def test_windowed_inner_admits_active_bucket(self): _bind_monthly("rank(sum(amount, window='1y', partition_by=customers.regions.name), " - "partition_by=ordered_at)") + "partition_by=ordered_at, direction='desc')") def test_nested_last_drops_its_axis(self): with pytest.raises(ValueError) as ei: - _bind_monthly(f"rank({LAST_INNER}, partition_by=ordered_at)") + _bind_monthly(f"rank({LAST_INNER}, partition_by=ordered_at, direction='desc')") _assert_membership_error(msg=str(ei.value), key="ordered_at", grain="customers.regions.name") def test_nested_last_keeps_remaining_keys(self): - _bind_monthly(f"rank({LAST_INNER}, partition_by=customers.regions.name)") + _bind_monthly(f"rank({LAST_INNER}, partition_by=customers.regions.name, direction='desc')") def test_aggregate_free_nested_last_drops_its_axis(self): with pytest.raises(ValueError) as ei: - _bind_monthly(f"rank({LAST_SHIFT}, partition_by=ordered_at)") + _bind_monthly(f"rank({LAST_SHIFT}, partition_by=ordered_at, direction='desc')") _assert_membership_error(msg=str(ei.value), key="ordered_at", grain="customers.regions.name") def test_aggregate_free_nested_last_keeps_remaining_keys(self): - _bind_monthly(f"rank({LAST_SHIFT}, partition_by=customers.regions.name)") + _bind_monthly(f"rank({LAST_SHIFT}, partition_by=customers.regions.name, direction='desc')") def test_aggregate_free_leaf_beside_nested_last_keeps_the_query_grain(self): - _bind_monthly(f"rank(customers.regions.name + {LAST_SHIFT}, partition_by=ordered_at)") + _bind_monthly(f"rank(customers.regions.name + {LAST_SHIFT}, partition_by=ordered_at, direction='desc')") def test_aggregate_free_literal_beside_nested_last_drops_its_axis(self): with pytest.raises(ValueError) as ei: - _bind_monthly(f"rank({LAST_SHIFT} + 1, partition_by=ordered_at)") + _bind_monthly(f"rank({LAST_SHIFT} + 1, partition_by=ordered_at, direction='desc')") _assert_membership_error(msg=str(ei.value), key="ordered_at", grain="customers.regions.name") def test_aggregate_free_shift_keeps_the_query_grain(self): - _bind_monthly("rank(time_shift(customers.regions.name, -1), partition_by=ordered_at)") + _bind_monthly("rank(time_shift(customers.regions.name, -1), partition_by=ordered_at, direction='desc')") class TestRepeatedKeyword: @pytest.mark.parametrize(argnames="formula,call", argvalues=[ - ("rank(sum(amount), partition_by=region, partition_by=city)", "rank"), + ("rank(sum(amount), partition_by=region, partition_by=city, direction='desc')", "rank"), ("sum(amount, partition_by=region, partition_by=city)", "sum"), ("amount:sum(partition_by=region, partition_by=city)", "sum"), ]) diff --git a/tests/test_dev1958_row_leaf_ban.py b/tests/test_dev1958_row_leaf_ban.py index 032c860a..5ab8bb3a 100644 --- a/tests/test_dev1958_row_leaf_ban.py +++ b/tests/test_dev1958_row_leaf_ban.py @@ -44,6 +44,8 @@ def _bundle() -> ResolvedSourceBundle: def _call(op: str, inner: str) -> str: if op == "ntile": return f"ntile({inner}, n=4)" + if op in ("rank", "dense_rank"): + return f"{op}({inner}, direction='desc')" return f"time_shift({inner}, -1)" if op == "time_shift" else f"{op}({inner})" diff --git a/tests/test_dev1964_dual_phase_consumption.py b/tests/test_dev1964_dual_phase_consumption.py index f2f02835..8ca51ff2 100644 --- a/tests/test_dev1964_dual_phase_consumption.py +++ b/tests/test_dev1964_dual_phase_consumption.py @@ -45,18 +45,18 @@ R = "avg(sum(amount, partition_by=[city, region]), partition_by=region)" RLEVEL = {"expression": f"CASE WHEN {R} > 50 THEN 'hi' ELSE 'lo' END", "name": "rlevel"} -TLEVEL = {"expression": f"CASE WHEN rank({R}) > 1 THEN 'top' ELSE 'rest' END", +TLEVEL = {"expression": f"CASE WHEN rank({R}, direction='desc') > 1 THEN 'top' ELSE 'rest' END", "name": "tlevel"} TOT = ModelMeasure(formula="amount:sum", name="tot") R_BY_REGION = {"North": 45.0, "South": 70.0, "East": 60.0, "Gap": 10.0, "Void": None} TOT_BY_REGION = {"North": 90.0, "South": 140.0, "East": 180.0, "Gap": 20.0, "Void": None} RLEVEL_BY_REGION = {"North": "lo", "South": "hi", "East": "hi", "Gap": "lo", "Void": "lo"} -RANK_BY_REGION = {"South": 1, "East": 2, "North": 3, "Gap": 4, "Void": 5} +RANK_BY_REGION = {"South": 1, "East": 2, "North": 3, "Gap": 4, "Void": None} TLEVEL_BY_REGION = {"South": "rest", "East": "top", "North": "top", "Gap": "top", - "Void": "top"} + "Void": "rest"} -PLAIN_RANK = "rank(amount:sum(partition_by=region))" +PLAIN_RANK = "rank(amount:sum(partition_by=region), direction='desc')" PBAND = {"expression": f"CASE WHEN {PLAIN_RANK} > 1 THEN 'top' ELSE 'rest' END", "name": "pband"} @@ -143,13 +143,13 @@ async def test_dimension_only(self, exec_engine): async def test_rank_as_measure(self, exec_engine): resp = await exec_engine.execute(sales_q( dimensions=["region", TLEVEL], - measures=[TOT, ModelMeasure(formula=f"rank({R})", name="rk")])) + measures=[TOT, ModelMeasure(formula=f"rank({R}, direction='desc')", name="rk")])) assert _col(resp, "rk") == RANK_BY_REGION assert _col(resp, "tlevel") == TLEVEL_BY_REGION @pytest.mark.parametrize(("flt", "kept"), [ - pytest.param(f"rank({R}) > 1", {"East", "North", "Gap", "Void"}, id="gt"), - pytest.param(f"rank({R}) < 4", {"South", "East", "North"}, id="lt"), + pytest.param(f"rank({R}, direction='desc') > 1", {"East", "North", "Gap"}, id="gt"), + pytest.param(f"rank({R}, direction='desc') < 4", {"South", "East", "North"}, id="lt"), ]) async def test_rank_in_filter(self, exec_engine, flt, kept): resp = await exec_engine.execute(sales_q( @@ -160,7 +160,7 @@ async def test_rank_in_filter(self, exec_engine, flt, kept): async def test_rank_as_order_target(self, exec_engine): resp = await exec_engine.execute(sales_q( dimensions=["region", TLEVEL], measures=[TOT], - order=[{"column": f"rank({R})", "direction": "asc"}])) + order=[{"column": f"rank({R}, direction='desc')", "direction": "asc"}])) assert _regions(resp) == ["South", "East", "North", "Gap", "Void"] async def test_two_dimensions_share_the_reaggregation(self, exec_engine): @@ -169,7 +169,7 @@ async def test_two_dimensions_share_the_reaggregation(self, exec_engine): measures=[TOT, ModelMeasure(formula=R, name="r")])) got = {r["sales.region"]: (r["sales.tlevel"], r["sales.rlevel"]) for r in resp.data} assert got == {"South": ("rest", "hi"), "East": ("top", "hi"), - "North": ("top", "lo"), "Gap": ("top", "lo"), "Void": ("top", "lo")} + "North": ("top", "lo"), "Gap": ("top", "lo"), "Void": ("rest", "lo")} assert _col(resp, "tot") == _approx(TOT_BY_REGION) assert _col(resp, "r") == _approx(R_BY_REGION) assert not any(REGROUP_LEAF_PREFIX in c or c.endswith(".grain") for c in resp.columns) @@ -187,8 +187,8 @@ async def test_order_by_dimension_name_sorts_band(self, exec_engine): dimensions=["region", PBAND], measures=[TOT], order=[{"column": "pband", "direction": "asc"}])) bands = [r["sales.pband"] for r in resp.data] - assert bands == ["rest", "top", "top", "top", "top"] - assert _regions(resp)[0] == "East" + assert bands == ["rest", "rest", "top", "top", "top"] + assert set(_regions(resp)[:2]) == {"East", "Void"} class TestFilterOnAggregateOwnedByDimensionTransform: @@ -202,7 +202,7 @@ async def test_executes_at_query_grain(self, exec_engine): assert _col(resp, "tot") == _approx({"South": 140.0, "East": 180.0}) def test_finer_partition_key_is_a_typed_error(self): - band = {"expression": "CASE WHEN rank(amount:sum(partition_by=[city, region])) > 1 " + band = {"expression": "CASE WHEN rank(amount:sum(partition_by=[city, region]), direction='desc') > 1 " "THEN 'top' ELSE 'rest' END", "name": "cband"} query = sales_q(dimensions=["region", band], measures=[TOT], filters=["amount:sum(partition_by=[city, region]) > 50"]) @@ -264,7 +264,7 @@ def _fine_cases(): r"order item\b"), ("arithmetic", {"measures": [ModelMeasure(formula=f"{FINE} + 1", name="f")]}, r"measure 'f'"), - ("transform", {"measures": [ModelMeasure(formula=f"rank({FINE})", name="f")]}, + ("transform", {"measures": [ModelMeasure(formula=f"rank({FINE}, direction='desc')", name="f")]}, r"measure 'f'"), ("filter", {"measures": [TOT], "filters": [f"{FINE} < amount:sum"]}, r"filter\b"), ("split-filter", @@ -294,7 +294,7 @@ def test_combined_consumer_rejected(self, kw, location, with_dim): @pytest.mark.parametrize(("dim", "agg"), [(CITY_BAND, "amount:sum(partition_by=[city, region])"), (FINE_DIM, FINE)], ids=["plain", "reagg"]) - @pytest.mark.parametrize("template", ["rank({})", "{} + 1"], ids=["transform", "arithmetic"]) + @pytest.mark.parametrize("template", ["rank({}, direction='desc')", "{} + 1"], ids=["transform", "arithmetic"]) def test_order_expression_over_dimension_aggregate(self, dim, agg, template): query = sales_q(dimensions=["region", dim], measures=[TOT], order=[{"column": template.format(agg), "direction": "asc"}]) diff --git a/tests/test_dev1967_stage_grain.py b/tests/test_dev1967_stage_grain.py index fc5dcfb1..6eb83f44 100644 --- a/tests/test_dev1967_stage_grain.py +++ b/tests/test_dev1967_stage_grain.py @@ -65,7 +65,7 @@ def test_partitioned_measure_is_not_a_member(self) -> None: def test_transform_valued_computed_dimension_is_a_member(self) -> None: grain = _grain( - dimensions=["status", {"expression": "rank(sum(amount, partition_by=channel))", "name": "rk"}], + dimensions=["status", {"expression": "rank(sum(amount, partition_by=channel), direction='desc')", "name": "rk"}], measures=[{"formula": "*:count", "name": "n"}], ) assert grain == ["status", "rk"] diff --git a/tests/test_dev1976_dimension_values.py b/tests/test_dev1976_dimension_values.py index b4a9be85..269a15e7 100644 --- a/tests/test_dev1976_dimension_values.py +++ b/tests/test_dev1976_dimension_values.py @@ -62,7 +62,7 @@ RD_P = {"expression": P, "name": "rd"} RD_R = {"expression": R, "name": "rd"} X_C = {"expression": C, "name": "x"} -RK_P = {"expression": f"rank({P})", "name": "rk"} +RK_P = {"expression": f"rank({P}, direction='desc')", "name": "rk"} Q2 = {"expression": "quantity * 2", "name": "q2"} CITY_BAND = {"expression": f"CASE WHEN {C} > 45 THEN 'hi' ELSE 'lo' END", "name": "band"} @@ -161,8 +161,8 @@ async def test_arithmetic(self, exec_engine, dim, agg, expected): async def test_arithmetic_over_transform(self, exec_engine): resp = await exec_engine.execute(sales_q( dimensions=["region", RK_P], measures=[TOT], - order=[{"column": f"rank({P}) + 1", "direction": "desc"}])) - assert [r["sales.rk"] for r in resp.data] == [5, 4, 3, 2, 1] + order=[{"column": f"rank({P}, direction='desc') + 1", "direction": "desc"}])) + assert [r["sales.rk"] for r in resp.data] == [4, 3, 2, 1, None] async def test_finer_grained_aggregate(self, exec_engine): resp = await exec_engine.execute(sales_q( @@ -262,9 +262,9 @@ async def test_cross_model(self, exec_engine): # IN over aggregates: phase follows the operands, and no column is dropped. # --------------------------------------------------------------------------- # IN_P = f"{P} in (90, 140)" -TOP2 = "rank(amount:sum) in (1, 2)" +TOP2 = "rank(amount:sum, direction='desc') in (1, 2)" P_HIT = {"North": True, "South": True, "East": False, "Gap": False, "Void": None} -TOP2_HIT = {"North": False, "South": True, "East": True, "Gap": False, "Void": False} +TOP2_HIT = {"North": False, "South": True, "East": True, "Gap": False, "Void": None} def _truth(resp, key: str = "sales.region") -> dict: @@ -293,8 +293,8 @@ async def test_inside_conditional(self, exec_engine): async def test_transform_over_predicate(self, exec_engine): resp = await exec_engine.execute(sales_q( - dimensions=["region"], measures=[_m(f"rank({IN_P})")])) - assert _col(resp, "m") == {"North": 1, "South": 1, "East": 3, "Gap": 3, "Void": 5} + dimensions=["region"], measures=[_m(f"rank({IN_P}, direction='desc')")])) + assert _col(resp, "m") == {"North": 1, "South": 1, "East": 3, "Gap": 3, "Void": None} async def test_cross_model(self, exec_engine): resp = await exec_engine.execute(chain_q( @@ -359,7 +359,7 @@ async def test_combined_select(self, monkeypatch): async def test_transform_chain(self, monkeypatch): monkeypatch.setattr(SQLGenerator, "_unmaterialised_post_slots", staticmethod(lambda _pq, _aliases: [])) - query = sales_q(dimensions=["region"], measures=[_m("rank(amount:sum) + 1")]) + query = sales_q(dimensions=["region"], measures=[_m("rank(amount:sum, direction='desc') + 1")]) with pytest.raises(ValueError, match="silently dropped"): await gen(query, dialect="duckdb") @@ -530,7 +530,7 @@ def test_partition_key_error(self, dims, formula): assert ei.value.location == "measure 'm'" @pytest.mark.parametrize(("formula", "family"), [ - pytest.param("rank(amount) + amount", TransformInputError, id="transform-input"), + pytest.param("rank(amount, direction='desc') + amount", TransformInputError, id="transform-input"), pytest.param("cumsum(amount:sum) + amount", TimeAxisError, id="time-axis"), pytest.param(f"sum({C}, window='90d') + amount", ReaggregationError, id="reaggregation-window"), diff --git a/tests/test_dev2013_expression_attribution.py b/tests/test_dev2013_expression_attribution.py index 1854818e..ce156944 100644 --- a/tests/test_dev2013_expression_attribution.py +++ b/tests/test_dev2013_expression_attribution.py @@ -449,7 +449,7 @@ async def test_fanning_witness_names_the_hop(self, fanning_engine): async def test_unsupported_kind_cannot_be_analysed(self, exec_engine): resp = await exec_engine.execute(_acr(dimensions=[ - "region", _dim("rank(sum(amount, partition_by=[city, region]))", "rk")])) + "region", _dim("rank(sum(amount, partition_by=[city, region]), direction='desc')", "rk")])) (w,) = broadcast_warnings(resp) (d,) = w.dimensions assert d.dimension == "rk" diff --git a/tests/test_distinct_dimension_values.py b/tests/test_distinct_dimension_values.py index b41c0b03..dd7fb01e 100644 --- a/tests/test_distinct_dimension_values.py +++ b/tests/test_distinct_dimension_values.py @@ -330,12 +330,12 @@ async def test_filter_star_count(self) -> None: await self._expect_reject(q, model) async def test_filter_transform_call(self) -> None: - """Case 5: ``"rank(amount:sum) <= 5"`` → reject.""" + """Case 5: ``"rank(amount:sum, direction='desc') <= 5"`` → reject.""" model = _orders_model() q = SlayerQuery( source_model="orders", dimensions=[ColumnRef(name="status")], - filters=["rank(amount:sum) <= 5"], + filters=["rank(amount:sum, direction='desc') <= 5"], distinct_dimension_values=False, ) await self._expect_reject(q, model) diff --git a/tests/test_error_messages.py b/tests/test_error_messages.py index d76a08cb..c5d9a1f2 100644 --- a/tests/test_error_messages.py +++ b/tests/test_error_messages.py @@ -171,7 +171,7 @@ def test_raw_over_in_filter(self): " expr: 'rank() OVER (ORDER BY x) <= 3'\n" " source: raw OVER(...) in DSL filter\n" " suggestion: use a rank-family transform " - "(e.g. `rank() <= N`)." + "(e.g. `rank(, direction='desc') <= N`)." ) def test_filter_naming_windowed_column(self): diff --git a/tests/test_functional_agg_positions.py b/tests/test_functional_agg_positions.py index 52cd9b48..47e1cf4d 100644 --- a/tests/test_functional_agg_positions.py +++ b/tests/test_functional_agg_positions.py @@ -600,7 +600,7 @@ def mk(expr: str) -> SlayerQuery: "sum(amount, partition_by=region) - sum(amount, partition_by=city)" ) _MIXED_RANK_FUNC = ( - "rank(sum(amount, partition_by=region) - sum(amount, partition_by=city))" + "rank(sum(amount, partition_by=region) - sum(amount, partition_by=city), direction='desc')" ) diff --git a/tests/test_functional_aggregations.py b/tests/test_functional_aggregations.py index 843dc6b9..1dc5ee36 100644 --- a/tests/test_functional_aggregations.py +++ b/tests/test_functional_aggregations.py @@ -220,8 +220,8 @@ def test_transform_over_functional_agg(self) -> None: assert parse_expr("cumsum(sum(revenue))") == parse_expr("cumsum(revenue:sum)") def test_rank_over_functional_agg_with_partition(self) -> None: - assert parse_expr("rank(sum(revenue), partition_by=status)") == parse_expr( - "rank(revenue:sum, partition_by=status)" + assert parse_expr("rank(sum(revenue), partition_by=status, direction='desc')") == parse_expr( + "rank(revenue:sum, partition_by=status, direction='desc')" ) def test_scalar_call_stays_scalar(self) -> None: diff --git a/tests/test_memories_resolver_typed.py b/tests/test_memories_resolver_typed.py index b09d47ae..4086d362 100644 --- a/tests/test_memories_resolver_typed.py +++ b/tests/test_memories_resolver_typed.py @@ -76,7 +76,7 @@ def test_list_partition_by_parses(self) -> None: # parse_expr accepts list-valued partition_by; only the inner agg # token surfaces. assert _tokens( - "rank(amount:sum, partition_by=[region, channel])" + "rank(amount:sum, partition_by=[region, channel], direction='desc')" ) == ["amount:sum"] diff --git a/tests/test_projection_trim.py b/tests/test_projection_trim.py index 13455ce5..2a036f4b 100644 --- a/tests/test_projection_trim.py +++ b/tests/test_projection_trim.py @@ -192,13 +192,13 @@ async def test_repro1_order_by_aggregate_not_projected( async def test_repro2_rank_of_col_agg_hides_intermediate( self, funds_model: SlayerModel, ) -> None: - """Repro (2): ``rank(expensenet:sum)`` named ``expense_rank`` must + """Repro (2): ``rank(expensenet:sum, direction='desc')`` named ``expense_rank`` must produce a 2-column outer SELECT (geozone, expense_rank). The hoisted ``expensenet_sum`` stays inside a CTE, never in the outer projection.""" query = SlayerQuery( source_model="funds", dimensions=[ColumnRef(name="geozone")], - measures=[ModelMeasure(formula="rank(expensenet:sum)", name="expense_rank")], + measures=[ModelMeasure(formula="rank(expensenet:sum, direction='desc')", name="expense_rank")], limit=3, ) sql = await _generate(query, funds_model) @@ -305,9 +305,9 @@ class TestWindowArgReuse: @pytest.mark.parametrize( "transform_formula,measure_name", [ - ("rank(revenue:sum)", "r_rank"), + ("rank(revenue:sum, direction='desc')", "r_rank"), ("percent_rank(revenue:sum)", "r_pct_rank"), - ("dense_rank(revenue:sum)", "r_dense"), + ("dense_rank(revenue:sum, direction='desc')", "r_dense"), ("ntile(revenue:sum, n=3)", "r_ntile"), ("cumsum(revenue:sum)", "r_cumsum"), ("lag(revenue:sum, -1)", "r_lag"), @@ -503,7 +503,7 @@ async def test_attributes_match_outer_projection_for_repros( query = SlayerQuery( source_model="funds", dimensions=[ColumnRef(name="geozone")], - measures=[ModelMeasure(formula="rank(expensenet:sum)", name="expense_rank")], + measures=[ModelMeasure(formula="rank(expensenet:sum, direction='desc')", name="expense_rank")], limit=3, ) resp = await engine.execute(query=query, dry_run=True) @@ -655,7 +655,7 @@ async def test_star_count_named_measure( async def test_rank_inside_arithmetic_reuses_named_arg( self, orders_model: SlayerModel, ) -> None: - """A formula like ``rank(revenue:sum) + 1`` whose inner argument is + """A formula like ``rank(revenue:sum, direction='desc') + 1`` whose inner argument is already a declared named measure must reuse that alias and not re-materialize the inner aggregate.""" query = SlayerQuery( @@ -666,7 +666,7 @@ async def test_rank_inside_arithmetic_reuses_named_arg( )], measures=[ ModelMeasure(formula="revenue:sum", name="total"), - ModelMeasure(formula="rank(revenue:sum) + 1", name="rank_plus_one"), + ModelMeasure(formula="rank(revenue:sum, direction='desc') + 1", name="rank_plus_one"), ], ) sql = await _generate(query, orders_model) @@ -719,7 +719,7 @@ async def test_inner_stage_keeps_full_projection( name="inner_stage", source_model="orders", dimensions=[ColumnRef(name="status")], - measures=[ModelMeasure(formula="rank(revenue:sum)", name="rev_rank")], + measures=[ModelMeasure(formula="rank(revenue:sum, direction='desc')", name="rev_rank")], ) outer = SlayerQuery( source_model="inner_stage", @@ -756,7 +756,7 @@ async def test_repro2_column_count_matches_declaration( query = SlayerQuery( source_model="funds", dimensions=[ColumnRef(name="geozone")], - measures=[ModelMeasure(formula="rank(expensenet:sum)", name="expense_rank")], + measures=[ModelMeasure(formula="rank(expensenet:sum, direction='desc')", name="expense_rank")], limit=3, ) sql = await _generate(query, funds_model, dialect=dialect) @@ -790,7 +790,7 @@ async def test_query_backed_wrap_keeps_full_projection( query = SlayerQuery( source_model="funds", dimensions=[ColumnRef(name="geozone")], - measures=[ModelMeasure(formula="rank(expensenet:sum)", name="expense_rank")], + measures=[ModelMeasure(formula="rank(expensenet:sum, direction='desc')", name="expense_rank")], ) model = await engine.create_model_from_query(query=query, name="ranked_funds", save=True) # The model's `columns` (derived from the wrapped query's projection) @@ -821,7 +821,7 @@ async def test_runtime_list_final_stage_trimmed_inner_not( name="ranked", source_model="orders", dimensions=[ColumnRef(name="status")], - measures=[ModelMeasure(formula="rank(revenue:sum)", name="r")], + measures=[ModelMeasure(formula="rank(revenue:sum, direction='desc')", name="r")], ) outer = SlayerQuery( source_model="ranked", @@ -1150,7 +1150,7 @@ async def test_structural_reuse_inline_subagg_collapses( self, orders_model: SlayerModel, ) -> None: """``m1={"formula":"revenue:sum","name":"total"}`` plus - ``m2={"formula":"rank(revenue:sum)","name":"r"}`` → ``r``'s OVER + ``m2={"formula":"rank(revenue:sum, direction='desc')","name":"r"}`` → ``r``'s OVER references ``total``'s alias (orders.total), no inner duplicate.""" query = SlayerQuery( source_model="orders", @@ -1160,7 +1160,7 @@ async def test_structural_reuse_inline_subagg_collapses( )], measures=[ ModelMeasure(formula="revenue:sum", name="total"), - ModelMeasure(formula="rank(revenue:sum)", name="r"), + ModelMeasure(formula="rank(revenue:sum, direction='desc')", name="r"), ], ) sql = await _generate(query, orders_model) @@ -1181,7 +1181,7 @@ async def test_structural_reuse_inline_subagg_collapses( async def test_nested_window_formula_stages_without_duplicate( self, orders_model: SlayerModel, ) -> None: - """``rank(rank(revenue:sum))`` named ``r2`` stages naturally as + """``rank(rank(revenue:sum, direction='desc'), direction='desc')`` named ``r2`` stages naturally as step1+step2 CTEs; the outer SELECT projects ``[created_at, r2]`` only; no ``_inner_*`` duplicate hoist.""" query = SlayerQuery( @@ -1190,7 +1190,7 @@ async def test_nested_window_formula_stages_without_duplicate( dimension=ColumnRef(name="created_at"), granularity=TimeGranularity.MONTH, )], - measures=[ModelMeasure(formula="rank(rank(revenue:sum))", name="r2")], + measures=[ModelMeasure(formula="rank(rank(revenue:sum, direction='desc'), direction='desc')", name="r2")], ) sql = await _generate(query, orders_model) outer_cols = _outer_select_columns(sql) @@ -1198,7 +1198,7 @@ async def test_nested_window_formula_stages_without_duplicate( # Two RANK() OVER (...) windows must appear, stacked across CTE layers. rank_count = sql.upper().count("RANK()") assert rank_count >= 2, ( - f"nested rank(rank(...)) needs two RANK() windows, found {rank_count}.\n" + f"nested rank(rank(..., direction='desc'), direction='desc') needs two RANK() windows, found {rank_count}.\n" f"SQL:\n{sql}" ) diff --git a/tests/test_query_backed_typed_expansion.py b/tests/test_query_backed_typed_expansion.py index 83fcd63f..21d472b7 100644 --- a/tests/test_query_backed_typed_expansion.py +++ b/tests/test_query_backed_typed_expansion.py @@ -337,7 +337,7 @@ async def test_promoted_hidden_slot_keeps_metadata(self) -> None: """Codex review fix — when a hidden slot is later promoted to public, its type / format / description must be filled in. - Repro: declare ``rank(*:count)`` first (hoists ``*:count`` as a + Repro: declare ``rank(*:count, direction='desc')`` first (hoists ``*:count`` as a hidden dep with no display metadata), THEN declare ``*:count`` as a public measure. The promoted public slot must end up with ``type=INT`` (not the default None → DOUBLE fallback). @@ -352,7 +352,7 @@ async def test_promoted_hidden_slot_keeps_metadata(self) -> None: # rank uses *:count as a hidden inner; intern order # hoists *:count hidden first, then the public # *:count entry promotes the same slot. - {"formula": "rank(*:count)", "name": "ranked"}, + {"formula": "rank(*:count, direction='desc')", "name": "ranked"}, {"formula": "*:count"}, ], )], @@ -515,7 +515,7 @@ async def test_carries_default_time_dimension(self) -> None: tmp.cleanup() async def test_excludes_hidden_hoisted_slots(self) -> None: - """A query with ``rank(amount:sum)`` hoists the inner ``amount_sum`` + """A query with ``rank(amount:sum, direction='desc')`` hoists the inner ``amount_sum`` as a hidden slot. The migrated path exposes ONLY user-declared public columns; ``amount_sum`` is NOT a column on the virtual model (decision #3 — P4 closure). @@ -526,7 +526,7 @@ async def test_excludes_hidden_hoisted_slots(self) -> None: source_queries=[SlayerQuery( source_model="orders", dimensions=["status"], - measures=[{"formula": "rank(amount:sum)", "name": "rank_by_amt"}], + measures=[{"formula": "rank(amount:sum, direction='desc')", "name": "rank_by_amt"}], )], ) engine, tmp = await _engine() diff --git a/tests/test_rank_direction.py b/tests/test_rank_direction.py new file mode 100644 index 00000000..be0cc1da --- /dev/null +++ b/tests/test_rank_direction.py @@ -0,0 +1,440 @@ +"""Rank-family ordering direction and NULL inputs (spec: queries/transforms › Rank-family +ordering direction; Rank-family NULL inputs rank NULL; queries/measure-naming › Rank +direction spelled as its bare value).""" + +from __future__ import annotations + +import pytest + +from slayer.core import errors as core_errors +from slayer.core.formula import parse_formula +from slayer.core.keys import TransformKey +from slayer.core.models import ModelMeasure +from slayer.core.scope import ModelScope +from slayer.engine.binding import bind_expr +from slayer.engine.syntax import parse_expr +from slayer.ir.source_bundle import ResolvedSourceBundle + +from tests._dev1847_fixtures import ( + _SALES_ROWS_WIDE, + corders_model, + customers_model, + dev1847_models, + gen, + make_exec_engine, + regions_model, + rows_by, + sales_model, + sales_q, +) +from tests._rank_direction_fixtures import ( + CITY_DENSE_DESC, + CITY_NAME_RANK_ASC, + CITY_RANK_ASC, + DIALECTS, + MIN_CITY_RANK_ASC, + NTILE2, + PERCENT_RANK, + RANK_ASC, + RANK_DESC, + REGION_TOTALS, + cell_totals, + rank_within, + rank_windows, +) + +BOTH_SPELLINGS = ("direction='asc'", "direction='desc'", "lowest first", "highest first") + + +@pytest.fixture(params=["sqlite", "duckdb"]) +async def engine(request): + async for e in make_exec_engine(request=request): + yield e + + +def _sales_with_measure(formula: str): + model = sales_model() + model.measures = [ModelMeasure(name="saved_rank", formula=formula)] + return [model, regions_model(), customers_model(), corders_model()] + + +@pytest.fixture(params=["sqlite", "duckdb"]) +async def saved_asc_engine(request): + async for e in make_exec_engine( + request=request, models=_sales_with_measure("rank(sum(amount), direction='asc')")): + yield e + + +@pytest.fixture(params=["sqlite", "duckdb"]) +async def saved_bare_engine(request): + async for e in make_exec_engine(request=request, models=_sales_with_measure("rank(sum(amount))")): + yield e + + +def _m(formula: str, name: str = "r") -> ModelMeasure: + return ModelMeasure(formula=formula, name=name) + + +def _by_region(resp, name: str = "r") -> dict: + return {k[0]: v[f"sales.{name}"] for k, v in rows_by(resp, "sales.region").items()} + + +def _by_region_city(resp, name: str = "r") -> dict: + return {k: v[f"sales.{name}"] for k, v in rows_by(resp, "sales.region", "sales.city").items()} + + +def _approx(d: dict) -> dict: + return {k: (None if v is None else pytest.approx(v)) for k, v in d.items()} + + +def _bind(formula: str): + models = dev1847_models() + bundle = ResolvedSourceBundle(dialect="postgres", source_model=models[0], + referenced_models=models[1:]) + return bind_expr(parse_expr(formula), scope=ModelScope(source_model=models[0]), + bundle=bundle).value_key + + +def _assert_missing_direction(msg: str, op: str = "rank") -> None: + assert f"'{op}'" in msg or f"{op}(" in msg, msg + for part in BOTH_SPELLINGS: + assert part in msg, (part, msg) + + +class TestOracleSelfCheck: + def test_region_ranks(self): + flat = {(None, r): v for r, v in REGION_TOTALS.items()} + assert {k[1]: v for k, v in rank_within(flat, descending=False).items()} == RANK_ASC + assert {k[1]: v for k, v in rank_within(flat, descending=True).items()} == RANK_DESC + assert REGION_TOTALS == {k[0]: v for k, v in cell_totals(lambda r: (r[1],)).items()} + + def test_city_ranks(self): + totals = cell_totals(lambda r: (r[1], r[2])) + assert rank_within(totals, descending=False) == CITY_RANK_ASC + assert rank_within(totals, descending=True, dense=True) == CITY_DENSE_DESC + names = {(r[1], r[2]): r[2] for r in _SALES_ROWS_WIDE} + assert rank_within(names, descending=False) == CITY_NAME_RANK_ASC + + +# --------------------------------------------------------------------------- # +# Direction: executed values. +# --------------------------------------------------------------------------- # +class TestDirectionValues: + async def test_ascending_rank_lowest_first(self, engine): + resp = await engine.execute(sales_q( + dimensions=["region"], measures=[_m("rank(sum(amount), direction='asc')")])) + assert _by_region(resp) == RANK_ASC + + async def test_descending_rank_highest_first(self, engine): + resp = await engine.execute(sales_q( + dimensions=["region"], measures=[_m("rank(sum(amount), direction='desc')")])) + assert _by_region(resp) == RANK_DESC + + async def test_dense_rank_takes_direction(self, engine): + resp = await engine.execute(sales_q( + dimensions=["region"], measures=[_m("dense_rank(sum(amount), direction='asc')")])) + assert _by_region(resp) == RANK_ASC + + @pytest.mark.parametrize("spelling", ["ASC", "Ascending", " ascending "]) + async def test_synonyms_execute(self, engine, spelling): + resp = await engine.execute(sales_q( + dimensions=["region"], measures=[_m(f"rank(sum(amount), direction='{spelling}')")])) + assert _by_region(resp) == RANK_ASC + + async def test_non_numeric_inner(self, engine): + resp = await engine.execute(sales_q( + dimensions=["region"], measures=[_m("rank(min(city), direction='asc')")])) + assert _by_region(resp) == MIN_CITY_RANK_ASC + + async def test_combines_with_partition_by(self, engine): + resp = await engine.execute(sales_q( + dimensions=["region", "city"], + measures=[_m("rank(sum(amount), partition_by=region, direction='asc')")])) + assert _by_region_city(resp) == CITY_RANK_ASC + + async def test_both_directions_stay_distinct(self, engine): + resp = await engine.execute(sales_q( + dimensions=["region"], + measures=["rank(sum(amount), direction='asc')", "rank(sum(amount), direction='desc')"])) + assert _by_region(resp, "rank_amount_sum_asc") == RANK_ASC + assert _by_region(resp, "rank_amount_sum_desc") == RANK_DESC + + async def test_ntile_and_percent_rank_ascending(self, engine): + resp = await engine.execute(sales_q( + dimensions=["region"], + measures=[_m("ntile(sum(amount), n=2)", "nt"), _m("percent_rank(sum(amount))", "pr")])) + assert _by_region(resp, "nt") == NTILE2 + assert _by_region(resp, "pr") == _approx(PERCENT_RANK) + + +class TestDirectionInEveryPosition: + async def test_filter_keeps_the_cheapest(self, engine): + resp = await engine.execute(sales_q( + dimensions=["region"], measures=[_m("sum(amount)", "a")], + filters=["rank(sum(amount), direction='asc') <= 1"])) + assert set(_by_region(resp, "a")) == {"Gap"} + + async def test_order_by_ascending_rank(self, engine): + resp = await engine.execute(sales_q( + dimensions=["region"], measures=[_m("sum(amount)", "a")], + order=[{"column": "rank(sum(amount), direction='asc')", "direction": "asc"}])) + assert [r["sales.region"] for r in resp.data] == ["Gap", "North", "South", "East", "Void"] + + async def test_computed_dimension(self, engine): + dim = {"expression": "rank(sum(amount, partition_by=region), direction='asc')", "name": "rk"} + resp = await engine.execute(sales_q( + dimensions=["region", dim], measures=[_m("sum(amount)", "a")])) + got = {k[0]: k[1] for k in rows_by(resp, "sales.region", "sales.rk")} + assert got == RANK_ASC + + @pytest.mark.parametrize(("direction", "want"), [("asc", 1340 / 32), ("desc", 810 / 33)]) + async def test_aggregation_parameter(self, engine, direction, want): + resp = await engine.execute(sales_q(measures=[_m( + f"weighted_avg(amount, weight=rank(sum(amount, partition_by=region), " + f"direction='{direction}'))", "w")])) + [row] = resp.data + assert float(row["sales.w"]) == pytest.approx(want) + + async def test_saved_model_measure(self, saved_asc_engine): + resp = await saved_asc_engine.execute(sales_q(dimensions=["region"], measures=["saved_rank"])) + assert _by_region(resp, "saved_rank") == RANK_ASC + + +# --------------------------------------------------------------------------- # +# Binding. +# --------------------------------------------------------------------------- # +class TestBinding: + @pytest.mark.parametrize(("spelling", "canonical"), [ + ("desc", "desc"), ("DESC", "desc"), (" Descending ", "desc"), + ("asc", "asc"), ("Ascending", "asc"), (" ASCENDING", "asc"), + ]) + def test_synonyms_normalise(self, spelling, canonical): + got = _bind(f"rank(amount:sum, direction='{spelling}')") + want = _bind(f"rank(amount:sum, direction='{canonical}')") + assert isinstance(got, TransformKey) + assert got == want + assert ("direction", canonical) in got.kwargs + + def test_directions_are_distinct_values(self): + assert _bind("rank(amount:sum, direction='asc')") != _bind("rank(amount:sum, direction='desc')") + assert (_bind("dense_rank(amount:sum, direction='asc')") + != _bind("dense_rank(amount:sum, direction='desc')")) + + def test_ntile_and_percent_rank_carry_no_direction(self): + for formula in ("ntile(amount:sum, n=4)", "percent_rank(amount:sum)"): + key = _bind(formula) + assert isinstance(key, TransformKey) + assert all(k != "direction" for k, _v in key.kwargs) + + def test_error_is_a_query_type_error(self): + assert issubclass(core_errors.TransformArgumentError, core_errors.QueryTypeError) + assert issubclass(core_errors.TransformArgumentError, ValueError) + + +# --------------------------------------------------------------------------- # +# Direction errors. +# --------------------------------------------------------------------------- # +_MISSING = [ + pytest.param({"dimensions": ["region"], "measures": [_m("rank(sum(amount))")]}, + "rank", id="measure"), + pytest.param({"dimensions": ["region", "city"], + "measures": [_m("dense_rank(sum(amount), partition_by=region)")]}, + "dense_rank", id="measure-dense-partitioned"), + pytest.param({"dimensions": ["region"], "measures": [_m("sum(amount)", "a")], + "filters": ["rank(sum(amount)) <= 2"]}, "rank", id="filter"), + pytest.param({"dimensions": ["region"], "measures": [_m("sum(amount)", "a")], + "order": [{"column": "rank(sum(amount))", "direction": "asc"}]}, + "rank", id="order"), + pytest.param({"dimensions": ["region", {"expression": "rank(sum(amount, partition_by=region))", + "name": "rk"}], + "measures": [_m("sum(amount)", "a")]}, "rank", id="computed-dimension"), + pytest.param({"dimensions": ["region"], + "measures": [_m("weighted_avg(amount, weight=rank(sum(amount, " + "partition_by=region)))", "w")]}, + "rank", id="aggregation-parameter"), +] + + +class TestDirectionErrors: + @pytest.mark.parametrize(("fields", "op"), _MISSING) + async def test_missing_direction_fails_before_sql(self, engine, fields, op): + for dry_run in (True, False): + with pytest.raises(core_errors.TransformArgumentError) as ei: + await engine.execute(sales_q(**fields), dry_run=dry_run) + _assert_missing_direction(str(ei.value), op=op) + + async def test_saved_measure_without_direction(self, saved_bare_engine): + with pytest.raises(core_errors.TransformArgumentError) as ei: + await saved_bare_engine.execute(sales_q(dimensions=["region"], measures=["saved_rank"])) + _assert_missing_direction(str(ei.value)) + + @pytest.mark.parametrize("formula", [ + "rank(sum(amount), direction='up')", + "rank(sum(amount), direction=region)", + "rank(sum(amount), direction=1)", + "dense_rank(sum(amount), direction='')", + ]) + async def test_unrecognised_or_non_literal(self, engine, formula): + with pytest.raises(core_errors.TransformArgumentError) as ei: + await engine.execute(sales_q(dimensions=["region"], measures=[_m(formula)])) + msg = str(ei.value) + for word in ("asc", "desc", "ascending", "descending"): + assert word in msg, msg + + @pytest.mark.parametrize(("formula", "op"), [ + ("ntile(sum(amount), n=2, direction='desc')", "ntile"), + ("percent_rank(sum(amount), direction='asc')", "percent_rank"), + ]) + async def test_ntile_and_percent_rank_reject_direction(self, engine, formula, op): + with pytest.raises(core_errors.TransformArgumentError) as ei: + await engine.execute(sales_q(dimensions=["region"], measures=[_m(formula)])) + msg = str(ei.value) + assert op in msg, msg + assert "ascending" in msg and "direction" in msg, msg + + @pytest.mark.parametrize("formula", [ + "rank(amount:sum, foo=1, direction='desc')", + "percent_rank(amount:sum, foo=1)", + "consecutive_periods(amount:sum > 0, foo=1)", + "lag(amount:sum, periods=region)", + "time_shift(amount:sum, granularity='month')", + "ntile(amount:sum)", + "ntile(amount:sum, n=0)", + "ntile(amount:sum, n=region)", + ]) + def test_other_transform_argument_errors_are_typed(self, formula): + with pytest.raises(core_errors.TransformArgumentError): + _bind(formula) + + +class TestImporterParity: + @pytest.mark.parametrize("formula", [ + "rank(sum(amount))", + "dense_rank(sum(amount), partition_by=region)", + "rank(sum(amount), direction='sideways')", + "ntile(sum(amount), n=4, direction='asc')", + "percent_rank(sum(amount), direction='desc')", + ]) + def test_same_error_as_the_binder(self, formula): + with pytest.raises(core_errors.TransformArgumentError) as importer: + parse_formula(formula) + with pytest.raises(core_errors.TransformArgumentError) as binder: + _bind(formula) + assert str(importer.value) == str(binder.value) + + @pytest.mark.parametrize(("formula", "want"), [ + ("rank(sum(amount), direction='Ascending')", "asc"), + ("dense_rank(sum(amount), partition_by=region, direction='DESC')", "desc"), + ]) + def test_accepts_and_normalises(self, formula, want): + field = parse_formula(formula) + assert field.kwargs["direction"] == want # type: ignore[union-attr] + + def test_ntile_keeps_no_direction(self): + field = parse_formula("ntile(sum(amount), n=4)") + assert "direction" not in field.kwargs # type: ignore[union-attr] + + +# --------------------------------------------------------------------------- # +# NULL inputs. +# --------------------------------------------------------------------------- # +class TestNullInputs: + async def test_mixed_null_and_non_null(self, engine): + resp = await engine.execute(sales_q( + dimensions=["region"], + measures=[_m("rank(sum(amount), direction='desc')", "rk"), + _m("percent_rank(sum(amount))", "pr"), + _m("ntile(sum(amount), n=2)", "nt")])) + assert _by_region(resp, "rk") == RANK_DESC + assert _by_region(resp, "pr") == _approx(PERCENT_RANK) + assert _by_region(resp, "nt") == NTILE2 + + async def test_all_null_partition(self, engine): + resp = await engine.execute(sales_q( + dimensions=["region", "city"], + measures=[_m("dense_rank(sum(amount), partition_by=region, direction='desc')")])) + assert _by_region_city(resp) == CITY_DENSE_DESC + + async def test_null_row_inside_partition_takes_no_position(self, engine): + resp = await engine.execute(sales_q( + dimensions=["region", "city"], + measures=[_m("rank(city, partition_by=region, direction='asc')")])) + assert _by_region_city(resp) == CITY_NAME_RANK_ASC + + async def test_filter_drops_null_ranked_rows(self, engine): + resp = await engine.execute(sales_q( + dimensions=["region"], measures=[_m("sum(amount)", "a")], + filters=["rank(sum(amount), direction='asc') <= 5"])) + assert set(_by_region(resp, "a")) == {"East", "Gap", "North", "South"} + + @pytest.mark.parametrize(("formula", "op"), [ + ("ntile(sum(amount), n=2)", "ntile"), + ("percent_rank(sum(amount))", "percent_rank"), + ("dense_rank(sum(amount), direction='asc')", "dense_rank"), + ]) + async def test_null_inner_is_null_for_every_function(self, engine, formula, op): + resp = await engine.execute(sales_q(dimensions=["region"], measures=[_m(formula)])) + got = _by_region(resp) + assert got["Void"] is None, (op, got) + assert all(v is not None for k, v in got.items() if k != "Void"), (op, got) + + +# --------------------------------------------------------------------------- # +# Emission shape across dialects. +# --------------------------------------------------------------------------- # +_EMISSION = [ + pytest.param("rank(sum(amount), direction='asc')", "RANK", False, id="rank-asc"), + pytest.param("rank(sum(amount), direction='desc')", "RANK", True, id="rank-desc"), + pytest.param("dense_rank(sum(amount), direction='asc')", "DENSERANK", False, id="dense-asc"), + pytest.param("dense_rank(sum(amount), direction='desc')", "DENSERANK", True, id="dense-desc"), + pytest.param("ntile(sum(amount), n=4)", "NTILE", False, id="ntile"), + pytest.param("percent_rank(sum(amount))", "PERCENTRANK", False, id="percent-rank"), +] + + +class TestEmission: + @pytest.mark.parametrize("dialect", DIALECTS) + @pytest.mark.parametrize(("formula", "fn", "descending"), _EMISSION) + async def test_window_orders_by_direction_and_isolates_nulls(self, dialect, formula, fn, descending): + sql = await gen(sales_q(dimensions=["region"], measures=[_m(formula)]), dialect=dialect) + [window] = rank_windows(sql, dialect=dialect) + assert window.fn == fn + assert window.descending is descending + assert window.null_flag, sql + assert window.null_guarded, sql + assert window.partition_sql == [] + + @pytest.mark.parametrize("dialect", DIALECTS) + async def test_partition_keys_precede_the_null_flag(self, dialect): + sql = await gen(sales_q( + dimensions=["region", "city"], + measures=[_m("rank(sum(amount), partition_by=region, direction='asc')")]), dialect=dialect) + [window] = rank_windows(sql, dialect=dialect) + assert len(window.partition_sql) == 1 and "region" in window.partition_sql[0], sql + assert window.null_flag and window.null_guarded and not window.descending + + @pytest.mark.parametrize("formula", [p.values[0] for p in _EMISSION]) + async def test_tsql_matches_postgres(self, formula): + query = sales_q(dimensions=["region"], measures=[_m(formula)]) + [pg] = rank_windows(await gen(query, dialect="postgres"), dialect="postgres") + [ts] = rank_windows(await gen(query, dialect="tsql"), dialect="tsql") + assert (ts.fn, ts.descending, ts.null_flag, ts.null_guarded) == ( + pg.fn, pg.descending, True, True) + + +# --------------------------------------------------------------------------- # +# Result-key naming. +# --------------------------------------------------------------------------- # +class TestNaming: + @pytest.mark.parametrize(("formula", "key"), [ + ("rank(sum(amount), direction='desc')", "sales.rank_amount_sum_desc"), + ("rank(sum(amount), direction='Ascending')", "sales.rank_amount_sum_asc"), + ("rank(sum(amount), partition_by=region, direction='asc')", + "sales.rank_amount_sum_partition_by_region_asc"), + ("dense_rank(sum(amount), direction=' DESC ')", "sales.dense_rank_amount_sum_desc"), + ("ntile(sum(amount), n=4)", "sales.ntile_amount_sum_n_4"), + ]) + async def test_unnamed_key(self, engine, formula, key): + resp = await engine.execute(sales_q(dimensions=["region", "city"], measures=[formula])) + assert key in resp.columns, resp.columns + assert not any("direction" in c for c in resp.columns), resp.columns diff --git a/tests/test_rank_direction_golden_sql.py b/tests/test_rank_direction_golden_sql.py new file mode 100644 index 00000000..d3034370 --- /dev/null +++ b/tests/test_rank_direction_golden_sql.py @@ -0,0 +1,57 @@ +"""Golden SQL for rank-family window ordering and NULL isolation; mechanics in ``tests/_golden_harness.py``.""" + +from __future__ import annotations + +from pathlib import Path + +from slayer.core.models import ModelMeasure +from slayer.core.query import SlayerQuery + +from tests._dev1847_fixtures import gen, sales_q +from tests._golden_harness import bind_golden_tests, record_raise +from tests._rank_direction_fixtures import DIALECTS + +GOLDEN_PATH = Path(__file__).parent / "golden" / "rank_direction_sql_baseline.json" + +# ``::`` -> why this entry is allowed to change right now. +ALLOWED_DELTAS: dict[str, str] = {} + + +def _q(formula: str, dimensions: list[str]) -> SlayerQuery: + return sales_q(dimensions=dimensions, measures=[ModelMeasure(formula=formula, name="r")]) + + +def _cases() -> dict[str, SlayerQuery]: + region, region_city = ["region"], ["region", "city"] + return { + "rank/asc": _q("rank(sum(amount), direction='asc')", region), + "rank/desc": _q("rank(sum(amount), direction='desc')", region), + "rank/asc_partitioned": _q("rank(sum(amount), partition_by=region, direction='asc')", region_city), + "dense_rank/asc": _q("dense_rank(sum(amount), direction='asc')", region), + "dense_rank/desc": _q("dense_rank(sum(amount), direction='desc')", region), + "ntile/n4": _q("ntile(sum(amount), n=4)", region), + "percent_rank/plain": _q("percent_rank(sum(amount))", region), + "rank/non_numeric": _q("rank(min(city), direction='asc')", region), + } + + +async def _generate_one(query: SlayerQuery, dialect: str): + """Emitted SQL, or a structured record of the raised error.""" + try: + return await gen(query, dialect=dialect) + except Exception as exc: # noqa: BLE001 — the exception itself is contract + return record_raise(exc) + + +bind_golden_tests( + namespace=globals(), + golden_path=GOLDEN_PATH, + cases=_cases, + dialects=DIALECTS, + allowed=ALLOWED_DELTAS, + generate_one=_generate_one, +) + + +def test_no_case_records_an_error(baseline) -> None: + assert not [k for k, v in baseline.items() if isinstance(v, dict) and "error" in v] diff --git a/tests/test_rank_direction_migration.py b/tests/test_rank_direction_migration.py new file mode 100644 index 00000000..b0b9d72d --- /dev/null +++ b/tests/test_rank_direction_migration.py @@ -0,0 +1,434 @@ +"""Stored rank calls without a direction load as descending (spec: queries/transforms › +Stored rank calls without a direction load as descending).""" + +from __future__ import annotations + +import json +import os +import tempfile +from collections.abc import AsyncIterator, Awaitable, Callable +from typing import Any + +import pytest +import yaml +from fastapi.testclient import TestClient + +from slayer.api.server import create_app +from slayer.async_utils import run_sync +from slayer.core import errors as core_errors +from slayer.core.models import DatasourceConfig, SlayerModel +from slayer.core.query import SlayerQuery +from slayer.engine.query_engine import SlayerQueryEngine +from slayer.mcp.server import create_mcp_server +from slayer.memories.models import Memory +from slayer.sql import engine_factory +from slayer.storage import migrations as mig +from slayer.storage.base import StorageBackend +from slayer.storage.sqlite_conn import transaction +from slayer.storage.sqlite_storage import SQLiteStorage +from slayer.storage.yaml_storage import YAMLStorage + +from tests._dev1847_fixtures import _seed_sqlite, rows_by, sales_model +from tests._rank_direction_fixtures import RANK_DESC + +DESC = ", direction='desc'" +BARE = "rank(sum(amount))" +FILLED = "rank(sum(amount), direction='desc')" + + +def _model_dict(*, measures: list[dict], version: Any = 12, **extra) -> dict: + data = sales_model().model_dump(mode="json", exclude_none=True) + data.pop("version", None) + if version is not None: + data["version"] = version + data["measures"] = measures + data.update(extra) + return data + + +def _migrated_formula(formula: str) -> str: + model = SlayerModel.model_validate(_model_dict(measures=[{"name": "x", "formula": formula}])) + return model.measures[0].formula + + +def _assert_only_direction_added(before: str, after: str) -> None: + assert DESC in after, after + assert after.replace(DESC, "") == before + + +# --------------------------------------------------------------------------- # +# The stored-only gate in migrate(). +# --------------------------------------------------------------------------- # +class TestStoredOnlyGate: + @pytest.fixture + def probe(self, monkeypatch): + monkeypatch.setattr(mig, "_REGISTRY", {}) + monkeypatch.setitem(mig.CURRENT_VERSIONS, "Probe", 3) + + @mig.register_migration("Probe", 1) + def _plain(data: dict) -> dict: + return {**data, "plain": True} + + @mig.register_migration("Probe", 2, stored_only=True) + def _stored(data: dict) -> dict: + return {**data, "stored": True} + + @pytest.mark.parametrize(("payload", "plain", "stored"), [ + pytest.param({}, True, False, id="fresh"), + pytest.param({"version": 1}, True, True, id="explicit-v1"), + pytest.param({"version": 2}, False, True, id="explicit-v2"), + pytest.param({"version": 3}, False, False, id="current"), + ]) + def test_gate(self, probe, payload, plain, stored): + out = mig.migrate("Probe", payload) + assert out.get("plain", False) is plain + assert out.get("stored", False) is stored + assert out["version"] == 3 + + def test_current_versions(self): + assert mig.CURRENT_VERSIONS["SlayerModel"] == 13 + assert mig.CURRENT_VERSIONS["SlayerQuery"] == 5 + assert mig.CURRENT_VERSIONS["Memory"] == 3 + for key in (("SlayerModel", 12), ("SlayerQuery", 4), ("Memory", 2)): + assert key in mig._REGISTRY + + +# --------------------------------------------------------------------------- # +# The rewrite, through a stored ModelMeasure.formula. +# --------------------------------------------------------------------------- # +class TestRewrite: + @pytest.mark.parametrize(("formula", "want"), [ + (BARE, FILLED), + ("dense_rank(sum(amount), partition_by=region)", + "dense_rank(sum(amount), partition_by=region, direction='desc')"), + ("rank(amount:sum)", "rank(amount:sum, direction='desc')"), + ("rank(amount:sum(partition_by=region)) <= 3", + "rank(amount:sum(partition_by=region), direction='desc') <= 3"), + ("rank(rank(amount:sum)) + 1", + "rank(rank(amount:sum, direction='desc'), direction='desc') + 1"), + ("weighted_avg(amount, weight=rank(sum(amount, partition_by=region)))", + "weighted_avg(amount, weight=rank(sum(amount, partition_by=region), direction='desc'))"), + ("sum(quantity * rank(avg(unit_price, partition_by=product)))", + "sum(quantity * rank(avg(unit_price, partition_by=product), direction='desc'))"), + ("iif(dense_rank(amount:sum) <= 3, 'top', 'rest')", + "iif(dense_rank(amount:sum, direction='desc') <= 3, 'top', 'rest')"), + ("rank(sum(amount)) + ntile(sum(amount), n=4)", + "rank(sum(amount), direction='desc') + ntile(sum(amount), n=4)"), + ]) + def test_fills_descending(self, formula, want): + assert _migrated_formula(formula) == want + + def test_formatting_is_preserved(self): + formula = "rank(\n sum(amount, partition_by=region)\n) + 1" + _assert_only_direction_added(before=formula, after=_migrated_formula(formula)) + + @pytest.mark.parametrize("formula", [ + "rank(sum(amount), direction='asc')", + "rank(sum(amount), direction = 'DESC')", + "dense_rank(sum(amount), partition_by=region, direction='ascending')", + "iif(region == 'rank(x)', 1, 0)", + "customers.rank(amount)", + "ntile(sum(amount), n=4)", + "percent_rank(sum(amount))", + "my_rank(amount) + franks(x)", + "sum(amount)", + ]) + def test_untouched(self, formula): + assert _migrated_formula(formula) == formula + + @pytest.mark.parametrize("formula", ["rank(sum(amount)", "rank(sum(amount), 'oops)"]) + def test_untokenisable_formula_loads_byte_identical(self, formula): + assert _migrated_formula(formula) == formula + + def test_idempotent(self): + once = SlayerModel.model_validate(_model_dict(measures=[{"name": "x", "formula": BARE}])) + again_raw = once.model_dump(mode="json", exclude_none=True) + assert SlayerModel.model_validate(again_raw).measures[0].formula == FILLED + again_raw["version"] = 12 + assert SlayerModel.model_validate(again_raw).measures[0].formula == FILLED + + def test_mode_a_sql_untouched(self): + data = _model_dict(measures=[{"name": "x", "formula": BARE}]) + data["columns"].append({"name": "rn", "type": "INT", "sql": "dense_rank() over (order by id)"}) + data["columns"].append({"name": "fa", "type": "DOUBLE", "sql": "amount", + "filter": "city <> 'rank(x)'"}) + data["filters"] = ["city <> 'rank(x)'"] + data["aggregations"].append({"name": "rk", "formula": "rank() over (order by {value})"}) + model = SlayerModel.model_validate(data) + assert next(c for c in model.columns if c.name == "rn").sql == "dense_rank() over (order by id)" + assert model.filters == ["city <> 'rank(x)'"] + assert next(c for c in model.columns if c.name == "fa").filter == "city <> 'rank(x)'" + assert next(a for a in model.aggregations if a.name == "rk").formula == "rank() over (order by {value})" + assert model.measures[0].formula == FILLED + + +# --------------------------------------------------------------------------- # +# Stored queries: every Mode-B field, nested and inline. +# --------------------------------------------------------------------------- # +def _query_dict(version: Any = 4) -> dict: + data: dict = { + "source_model": "sales", + "dimensions": ["region", {"expression": "rank(amount:sum(partition_by=region))", "name": "rk"}], + "measures": [ + "rank(sum(amount)) + 1", + {"formula": "iif(dense_rank(amount:sum(partition_by=[region])) <= 2, 1, 0)", "name": "top"}, + ], + "filters": ["rank(sum(amount, partition_by=region)) <= 3 and city <> 'rank('"], + "order": [{"column": "abs(rank(amount:sum))", "direction": "asc"}], + "main_time_dimension": "rank(sum(amount))", + } + if version is not None: + data["version"] = version + return data + + +def _assert_query_filled(q: SlayerQuery) -> None: + raw = _query_dict() + dims = q.dimensions or [] + expr = next(d for d in dims if getattr(d, "name", None) == "rk") + _assert_only_direction_added(before=raw["dimensions"][1]["expression"], after=getattr(expr, "expression")) + measures = q.measures or [] + _assert_only_direction_added(before=raw["measures"][0], after=measures[0].formula) + _assert_only_direction_added(before=raw["measures"][1]["formula"], after=measures[1].formula) + assert q.filters == ["rank(sum(amount, partition_by=region), direction='desc') <= 3 and city <> 'rank('"] + order = q.order or [] + assert order[0].raw_formula == "abs(rank(amount:sum, direction='desc'))" + assert q.main_time_dimension == FILLED + + +class TestStoredQuery: + def test_old_query_every_field(self): + _assert_query_filled(SlayerQuery.model_validate(_query_dict(version=4))) + + @pytest.mark.parametrize("version", [4, None]) + def test_source_queries_of_a_stored_model(self, version): + data = _model_dict(measures=[], source_queries=[_query_dict(version=version)]) + data.pop("sql_table", None) + data.pop("columns", None) + [q] = SlayerModel.model_validate(data).source_queries or [] + _assert_query_filled(q) + + def test_inline_source_model_of_a_stored_query(self): + inline = _model_dict(measures=[{"name": "x", "formula": BARE}], version=None) + stage = {**_query_dict(version=None), "source_model": inline} + data = _model_dict(measures=[], source_queries=[stage]) + data.pop("sql_table", None) + data.pop("columns", None) + model = SlayerModel.model_validate(data) + [q] = model.source_queries or [] + assert isinstance(q.source_model, SlayerModel) + assert q.source_model.measures[0].formula == FILLED + + def test_inline_extension_measures_of_a_stored_query(self): + ext = {"source_name": "sales", "measures": [{"name": "x", "formula": BARE}]} + q = SlayerQuery.model_validate({**_query_dict(version=4), "source_model": ext}) + assert getattr(q.source_model, "measures")[0].formula == FILLED + + def test_memory_query(self): + mem = Memory.model_validate({"version": 2, "id": "m1", "learning": "x", "query": _query_dict(version=4)}) + assert mem.query is not None + _assert_query_filled(mem.query) + + +# --------------------------------------------------------------------------- # +# Fresh and current payloads are never filled; an explicit old version is legacy. +# --------------------------------------------------------------------------- # +class TestPayloadVersions: + @pytest.mark.parametrize("version", [None, "current"]) + def test_fresh_or_current_query_untouched(self, version): + v = mig.CURRENT_VERSIONS["SlayerQuery"] if version == "current" else None + q = SlayerQuery.model_validate({**_query_dict(version=v)}) + assert (q.measures or [])[0].formula == "rank(sum(amount)) + 1" + assert q.filters == _query_dict()["filters"] + + @pytest.mark.parametrize("version", [None, "current"]) + def test_fresh_or_current_model_untouched(self, version): + v = mig.CURRENT_VERSIONS["SlayerModel"] if version == "current" else None + model = SlayerModel.model_validate(_model_dict(measures=[{"name": "x", "formula": BARE}], version=v)) + assert model.measures[0].formula == BARE + + def test_fresh_memory_untouched(self): + mem = Memory.model_validate({"id": "m1", "learning": "x", "query": _query_dict(version=None)}) + assert mem.query is not None + assert (mem.query.measures or [])[0].formula == "rank(sum(amount)) + 1" + + @pytest.mark.parametrize("version", [3, 4]) + def test_explicit_old_query_is_filled(self, version): + q = SlayerQuery.model_validate({"version": version, "source_model": "sales", "measures": [BARE]}) + assert (q.measures or [])[0].formula == FILLED + + +# --------------------------------------------------------------------------- # +# Storage load paths (YAML and SQLite), executed against seeded sales data. +# --------------------------------------------------------------------------- # +Seeder = Callable[..., Awaitable[StorageBackend]] + + +@pytest.fixture(params=["yaml", "sqlite"]) +async def seed(request) -> AsyncIterator[Seeder]: + with tempfile.TemporaryDirectory() as tmp: + db = os.path.join(tmp, "sales.db") + _seed_sqlite(db) + ds = DatasourceConfig(name="test", type="sqlite", database=db) + store_path = os.path.join(tmp, "store.db") + + async def _seed(models: list[dict] | None = None, + memories: list[dict] | None = None) -> StorageBackend: + models, memories = models or [], memories or [] + if request.param == "yaml": + storage: StorageBackend = YAMLStorage(base_dir=tmp) + await storage.save_datasource(ds) + for m in models: + path = os.path.join(tmp, "models", "test", f"{m['name']}.yaml") + os.makedirs(os.path.dirname(path), exist_ok=True) + with open(path, "w") as f: # NOSONAR(S7493) — test seed + yaml.dump(m, f, sort_keys=False) + for mem in memories: + fm = {k: v for k, v in mem.items() if k not in ("id", "learning")} + path = storage._memory_md_path(mem["id"]) # type: ignore[attr-defined] + os.makedirs(os.path.dirname(path), exist_ok=True) + with open(path, "w", encoding="utf-8") as f: # NOSONAR(S7493) — test seed + f.write(f"---\n{yaml.safe_dump(fm)}---\n{mem['learning']}") + return storage + storage = SQLiteStorage(db_path=store_path) + await storage.save_datasource(ds) + with transaction(store_path) as conn: + for m in models: + conn.execute("INSERT INTO models (data_source, name, data) VALUES (?, ?, ?)", + ("test", m["name"], json.dumps(m))) + for mem in memories: + conn.execute("INSERT INTO memories (id, data) VALUES (?, ?)", + (mem["id"], json.dumps(mem))) + return storage + + try: + yield _seed + finally: + engine_factory.invalidate_engine(ds) + + +async def _raw(storage: StorageBackend, name: str) -> dict: + raw = await storage._load_raw_model_dict(name=name, data_source="test") + assert raw is not None + return raw + + +async def _execute(storage: StorageBackend, query: SlayerQuery): + engine = SlayerQueryEngine(storage=storage) + try: + return await engine.execute(query) + finally: + engine.close() + + +class TestStorageLoad: + async def test_stored_model_measure_keeps_descending_meaning(self, seed): + storage = await seed(models=[_model_dict(measures=[{"name": "r", "formula": BARE}])]) + model = await storage.get_model("sales", data_source="test") + assert model is not None + assert model.measures[0].formula == FILLED + raw = await _raw(storage, "sales") + assert raw["version"] == mig.CURRENT_VERSIONS["SlayerModel"] + assert raw["measures"][0]["formula"] == FILLED + resp = await _execute(storage, SlayerQuery.model_validate( + {"source_model": "sales", "dimensions": ["region"], "measures": ["r"]})) + assert {k[0]: v["sales.r"] for k, v in rows_by(resp, "sales.region").items()} == RANK_DESC + + async def test_unversioned_model_with_unversioned_nested_query(self, seed): + qb = {"name": "qb", "data_source": "test", + "source_queries": [{"source_model": "sales", "dimensions": ["region"], + "measures": [{"formula": BARE, "name": "r"}]}]} + storage = await seed(models=[_model_dict(measures=[]), qb]) + model = await storage.get_model("qb", data_source="test") + assert model is not None + [q] = model.source_queries or [] + assert (q.measures or [])[0].formula == FILLED + + async def test_unversioned_memory_and_query(self, seed): + storage = await seed(memories=[{ + "id": "m1", "learning": "top regions", + "query": {"source_model": "sales", "measures": [BARE], "filters": [f"{BARE} <= 2"]}, + }]) + mem = await storage.get_memory("m1") + assert mem.query is not None + assert (mem.query.measures or [])[0].formula == FILLED + assert mem.query.filters == [f"{FILLED} <= 2"] + + async def test_versioned_legacy_memory(self, seed): + storage = await seed(memories=[{ + "version": 2, "id": "m2", "learning": "top regions", + "query": {"version": 4, "source_model": "sales", "measures": [BARE]}, + }]) + mem = await storage.get_memory("m2") + assert mem.query is not None + assert (mem.query.measures or [])[0].formula == FILLED + + async def test_untouched_calls_stay_byte_identical(self, seed): + formulas = ["rank(sum(amount), direction='asc')", "ntile(sum(amount), n=4)", + "percent_rank(sum(amount))"] + data = _model_dict(measures=[{"name": f"m{i}", "formula": f} for i, f in enumerate(formulas)]) + data["columns"].append({"name": "rn", "type": "INT", "sql": "dense_rank() over (order by id)"}) + storage = await seed(models=[data]) + model = await storage.get_model("sales", data_source="test") + assert model is not None + assert [m.formula for m in model.measures] == formulas + assert next(c for c in model.columns if c.name == "rn").sql == "dense_rank() over (order by id)" + + async def test_untokenisable_measure_still_loads(self, seed): + storage = await seed(models=[_model_dict(measures=[{"name": "x", "formula": "rank(sum(amount)"}])]) + model = await storage.get_model("sales", data_source="test") + assert model is not None + assert model.measures[0].formula == "rank(sum(amount)" + + @pytest.mark.parametrize("version", [None, "current"]) + async def test_fresh_or_current_query_with_bare_rank_fails(self, seed, version): + storage = await seed(models=[_model_dict(measures=[])]) + payload: dict = {"source_model": "sales", "dimensions": ["region"], "measures": [BARE]} + if version == "current": + payload["version"] = mig.CURRENT_VERSIONS["SlayerQuery"] + with pytest.raises(core_errors.TransformArgumentError) as ei: + await _execute(storage, SlayerQuery.model_validate(payload)) + assert "direction='asc'" in str(ei.value) and "direction='desc'" in str(ei.value) + + async def test_explicit_old_query_executes_descending(self, seed): + storage = await seed(models=[_model_dict(measures=[])]) + resp = await _execute(storage, SlayerQuery.model_validate( + {"version": 4, "source_model": "sales", "dimensions": ["region"], + "measures": [{"formula": BARE, "name": "r"}]})) + assert {k[0]: v["sales.r"] for k, v in rows_by(resp, "sales.region").items()} == RANK_DESC + + +# --------------------------------------------------------------------------- # +# Fresh payloads through the REST API and MCP. +# --------------------------------------------------------------------------- # +@pytest.fixture +def served_storage(): + with tempfile.TemporaryDirectory() as tmp: + db = os.path.join(tmp, "sales.db") + _seed_sqlite(db) + ds = DatasourceConfig(name="test", type="sqlite", database=db) + storage = YAMLStorage(base_dir=os.path.join(tmp, "store")) + run_sync(storage.save_datasource(ds)) + run_sync(storage.save_model(sales_model())) + try: + yield storage + finally: + engine_factory.invalidate_engine(ds) + + +def test_rest_fresh_payload_fails(served_storage): + client = TestClient(create_app(storage=served_storage)) + resp = client.post("/query", json={"source_model": "sales", "dimensions": ["region"], + "measures": [{"formula": BARE}]}) + assert 400 <= resp.status_code < 500 + assert "direction='desc'" in resp.text and "direction='asc'" in resp.text + + +async def test_mcp_fresh_payload_fails(served_storage): + server = create_mcp_server(storage=served_storage) + try: + text = str(await server.call_tool(name="query", arguments={"query": { + "source_model": "sales", "dimensions": ["region"], "measures": [BARE]}})) + except Exception as exc: # noqa: BLE001 — a ToolError carries the message too + text = str(exc) + assert "direction='desc'" in text and "direction='asc'" in text diff --git a/tests/test_reagg_outer_grain_attribution.py b/tests/test_reagg_outer_grain_attribution.py index 7c8767e3..1a619db0 100644 --- a/tests/test_reagg_outer_grain_attribution.py +++ b/tests/test_reagg_outer_grain_attribution.py @@ -276,7 +276,7 @@ async def test_computed_dimension(self, engine): _clean(resp) async def test_transform_input(self, engine): - resp = await engine.execute(_by_account(m(f"rank({SUM_MAX_Q_BY_OUTER_ACCOUNT})"))) + resp = await engine.execute(_by_account(m(f"rank({SUM_MAX_Q_BY_OUTER_ACCOUNT}, direction='desc')"))) got = cells(resp, keys=BY_NAME_ACCOUNT) assert {k: int(x) for k, x in got.items() if k[0] != "Cy"} == RANK_OF_ACCOUNT_MAX _clean(resp) diff --git a/tests/test_schema_drift_typed.py b/tests/test_schema_drift_typed.py index c9eb1236..584f72ea 100644 --- a/tests/test_schema_drift_typed.py +++ b/tests/test_schema_drift_typed.py @@ -147,7 +147,7 @@ def test_transform_list_partition_by_parses_and_skips_kwargs(self) -> None: # only the transform input, so partition columns never surface. nodes = list( walk_parsed_refs( - parse_expr("rank(amount:sum, partition_by=[region, channel])") + parse_expr("rank(amount:sum, partition_by=[region, channel], direction='desc')") ) ) assert nodes == [AggCall(source=Ref(name="amount"), agg="sum")] @@ -223,7 +223,7 @@ def test_transform_list_partition_by_inner_only(self) -> None: # must accept it. Only the inner value's refs surface (legacy never # extracted the partition columns either). assert _measure_formula_refs( - "rank(revenue:sum, partition_by=[status, customer_id])" + "rank(revenue:sum, partition_by=[status, customer_id], direction='desc')" ) == {"revenue"} def test_func_style_simple_agg_rewritten(self) -> None: diff --git a/tests/test_sql_generator.py b/tests/test_sql_generator.py index 30628600..31949dfc 100644 --- a/tests/test_sql_generator.py +++ b/tests/test_sql_generator.py @@ -33,6 +33,7 @@ _join_aliases, _norm, ) +from tests._rank_direction_fixtures import rank_windows def _outer_order_terms(sql: str, dialect: str = "postgres") -> list[tuple[str, str]]: @@ -1324,7 +1325,7 @@ async def test_rank(self, generator: SQLGenerator, orders_model: SlayerModel) -> query = SlayerQuery( source_model="orders", dimensions=[ColumnRef(name="status")], - measures=[ModelMeasure(formula="revenue:sum"), ModelMeasure(formula="rank(revenue:sum)", name="rev_rank")], + measures=[ModelMeasure(formula="revenue:sum"), ModelMeasure(formula="rank(revenue:sum, direction='desc')", name="rev_rank")], ) sql = await _generate(generator, query, orders_model) assert "RANK()" in sql @@ -1849,6 +1850,14 @@ async def test_field_mixed_with_measures(self, generator: SQLGenerator, orders_m assert "aov" in sql.lower() +def _assert_rank_window(sql: str, *, fn: str, partition: list[str], descending: bool) -> None: + """The one rank-family window orders ``orders.revenue_sum`` and isolates NULL inputs.""" + [window] = rank_windows(sql, dialect="postgres") + assert (window.fn, window.order_sql, window.partition_sql, window.descending) == ( + fn, '"orders.revenue_sum"', partition, descending), sql + assert window.null_flag and window.null_guarded, sql + + class TestRankFamilyTransforms: """rank, percent_rank, dense_rank, ntile — first-class window-function transforms.""" @@ -1857,13 +1866,10 @@ async def test_rank_no_partition_unchanged(self, generator: SQLGenerator, orders query = SlayerQuery( source_model="orders", dimensions=[ColumnRef(name="status")], - measures=[ModelMeasure(formula="revenue:sum"), ModelMeasure(formula="rank(revenue:sum)", name="rev_rank")], + measures=[ModelMeasure(formula="revenue:sum"), ModelMeasure(formula="rank(revenue:sum, direction='desc')", name="rev_rank")], ) sql = await _generate(generator, query, orders_model) - assert ( - 'RANK() OVER (ORDER BY "orders.revenue_sum" DESC NULLS LAST)' - in _norm(sql) - ) + _assert_rank_window(sql, fn='RANK', partition=[], descending=True) async def test_rank_with_partition_by_single(self, generator: SQLGenerator, orders_model: SlayerModel) -> None: query = SlayerQuery( @@ -1871,14 +1877,11 @@ async def test_rank_with_partition_by_single(self, generator: SQLGenerator, orde dimensions=[ColumnRef(name="status"), ColumnRef(name="customer_id")], measures=[ ModelMeasure(formula="revenue:sum"), - ModelMeasure(formula="rank(revenue:sum, partition_by=status)", name="rev_rank"), + ModelMeasure(formula="rank(revenue:sum, partition_by=status, direction='desc')", name="rev_rank"), ], ) sql = await _generate(generator, query, orders_model) - assert ( - 'RANK() OVER (PARTITION BY "orders.status" ORDER BY "orders.revenue_sum" DESC NULLS LAST)' - in _norm(sql) - ) + _assert_rank_window(sql, fn='RANK', partition=['"orders.status"'], descending=True) async def test_rank_with_partition_by_list(self, generator: SQLGenerator, orders_model: SlayerModel) -> None: query = SlayerQuery( @@ -1887,28 +1890,14 @@ async def test_rank_with_partition_by_list(self, generator: SQLGenerator, orders measures=[ ModelMeasure(formula="revenue:sum"), ModelMeasure( - formula="rank(revenue:sum, partition_by=[status, customer_id])", + formula="rank(revenue:sum, partition_by=[status, customer_id], direction='desc')", name="rev_rank", ), ], ) sql = await _generate(generator, query, orders_model) - # PARTITION BY key order is irrelevant (planner sorts keys); compared as a re-emitted Window node, not raw text, since the pretty-printer line-breaks a long OVER. Pins the window's shape. - window = next( - ( - w - for w in sqlglot.parse_one(sql, read="postgres").find_all( - sqlglot.exp.Window, - ) - if isinstance(w.this, sqlglot.exp.Rank) - ), - None, - ) - assert window is not None, sql - assert window.sql(dialect="postgres") == ( - 'RANK() OVER (PARTITION BY "orders.customer_id", "orders.status" ' - 'ORDER BY "orders.revenue_sum" DESC NULLS LAST)' - ) + # PARTITION BY key order is irrelevant (planner sorts keys). + _assert_rank_window(sql, fn='RANK', partition=['"orders.customer_id"', '"orders.status"'], descending=True) async def test_percent_rank_default(self, generator: SQLGenerator, orders_model: SlayerModel) -> None: query = SlayerQuery( @@ -1920,10 +1909,7 @@ async def test_percent_rank_default(self, generator: SQLGenerator, orders_model: ], ) sql = await _generate(generator, query, orders_model) - assert ( - 'PERCENT_RANK() OVER (ORDER BY "orders.revenue_sum" DESC NULLS LAST)' - in _norm(sql) - ) + _assert_rank_window(sql, fn='PERCENTRANK', partition=[], descending=False) async def test_percent_rank_with_partition(self, generator: SQLGenerator, orders_model: SlayerModel) -> None: query = SlayerQuery( @@ -1937,11 +1923,7 @@ async def test_percent_rank_with_partition(self, generator: SQLGenerator, orders ], ) sql = await _generate(generator, query, orders_model) - assert ( - 'PERCENT_RANK() OVER (PARTITION BY "orders.status" ' - 'ORDER BY "orders.revenue_sum" DESC NULLS LAST)' - in _norm(sql) - ) + _assert_rank_window(sql, fn='PERCENTRANK', partition=['"orders.status"'], descending=False) async def test_dense_rank_default(self, generator: SQLGenerator, orders_model: SlayerModel) -> None: query = SlayerQuery( @@ -1949,14 +1931,11 @@ async def test_dense_rank_default(self, generator: SQLGenerator, orders_model: S dimensions=[ColumnRef(name="status")], measures=[ ModelMeasure(formula="revenue:sum"), - ModelMeasure(formula="dense_rank(revenue:sum)", name="rev_dr"), + ModelMeasure(formula="dense_rank(revenue:sum, direction='desc')", name="rev_dr"), ], ) sql = await _generate(generator, query, orders_model) - assert ( - 'DENSE_RANK() OVER (ORDER BY "orders.revenue_sum" DESC NULLS LAST)' - in _norm(sql) - ) + _assert_rank_window(sql, fn='DENSERANK', partition=[], descending=True) async def test_dense_rank_with_partition(self, generator: SQLGenerator, orders_model: SlayerModel) -> None: query = SlayerQuery( @@ -1965,16 +1944,12 @@ async def test_dense_rank_with_partition(self, generator: SQLGenerator, orders_m measures=[ ModelMeasure(formula="revenue:sum"), ModelMeasure( - formula="dense_rank(revenue:sum, partition_by=status)", name="rev_dr" + formula="dense_rank(revenue:sum, partition_by=status, direction='desc')", name="rev_dr" ), ], ) sql = await _generate(generator, query, orders_model) - assert ( - 'DENSE_RANK() OVER (PARTITION BY "orders.status" ' - 'ORDER BY "orders.revenue_sum" DESC NULLS LAST)' - in _norm(sql) - ) + _assert_rank_window(sql, fn='DENSERANK', partition=['"orders.status"'], descending=True) async def test_ntile_n_4(self, generator: SQLGenerator, orders_model: SlayerModel) -> None: query = SlayerQuery( @@ -1986,10 +1961,7 @@ async def test_ntile_n_4(self, generator: SQLGenerator, orders_model: SlayerMode ], ) sql = await _generate(generator, query, orders_model) - assert ( - 'NTILE(4) OVER (ORDER BY "orders.revenue_sum" DESC NULLS LAST)' - in _norm(sql) - ) + _assert_rank_window(sql, fn='NTILE', partition=[], descending=False) async def test_ntile_with_partition(self, generator: SQLGenerator, orders_model: SlayerModel) -> None: query = SlayerQuery( @@ -2004,21 +1976,17 @@ async def test_ntile_with_partition(self, generator: SQLGenerator, orders_model: ], ) sql = await _generate(generator, query, orders_model) - assert ( - 'NTILE(4) OVER (PARTITION BY "orders.status" ' - 'ORDER BY "orders.revenue_sum" DESC NULLS LAST)' - in _norm(sql) - ) + _assert_rank_window(sql, fn='NTILE', partition=['"orders.status"'], descending=False) async def test_dense_rank_in_filter_top_5_distinct( self, generator: SQLGenerator, orders_model: SlayerModel ) -> None: - """``dense_rank(...) <= 5`` is auto-extracted as a hidden field and post-filtered.""" + """``dense_rank(..., direction='desc') <= 5`` is auto-extracted as a hidden field and post-filtered.""" query = SlayerQuery( source_model="orders", dimensions=[ColumnRef(name="customer_id")], measures=[ModelMeasure(formula="revenue:sum")], - filters=["dense_rank(revenue:sum) <= 5"], + filters=["dense_rank(revenue:sum, direction='desc') <= 5"], ) sql = await _generate(generator, query, orders_model) assert "<= 5" in post_filter_where(sql) @@ -2060,21 +2028,18 @@ async def test_ntile_with_n_kwarg_in_filter( async def test_rank_with_partition_by_kwarg_in_filter( self, generator: SQLGenerator, orders_model: SlayerModel ) -> None: - """DEV-1492: ``rank(, partition_by=) <= 1`` end-to-end.""" + """DEV-1492: ``rank(, partition_by=, direction='desc') <= 1`` end-to-end.""" query = SlayerQuery( source_model="orders", dimensions=[ColumnRef(name="status"), ColumnRef(name="customer_id")], measures=[ModelMeasure(formula="revenue:sum")], - filters=["rank(revenue:sum, partition_by=status) <= 1"], + filters=["rank(revenue:sum, partition_by=status, direction='desc') <= 1"], ) sql = await _generate(generator, query, orders_model) post_filter_where(sql) inner_sql, outer_sql = split_chain(sql) - assert ( - 'RANK() OVER (PARTITION BY "orders.status" ' - 'ORDER BY "orders.revenue_sum" DESC NULLS LAST)' - in _norm(inner_sql) - ), f"PARTITION BY status should appear in the inner SELECT, got:\n{sql}" + assert "RANK()" in inner_sql, f"RANK should be materialised in the inner SELECT, got:\n{sql}" + _assert_rank_window(sql, fn='RANK', partition=['"orders.status"'], descending=True) assert "RANK()" not in outer_sql, ( f"RANK should not appear in the outer wrapper, got:\n{sql}" ) @@ -2096,16 +2061,12 @@ async def test_rank_partition_by_time_dimension( measures=[ ModelMeasure(formula="revenue:sum"), ModelMeasure( - formula="rank(revenue:sum, partition_by=created_at)", name="rev_rank" + formula="rank(revenue:sum, partition_by=created_at, direction='desc')", name="rev_rank" ), ], ) sql = await _generate(generator, query, orders_model) - assert ( - 'RANK() OVER (PARTITION BY "orders.created_at" ' - 'ORDER BY "orders.revenue_sum" DESC NULLS LAST)' - in _norm(sql) - ) + _assert_rank_window(sql, fn='RANK', partition=['"orders.created_at"'], descending=True) async def test_partition_by_must_be_a_query_dimension( self, generator: SQLGenerator, orders_model: SlayerModel @@ -2117,7 +2078,7 @@ async def test_partition_by_must_be_a_query_dimension( measures=[ ModelMeasure(formula="revenue:sum"), ModelMeasure( - formula="rank(revenue:sum, partition_by=customer_id)", name="rev_rank" + formula="rank(revenue:sum, partition_by=customer_id, direction='desc')", name="rev_rank" ), ], ) @@ -9839,7 +9800,7 @@ async def test_rewrite_mangles_the_final_public_projection( measures=[ModelMeasure(formula="*:count")], dimensions=[ColumnRef(name="status")], # Filter using a windowed transform creates a hidden hoisted column, so the public projection is a real narrowing. - filters=["dense_rank(revenue:sum) <= 5"], + filters=["dense_rank(revenue:sum, direction='desc') <= 5"], ) async with _persist_and_engine(orders_model, ds_type="bigquery") as engine: resp = await engine.execute(query=query, dry_run=True) diff --git a/tests/test_sql_predicate.py b/tests/test_sql_predicate.py index f2b76696..20c8a241 100644 --- a/tests/test_sql_predicate.py +++ b/tests/test_sql_predicate.py @@ -100,7 +100,7 @@ def test_slayer_transform_call_rejected(self) -> None: def test_rank_transform_rejected(self) -> None: with pytest.raises(ValueError, match="transform call"): - parse_sql_predicate("rank(revenue) <= 10") + parse_sql_predicate("rank(revenue, direction='desc') <= 10") def test_raw_over_window_rejected(self) -> None: with pytest.raises(ValueError, match="window function"): diff --git a/tests/test_syntax.py b/tests/test_syntax.py index e1c76a29..7501f5ae 100644 --- a/tests/test_syntax.py +++ b/tests/test_syntax.py @@ -155,7 +155,7 @@ def test_cumsum(self): assert result.kwargs == () def test_rank(self): - result = parse_expr("rank(revenue:sum)") + result = parse_expr("rank(revenue:sum, direction='desc')") assert isinstance(result, TransformCall) assert result.op == "rank" @@ -245,10 +245,11 @@ def test_transform_over_arithmetic_input(self): def test_rank_partition_by_dotted_path(self): # partition_by may name a joined column via a dotted path. - result = parse_expr("rank(revenue:sum, partition_by=customers.region)") + result = parse_expr("rank(revenue:sum, partition_by=customers.region, direction='desc')") assert isinstance(result, TransformCall) assert result.kwargs == ( ("partition_by", DottedRef(parts=("customers", "region"))), + ("direction", Literal(value="desc")), ) def test_triple_nested_transforms(self): @@ -637,7 +638,7 @@ def test_sql_equality_normalised(self): def test_transform_no_kwargs_in_filter(self): # The no-kwargs top-N form is unaffected by the operator rewrite. - result = parse_filter_expr("dense_rank(revenue:sum) <= 5") + result = parse_filter_expr("dense_rank(revenue:sum, direction='desc') <= 5") assert isinstance(result, Cmp) assert isinstance(result.left, TransformCall) assert result.left.op == "dense_rank" @@ -706,26 +707,29 @@ def test_rank_partition_by_kwarg_preserved_in_filter(self): # DEV-1492: rank(revenue:sum, partition_by=region) <= 1 — kwarg # survives the operator rewrite; binder turns partition_by into a # column ref (covered by SQL-gen tests). - result = parse_filter_expr("rank(revenue:sum, partition_by=region) <= 1") + result = parse_filter_expr("rank(revenue:sum, partition_by=region, direction='desc') <= 1") assert isinstance(result, Cmp) assert result.op == "<=" assert isinstance(result.left, TransformCall) assert result.left.op == "rank" assert result.left.args == () - assert result.left.kwargs == (("partition_by", Ref(name="region")),) + assert result.left.kwargs == ( + ("partition_by", Ref(name="region")), ("direction", Literal(value="desc")), + ) assert result.right == Literal(value=Decimal(1)) def test_rank_partition_by_list_kwarg_preserved_in_filter(self): # DEV-1492: list-form partition_by kwarg survives. _convert_kwarg_value # converts the list to a tuple of Refs. result = parse_filter_expr( - "rank(revenue:sum, partition_by=[region, channel]) <= 1" + "rank(revenue:sum, partition_by=[region, channel], direction='desc') <= 1" ) assert isinstance(result, Cmp) assert isinstance(result.left, TransformCall) assert result.left.op == "rank" assert result.left.kwargs == ( ("partition_by", (Ref(name="region"), Ref(name="channel"))), + ("direction", Literal(value="desc")), ) # -- Aggregation args/kwargs INSIDE a filter transform ------------------ @@ -906,14 +910,16 @@ def test_nested_scalar_in_transform_with_kwarg(self): # 0)` has a `=` which must be rewritten (SCALAR_FUNCTIONS # narrowing). Exercises the per-frame (kind, callee) stack. result = parse_filter_expr( - "rank(coalesce(status = 'paid', 0), partition_by=region) <= 1" + "rank(coalesce(status = 'paid', 0), partition_by=region, direction='desc') <= 1" ) assert isinstance(result, Cmp) assert result.op == "<=" assert isinstance(result.left, TransformCall) assert result.left.op == "rank" # Transform kwarg preserved. - assert result.left.kwargs == (("partition_by", Ref(name="region")),) + assert result.left.kwargs == ( + ("partition_by", Ref(name="region")), ("direction", Literal(value="desc")), + ) # Transform input is the inner coalesce ScalarCall with a Cmp arg. inner = result.left.input assert isinstance(inner, ScalarCall) diff --git a/tests/test_transforms_planner.py b/tests/test_transforms_planner.py index 342b1e13..c8d68e5d 100644 --- a/tests/test_transforms_planner.py +++ b/tests/test_transforms_planner.py @@ -178,7 +178,7 @@ def test_lead_with_explicit_periods(self) -> None: def test_unknown_kwarg_on_rank_raises(self) -> None: # rank's allowed kwargs: {partition_by}. Anything else → error. - parsed = parse_expr("rank(amount:sum, foo='bar')") + parsed = parse_expr("rank(amount:sum, foo='bar', direction='desc')") scope, bundle = _scope(), _bundle() with pytest.raises(ValueError, match="rank.*not.*accept"): bind_expr(parsed=parsed, scope=scope, bundle=bundle) @@ -190,7 +190,7 @@ def test_unknown_kwarg_on_percent_rank_raises(self) -> None: bind_expr(parsed=parsed, scope=scope, bundle=bundle) def test_unknown_kwarg_on_dense_rank_raises(self) -> None: - parsed = parse_expr("dense_rank(amount:sum, foo='bar')") + parsed = parse_expr("dense_rank(amount:sum, foo='bar', direction='desc')") scope, bundle = _scope(), _bundle() with pytest.raises(ValueError, match="dense_rank.*not.*accept"): bind_expr(parsed=parsed, scope=scope, bundle=bundle) @@ -210,7 +210,7 @@ def test_unknown_kwarg_on_consecutive_periods_raises(self) -> None: def test_rank_rejects_extra_positional_arg(self) -> None: # rank-family transforms are keyword-only after the value; an extra # positional must fail fast rather than being silently dropped. - parsed = parse_expr("rank(amount:sum, 2)") + parsed = parse_expr("rank(amount:sum, 2, direction='desc')") scope, bundle = _scope(), _bundle() with pytest.raises(ValueError, match="exactly one positional"): bind_expr(parsed=parsed, scope=scope, bundle=bundle) @@ -224,7 +224,7 @@ def test_ntile_rejects_positional_n(self) -> None: def test_rank_rejects_n_kwarg(self) -> None: # ``n`` is ntile-only; rank must not silently accept it. - parsed = parse_expr("rank(amount:sum, n=4)") + parsed = parse_expr("rank(amount:sum, n=4, direction='desc')") scope, bundle = _scope(), _bundle() with pytest.raises(ValueError, match="rank.*not.*accept.*'n'"): bind_expr(parsed=parsed, scope=scope, bundle=bundle) @@ -286,7 +286,7 @@ def test_time_shift_unused_positional_name_as_keyword_binds(self) -> None: def test_rank_with_partition_by_binds(self) -> None: bound = bind_expr( - parsed=parse_expr("rank(amount:sum, partition_by=region)"), + parsed=parse_expr("rank(amount:sum, partition_by=region, direction='desc')"), scope=_scope(), bundle=_bundle(), ) assert isinstance(bound.value_key, TransformKey) @@ -399,7 +399,7 @@ def test_cumsum_emits_transform_layer(self) -> None: def test_rank_emits_transform_layer(self) -> None: q = SlayerQuery( source_model="orders", - measures=[{"formula": "rank(amount:sum)"}], + measures=[{"formula": "rank(amount:sum, direction='desc')"}], ) planned = plan_query(query=q, bundle=_bundle()) assert any( @@ -470,7 +470,7 @@ def test_each_transform_slot_emits_its_own_layer(self) -> None: q = SlayerQuery( source_model="orders", measures=[ - {"formula": "rank(amount:sum)"}, + {"formula": "rank(amount:sum, direction='desc')"}, {"formula": "cumsum(amount:sum)"}, ], time_dimensions=[ From e1427347c853995a8027f1df02019b8f1eba7069 Mon Sep 17 00:00:00 2001 From: Egor Kraev Date: Sat, 3 Oct 2026 16:02:57 +0200 Subject: [PATCH 03/11] Rank-family ordering: required direction on rank/dense_rank, ascending ntile/percent_rank, NULL inputs rank NULL, lazy stored-only migration --- .../tasks.md | 24 +-- slayer/cli.py | 2 +- slayer/core/direction.py | 55 +++++++ slayer/core/errors.py | 6 +- slayer/core/formula.py | 18 ++- slayer/core/query.py | 27 ++-- slayer/engine/binding.py | 36 +++-- slayer/engine/syntax.py | 16 +- slayer/sql/generator.py | 41 +++-- slayer/storage/base.py | 3 +- slayer/storage/migrations.py | 35 ++++- slayer/storage/rank_direction_migration.py | 145 ++++++++++++++++++ slayer/storage/sqlite_storage.py | 7 +- slayer/storage/v2_migration.py | 3 +- slayer/storage/v3_migration.py | 4 +- slayer/storage/yaml_storage.py | 7 +- tests/golden/dev1824_sql_baseline.json | 20 +-- tests/golden/dev1832_sql_baseline.json | 28 ++-- tests/golden/dev1839_sql_baseline.json | 90 +++++------ tests/golden/dev1859_sql_baseline.json | 28 ++-- tests/golden/rank_direction_sql_baseline.json | 42 +++++ tests/test_dev1934_save_time_formula.py | 2 +- tests/test_memories_storage.py | 2 +- tests/test_memory_string_ids.py | 6 +- tests/test_rank_direction_migration.py | 7 + tests/test_sqlite_memories_pk_migration.py | 2 +- 26 files changed, 492 insertions(+), 164 deletions(-) create mode 100644 slayer/core/direction.py create mode 100644 slayer/storage/rank_direction_migration.py create mode 100644 tests/golden/rank_direction_sql_baseline.json diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/tasks.md b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/tasks.md index 4fc8fa50..d7539960 100644 --- a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/tasks.md +++ b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/tasks.md @@ -14,7 +14,7 @@ old `810 / 43` and gives `810 / 33` under NULL→NULL. - [x] 1.3 NULL-semantics tests: every scenario of "Rank-family NULL inputs rank NULL", covering all four functions with mixed-NULL, all-NULL-partition and partitioned inputs on SQLite and DuckDB, plus T-SQL / postgres emission. Verify: they fail now. -- [ ] 1.4 Golden emission tests for the rank family on postgres, sqlite, duckdb, tsql and +- [x] 1.4 Golden emission tests for the rank family on postgres, sqlite, duckdb, tsql and bigquery, recorded after implementation. Verify: the golden files exist and are pinned. - [x] 1.5 Naming tests: the "Rank direction spelled as its bare value" scenario. Verify: they fail now. @@ -45,40 +45,40 @@ old `810 / 43` and gives `810 / 33` under NULL→NULL. ## 2. Core rule and error -- [ ] 2.1 Add `TransformArgumentError(QueryTypeError)` to `slayer/core/errors.py`. +- [x] 2.1 Add `TransformArgumentError(QueryTypeError)` to `slayer/core/errors.py`. Verify: the error tests from 1.1 can import it. -- [ ] 2.2 Move the direction synonym table out of `core/query.py` into a core module that +- [x] 2.2 Move the direction synonym table out of `core/query.py` into a core module that `OrderItem` and the new rule both import, and add the core direction validator (required / forbidden / literal / normalise / raise). Verify: `OrderItem` tests still pass. -- [ ] 2.3 `core/formula.py`: route `_parse_transform_kwargs` through the validator, with +- [x] 2.3 `core/formula.py`: route `_parse_transform_kwargs` through the validator, with `direction` admitted for `rank` / `dense_rank`. Verify: 1.2 passes. ## 3. Binding, naming, emission -- [ ] 3.1 `engine/binding.py`: call the validator in `_bind_transform_params`, store +- [x] 3.1 `engine/binding.py`: call the validator in `_bind_transform_params`, store `("direction", ...)` in `TransformKey.kwargs`, and move the other transform-kwarg `ValueError`s onto `TransformArgumentError`. Verify: 1.1 binding/error scenarios pass. -- [ ] 3.2 Render `direction` as its bare value in the canonical formula text used for +- [x] 3.2 Render `direction` as its bare value in the canonical formula text used for derived keys. Verify: 1.5 passes. -- [ ] 3.3 `sql/generator.py`: emit `ORDER BY v ASC|DESC` per `direction` for `rank` / +- [x] 3.3 `sql/generator.py`: emit `ORDER BY v ASC|DESC` per `direction` for `rank` / `dense_rank` and `ASC` for `ntile` / `percent_rank`, all wrapped in the NULL→NULL shape (design decision 5) as sqlglot AST. Verify: 1.1 and 1.3 pass on SQLite and DuckDB, and the T-SQL emission test passes. -- [ ] 3.4 Record the golden baselines and re-bless any existing golden SQL that changed +- [x] 3.4 Record the golden baselines and re-bless any existing golden SQL that changed only by the CASE wrapper, `ASC` or the direction. Review each re-blessed diff. Verify: 1.4 and the golden suites pass. ## 4. Lazy migration -- [ ] 4.1 `storage/migrations.py`: add the `stored_only` registration flag and its gate in +- [x] 4.1 `storage/migrations.py`: add the `stored_only` registration flag and its gate in `migrate()`. Verify: the gate unit tests from 1.6 pass. -- [ ] 4.2 Add the token-level rewrite function in `storage` (stdlib `tokenize` only). +- [x] 4.2 Add the token-level rewrite function in `storage` (stdlib `tokenize` only). Verify: the rewrite edge-case tests pass. -- [ ] 4.3 Register the stored-only steps `SlayerModel` 12→13, `SlayerQuery` 4→5 and +- [x] 4.3 Register the stored-only steps `SlayerModel` 12→13, `SlayerQuery` 4→5 and `Memory` 2→3, including the nested-query stamping (design decision 2), and bump `CURRENT_VERSIONS`. Verify: the 1.6 model/query/memory scenarios pass. -- [ ] 4.4 Stamp `version: 1` on unversioned stored dicts in `_migrate_and_refine_on_load` +- [x] 4.4 Stamp `version: 1` on unversioned stored dicts in `_migrate_and_refine_on_load` and the YAML / SQLite memory load sites. Verify: the unversioned-legacy scenarios pass. ## 5. Agent-facing text, docs, examples diff --git a/slayer/cli.py b/slayer/cli.py index 1d619ff0..2ad04354 100644 --- a/slayer/cli.py +++ b/slayer/cli.py @@ -1279,7 +1279,7 @@ def _refine_one_model_for_cli( raw = run_sync(_load_raw_model_dict(inner, ds_name, model_name)) if raw is None: return False - upgraded = _mig.migrate("SlayerModel", copy.deepcopy(raw)) + upgraded = _mig.migrate("SlayerModel", _mig.stamp_stored(copy.deepcopy(raw))) # Snapshot column types AFTER migration but BEFORE refinement so the # before/after diff reports only actual refinement changes — migration- # only aliases like ``number → DOUBLE`` are not refinement events. diff --git a/slayer/core/direction.py b/slayer/core/direction.py new file mode 100644 index 00000000..65134760 --- /dev/null +++ b/slayer/core/direction.py @@ -0,0 +1,55 @@ +"""Ordering direction: the shared synonym table and the rank-family ``direction=`` rule.""" + +from typing import Any + +from slayer.core.errors import TransformArgumentError + +DIRECTION_NORMALIZE = { + "asc": "asc", + "ascending": "asc", + "desc": "desc", + "descending": "desc", +} + +#: Rank-family transforms whose ordering direction is a required ``direction=`` keyword. +DIRECTED_RANK_TRANSFORMS = frozenset({"rank", "dense_rank"}) +#: Rank-family transforms that always order ascending and take no ``direction=``. +ASCENDING_RANK_TRANSFORMS = frozenset({"ntile", "percent_rank"}) + +_ACCEPTED = "asc, desc, ascending or descending (any case)" + + +def normalize_direction(value: Any) -> str | None: + """``asc`` / ``desc`` for a recognised direction word, else ``None``.""" + if not isinstance(value, str): + return None + return DIRECTION_NORMALIZE.get(value.strip().lower()) + + +def rank_direction(*, op: str, given: bool, value: Any = None) -> str | None: + """Validate a rank-family call's ``direction=``; ``value`` is the literal, or any non-``str`` when not a string literal.""" + if op in ASCENDING_RANK_TRANSFORMS: + if given: + raise TransformArgumentError( + summary=f"Transform '{op}' always orders ascending and takes no direction= keyword.", + suggestion=f"drop direction=; {op} gives the lowest value the lowest result.", + ) + return None + if op not in DIRECTED_RANK_TRANSFORMS: + return None + if not given: + raise TransformArgumentError( + summary=f"Transform '{op}' needs an ordering direction.", + suggestion=( + f"pass direction='asc' (lowest first) or direction='desc' (highest first), " + f"e.g. {op}(sum(amount), direction='desc')." + ), + ) + normalized = normalize_direction(value) + if normalized is None: + got = repr(value) if isinstance(value, str) else "a value that is not a string literal" + raise TransformArgumentError( + summary=f"Transform '{op}' direction must be {_ACCEPTED}; got {got}.", + suggestion=f"write {op}(..., direction='asc') or {op}(..., direction='desc').", + ) + return normalized diff --git a/slayer/core/errors.py b/slayer/core/errors.py index 2a6af032..77e3a887 100644 --- a/slayer/core/errors.py +++ b/slayer/core/errors.py @@ -303,6 +303,10 @@ class TransformInputError(QueryTypeError): """A transform operand the transform cannot consume.""" +class TransformArgumentError(QueryTypeError): + """A missing, unknown or malformed transform argument.""" + + class ComputedDimensionError(QueryTypeError): """A malformed computed (expression) dimension.""" @@ -526,7 +530,7 @@ def __init__( self, filter_expr: str, source: str, - suggestion: str = "use a rank-family transform (e.g. `rank() <= N`).", + suggestion: str = "use a rank-family transform (e.g. `rank(, direction='desc') <= N`).", ) -> None: self.filter_expr = filter_expr self.source = source diff --git a/slayer/core/formula.py b/slayer/core/formula.py index 38025b81..3a2c8bd8 100644 --- a/slayer/core/formula.py +++ b/slayer/core/formula.py @@ -23,6 +23,7 @@ from pydantic import BaseModel, Field +from slayer.core.direction import rank_direction from slayer.core.enums import ( BUILTIN_AGGREGATIONS, RANK_FAMILY_TRANSFORMS, @@ -150,7 +151,8 @@ class TransformField(BaseModel): default_factory=dict, description=( "Keyword args from the call site, e.g. partition_by=[...] for the " - "rank family or n=4 for ntile. Validated per-transform at parse time." + "rank family, direction='desc' for rank / dense_rank or n=4 for ntile. " + "Validated per-transform at parse time." ), ) @@ -662,7 +664,7 @@ def _parse_node( if func_name in RANK_FAMILY_TRANSFORMS and len(node.args) > 1: raise ValueError( f"Transform '{func_name}' does not accept positional arguments " - f"beyond the measure; use keyword args (e.g. partition_by=, n=). " + f"beyond the measure; use keyword args (e.g. partition_by=, direction=, n=). " f"Formula: {original!r}" ) extra_args = [] @@ -876,12 +878,16 @@ def _parse_transform_kwargs( # NOSONAR S3776 — straight-line whitelist + per- """ allowed = _ALLOWED_TRANSFORM_KWARGS.get(transform, frozenset()) parsed: dict[str, Any] = {} + direction_kw: ast.keyword | None = None for kw in keywords: if kw.arg is None: raise ValueError( f"Transform '{transform}' does not accept **kwargs in formula {original!r}" ) + if kw.arg == "direction" and transform in RANK_FAMILY_TRANSFORMS: + direction_kw = kw + continue if kw.arg not in allowed: if not allowed: raise ValueError( @@ -919,6 +925,14 @@ def _parse_transform_kwargs( # NOSONAR S3776 — straight-line whitelist + per- else: # pragma: no cover — guarded by the whitelist check above parsed[kw.arg] = _parse_literal(node=kw.value, original=original) + literal = direction_kw.value if direction_kw is not None else None + direction = rank_direction( + op=transform, given=direction_kw is not None, + value=literal.value if isinstance(literal, ast.Constant) else None, + ) + if direction is not None: + parsed["direction"] = direction + if transform == "ntile" and "n" not in parsed: raise ValueError( f"Transform 'ntile' requires keyword argument 'n' (positive integer) " diff --git a/slayer/core/query.py b/slayer/core/query.py index 63af3e54..5134315e 100644 --- a/slayer/core/query.py +++ b/slayer/core/query.py @@ -21,6 +21,7 @@ model_validator, ) +from slayer.core.direction import normalize_direction from slayer.core.enums import BUILTIN_AGGREGATIONS, GRANULARITY_NAMES, TimeGranularity, normalize_aggregation_name from slayer.core.formula import ALL_TRANSFORMS from slayer.core.keys import SCALAR_FUNCTIONS @@ -41,6 +42,9 @@ logger = logging.getLogger(__name__) +#: The SlayerQuery version whose migration retired ``strict``. +_STRICT_RETIRED_AT = 4 + _NAME_PATTERN = re.compile(r"^[a-zA-Z_]\w*$", re.ASCII) _VAR_PATTERN = re.compile(r"\{\{|\}\}|\{([a-zA-Z_]\w*)\}|\{([^}]*)\}", re.ASCII) @@ -703,19 +707,9 @@ def _coerce_order_column(v: Any) -> Any: return v -# ORDER BY direction synonyms → canonical lowercase (the generator compares -# ``direction == "asc"``). Shared by the shorthand healer and the validator. -_DIRECTION_NORMALIZE = { - "asc": "asc", - "ascending": "asc", - "desc": "desc", - "descending": "desc", -} - - def _is_direction(value: Any) -> bool: """True if ``value`` is a recognized direction word (case/whitespace-insensitive).""" - return isinstance(value, str) and value.strip().lower() in _DIRECTION_NORMALIZE + return normalize_direction(value) is not None def _coerce_date_range(value: Any) -> Any: @@ -799,8 +793,9 @@ def _capture_raw_formula(cls, data: Any) -> Any: def _normalize_direction(cls, v: str) -> str: """Normalize direction to canonical ``asc``/``desc`` (else raise); the generator compares ``== "asc"`` strictly, so a non-normalized ``"ASC"`` would silently emit DESC.""" - if _is_direction(v): - return _DIRECTION_NORMALIZE[v.strip().lower()] + normalized = normalize_direction(v) + if normalized is not None: + return normalized raise ValueError( "order direction must be one of asc/desc/ascending/descending " f"(case-insensitive), got {v!r}" @@ -1037,8 +1032,8 @@ def _migrate_and_rewrite(cls, data: Any) -> Any: # Single before-validator: migrate, THEN rewrite the functional granularity # form. Pydantic runs before-validators in reverse declaration order, so # sequencing them explicitly here keeps the rewrite on migrated input. - # `strict` is retired. Reject it for fresh (no version), current-version, - # or malformed payloads; only a pre-current *integer* stored version + # `strict` is retired. Reject it for fresh (no version), v4-or-later, + # or malformed payloads; only a pre-v4 *integer* stored version # migrates it (v3→v4 maps strict:true→to_many_handling='error'). ``version`` # is raw here (pre-coercion), so accept only int / integer-string forms — # never truncate a float or other malformed value into a stale version. @@ -1052,7 +1047,7 @@ def _migrate_and_rewrite(cls, data: Any) -> Any: version = int(raw_version) except ValueError: version = None - if version is None or version >= CURRENT_VERSIONS["SlayerQuery"]: + if version is None or version >= _STRICT_RETIRED_AT: raise ValueError( "`strict` is retired; set to_many_handling='error' instead " "(one of broadcast|associate|error)." diff --git a/slayer/engine/binding.py b/slayer/engine/binding.py index 4ee1a3d9..692b9496 100644 --- a/slayer/engine/binding.py +++ b/slayer/engine/binding.py @@ -20,6 +20,7 @@ MeasureCycleError, MeasureRecursionLimitError, PartitionKeyError, + TransformArgumentError, UnknownFunctionError, UnknownReferenceError, UnresolvableDimensionJoinError, @@ -35,6 +36,7 @@ format_unknown_aggregation, normalize_aggregation_name, ) +from slayer.core.direction import rank_direction from slayer.core.enums import RANK_FAMILY_TRANSFORMS from slayer.core.granularity import CustomGranularity, Granularity, resolve_granularity from slayer.core.refs import EXPRESSION_SOURCE_KINDS, key_display @@ -1649,29 +1651,37 @@ def _bind_transform_params( partition_keys: Grain = Grain.EMPTY allowed_kwargs = _TRANSFORM_KWARG_RULES.get(op, frozenset()) seen_kwargs: set = set() - rank_partition_ok = op in RANK_FAMILY_TRANSFORMS + rank_family = op in RANK_FAMILY_TRANSFORMS + direction_value: object = _NOT_SCALAR for k, v in [*positional_pairs, *kwargs]: - if k == "partition_by" and rank_partition_ok: + if k == "partition_by" and rank_family: partition_keys = _bind_partition_keys( value=v, scope=scope, bundle=bundle, dim_alias_map=dim_alias_map, label=f"transform {op!r}", ) continue + if k == "direction" and rank_family: + seen_kwargs.add(k) + direction_value = _fold_to_scalar(v) + continue if k not in allowed_kwargs: - advertised = allowed_kwargs | ({"partition_by"} if rank_partition_ok else set()) - raise ValueError( - f"Transform {op!r} does not accept keyword " + advertised = allowed_kwargs | ({"partition_by"} if rank_family else set()) + raise TransformArgumentError( + summary=f"Transform {op!r} does not accept keyword " f"argument {k!r}. Accepted: {sorted(advertised)}." ) seen_kwargs.add(k) scalar = _fold_to_scalar(v) if scalar is _NOT_SCALAR: - raise ValueError( - f"Transform {op!r} keyword {k!r} must be a " + raise TransformArgumentError( + summary=f"Transform {op!r} keyword {k!r} must be a " f"scalar literal; got expression of kind " f"{type(v).__name__}." ) bound_kwargs.append((k, scalar)) + direction = rank_direction(op=op, given="direction" in seen_kwargs, value=direction_value) + if direction is not None: + bound_kwargs.append(("direction", direction)) bound_kwargs = _apply_transform_kwarg_defaults( op=op, kwargs=bound_kwargs, seen=seen_kwargs, ) @@ -1702,19 +1712,19 @@ def _apply_transform_kwarg_defaults( Integer checks accept integral ``Decimal`` (``normalize_scalar`` wraps numbers).""" if op == "ntile": if "n" not in seen: - raise ValueError( - "Transform 'ntile' requires keyword argument n (the " + raise TransformArgumentError( + summary="Transform 'ntile' requires keyword argument n (the " "number of buckets, a positive integer)." ) n_value = next(v for k, v in kwargs if k == "n") if not _is_positive_integer(n_value): - raise ValueError( - f"Transform {op!r} keyword n must be a positive " + raise TransformArgumentError( + summary=f"Transform {op!r} keyword n must be a positive " f"integer; got {n_value!r}." ) if op == "time_shift" and "periods" not in seen: - raise ValueError( - "Transform 'time_shift' requires keyword argument periods " + raise TransformArgumentError( + summary="Transform 'time_shift' requires keyword argument periods " "(the integer offset, negative for a backward shift)." ) if op in ("lag", "lead") and "periods" not in seen: diff --git a/slayer/engine/syntax.py b/slayer/engine/syntax.py index 82266e09..4b7bb232 100644 --- a/slayer/engine/syntax.py +++ b/slayer/engine/syntax.py @@ -18,6 +18,7 @@ from pydantic import BaseModel, ConfigDict +from slayer.core.direction import normalize_direction from slayer.core.enums import BUILTIN_AGGREGATIONS, GRANULARITY_NAMES, normalize_aggregation_name from slayer.core.errors import GranularityCallError, IllegalWindowInFilterError, UnknownFunctionError from slayer.core.formula import ALL_TRANSFORMS @@ -1347,13 +1348,22 @@ def _walk_parsed(node: Any) -> Iterator[Any]: def _canonical_call_params( - args: Tuple[Any, ...], kwargs: Tuple[Tuple[str, Any], ...], + args: Tuple[Any, ...], kwargs: Tuple[Tuple[str, Any], ...], *, bare_direction: bool = False, ) -> str: parts = [canonical_measure_text(a) for a in args] - parts += [f"{k}={_canonical_kwarg_text(v)}" for k, v in kwargs] + parts += [_canonical_kwarg(k, v, bare_direction=bare_direction) for k, v in kwargs] return f"({', '.join(parts)})" if parts else "" +def _canonical_kwarg(name: str, value: Any, *, bare_direction: bool) -> str: + """``name=value``; a transform's recognised ``direction`` renders as its bare normalised value.""" + if bare_direction and name == "direction" and isinstance(value, Literal): + direction = normalize_direction(value.value) + if direction is not None: + return direction + return f"{name}={_canonical_kwarg_text(value)}" + + def _canonical_kwarg_text(value: Any) -> str: if isinstance(value, tuple): return f"[{', '.join(canonical_measure_text(v) for v in value)}]" @@ -1391,7 +1401,7 @@ def canonical_measure_text(parsed: Any) -> str: # NOSONAR(S3776) — flat per-n return f"{parsed.agg}({', '.join(parts)})" if isinstance(parsed, TransformCall): inner = canonical_measure_text(parsed.input) - params = _canonical_call_params(parsed.args, parsed.kwargs) + params = _canonical_call_params(parsed.args, parsed.kwargs, bare_direction=True) return f"{parsed.op}({inner}{', ' + params[1:-1] if params else ''})" if isinstance(parsed, ScalarCall): return f"{parsed.name}({', '.join(canonical_measure_text(a) for a in parsed.args)})" diff --git a/slayer/sql/generator.py b/slayer/sql/generator.py index 3ca92b19..a5260e4f 100644 --- a/slayer/sql/generator.py +++ b/slayer/sql/generator.py @@ -652,6 +652,9 @@ def _is_host_grain(key) -> bool: "replace", "substr", "substring", "concat", }) +#: Argument-less rank-family window functions (``ntile`` carries its bucket count). +_RANK_WINDOW_FNS = {"rank": exp.Rank, "dense_rank": exp.DenseRank, "percent_rank": exp.PercentRank} + # Real ValueKey args (a ScalarCallKey / iif may also carry raw scalar literals). _COMPOUND_VALUE_KEYS = ( ColumnKey, ColumnSqlKey, TimeTruncKey, StarKey, @@ -4494,6 +4497,24 @@ def _joined_or_local_dim_expr( table=exp.to_identifier(current_alias), ) + def _rank_family_window( + self, *, fn: Expression, measure: Expression, partition_by: List[Expression], descending: bool, + ) -> Expression: + """``CASE WHEN v IS NULL THEN NULL ELSE fn OVER (PARTITION BY …, ORDER BY v) END``: NULL rows rank NULL, outside the others' window.""" + null_flag = exp.Case( + ifs=[exp.If(this=exp.Is(this=measure.copy(), expression=exp.Null()), true=exp.Literal.number(1))], + default=exp.Literal.number(0), + ) + window = exp.Window( + this=fn, + partition_by=[*(c.copy() for c in partition_by), null_flag], + order=exp.Order(expressions=[self._window_ordered(measure.copy(), descending=descending)]), + ) + return exp.Case( + ifs=[exp.If(this=exp.Is(this=measure.copy(), expression=exp.Null()), true=exp.Null())], + default=window, + ) + def _window_ordered(self, col: Expression, *, descending: bool = False) -> exp.Ordered: """One ``ORDER BY`` term INSIDE an ``OVER (…)`` clause.""" args: Dict[str, Any] = { @@ -4640,10 +4661,6 @@ def _over( if time_col is not None else None ) - # Rank has no frame; pin uniform NULLS LAST (not frame-safe native) for cross-dialect parity. - rank_order = exp.Order( - expressions=[self._dialect.build_ordered(measure.copy(), descending=True)], - ) unbounded_frame = exp.WindowSpec( kind="ROWS", start="UNBOUNDED", start_side="PRECEDING", @@ -4688,12 +4705,6 @@ def _normalise_periods(raw: Any, *, kw: str = "periods") -> int: exp.Lead(this=measure, offset=exp.Literal.number(n)), order=time_order, ) - if op == "rank": - return _over(exp.Rank(), order=rank_order) - if op == "percent_rank": - return _over(exp.PercentRank(), order=rank_order) - if op == "dense_rank": - return _over(exp.DenseRank(), order=rank_order) if op == "ntile": # Route through the shared normaliser (like lag/lead) so bool is rejected and a non-integral Decimal raises # rather than truncating; render-side defense. @@ -4702,8 +4713,14 @@ def _normalise_periods(raw: Any, *, kw: str = "periods") -> int: raise ValueError( f"ntile requires a positive integer n, got {n!r}", ) - return _over( - exp.Ntile(this=exp.Literal.number(n)), order=rank_order, + return self._rank_family_window( + fn=exp.Ntile(this=exp.Literal.number(n)), measure=measure, + partition_by=partition_by, descending=False, + ) + if op in _RANK_WINDOW_FNS: + return self._rank_family_window( + fn=_RANK_WINDOW_FNS[op](), measure=measure, partition_by=partition_by, + descending=kwarg_map.get("direction") == "desc", ) if op == "first": return _over( diff --git a/slayer/storage/base.py b/slayer/storage/base.py index c719bac1..8f2881f2 100644 --- a/slayer/storage/base.py +++ b/slayer/storage/base.py @@ -641,7 +641,8 @@ async def _migrate_and_refine_on_load( f"to recreate it." ) write_back = False - pre_version = int(data.get("version", 1)) + data = _mig.stamp_stored(data) + pre_version = int(data["version"]) if pre_version < _mig.CURRENT_VERSIONS["SlayerModel"]: data = _mig.migrate("SlayerModel", data) # Rewrite legacy ``__`` split-alias qualifiers to dotted on the RAW diff --git a/slayer/storage/migrations.py b/slayer/storage/migrations.py index 8830e2b0..8a3ab2c4 100644 --- a/slayer/storage/migrations.py +++ b/slayer/storage/migrations.py @@ -16,22 +16,27 @@ # Per-entity current version. Bump independently when an entity's schema changes. CURRENT_VERSIONS: dict[str, int] = { - "SlayerModel": 12, - "SlayerQuery": 4, + "SlayerModel": 13, + "SlayerQuery": 5, "DatasourceConfig": 2, - "Memory": 2, + "Memory": 3, "Embedding": 1, } # Registry: (entity_name, source_version) -> converter producing source_version+1. _REGISTRY: dict[tuple[str, int], Callable[[dict], dict]] = {} +# Registry keys whose converter runs only on stored (explicitly versioned) dicts. +_STORED_ONLY: set[tuple[str, int]] = set() def register_migration( - entity: str, source_version: int + entity: str, source_version: int, stored_only: bool = False, ) -> Callable[[Callable[[dict], dict]], Callable[[dict], dict]]: """Register a converter from ``source_version`` to ``source_version+1``. + A ``stored_only`` converter runs only on a dict that carries an explicit + ``version`` (a stored document); a version-less fresh payload skips it. + Used as a decorator:: @register_migration("SlayerModel", 1) @@ -47,11 +52,28 @@ def deco(fn: Callable[[dict], dict]) -> Callable[[dict], dict]: f"Duplicate migration for {entity} v{source_version}" ) _REGISTRY[key] = fn + if stored_only: + _STORED_ONLY.add(key) return fn return deco +def stamp_stored(data: Any) -> Any: + """Mark a stored dict without ``version`` as v1, so stored-only steps run on it.""" + if isinstance(data, dict) and "version" not in data: + return {**data, "version": 1} + return data + + +def migrate_nested(entity: str, data: Any) -> Any: + """``migrate`` a nested document, keeping an absent ``version`` absent for the stored-only gate.""" + out = migrate(entity, data) + if isinstance(data, dict) and "version" not in data: + out.pop("version", None) + return out + + @register_migration("SlayerModel", 9) def _model_v9_to_v10(data: dict) -> dict: """v10: per-doc no-op; the cross-document exact-inverse join dedup runs in @@ -134,6 +156,7 @@ def migrate(entity: str, data: Any) -> Any: raise KeyError(f"Unknown entity '{entity}' in migrate()") data = dict(data) # never mutate caller's payload target = CURRENT_VERSIONS[entity] + stored = "version" in data current = int(data.get("version", 1)) while current < target: fn = _REGISTRY.get((entity, current)) @@ -141,7 +164,8 @@ def migrate(entity: str, data: Any) -> Any: raise RuntimeError( f"No migration registered for {entity} v{current} → v{current + 1}" ) - data = fn(dict(data)) + if stored or (entity, current) not in _STORED_ONLY: + data = fn(dict(data)) current += 1 data["version"] = current data.setdefault("version", target) @@ -162,3 +186,4 @@ def migrate(entity: str, data: Any) -> Any: from slayer.storage import v7_migration # noqa: E402, F401 # ALLOW(import-not-top): circular — migration modules import register_migration from here from slayer.storage import v8_migration # noqa: E402, F401 # ALLOW(import-not-top): circular — migration modules import register_migration from here from slayer.storage import v9_migration # noqa: E402, F401 # ALLOW(import-not-top): circular — migration modules import register_migration from here +from slayer.storage import rank_direction_migration # noqa: E402, F401 # ALLOW(import-not-top): circular — migration modules import register_migration from here diff --git a/slayer/storage/rank_direction_migration.py b/slayer/storage/rank_direction_migration.py new file mode 100644 index 00000000..6079456e --- /dev/null +++ b/slayer/storage/rank_direction_migration.py @@ -0,0 +1,145 @@ +"""SlayerModel v13 / SlayerQuery v5 / Memory v3: a stored bare ``rank(`` / ``dense_rank(`` keeps its descending order. + +Stored-only steps: a fresh payload's bare call stays bare and fails with the missing-direction error. +""" + +import io +import tokenize +from typing import Any + +from slayer.storage.migrations import register_migration, stamp_stored + +_DIRECTED = frozenset({"rank", "dense_rank"}) +_OPEN = frozenset({"(", "[", "{"}) +_CLOSE = frozenset({")", "]", "}"}) +_SKIP = frozenset({tokenize.NL, tokenize.NEWLINE, tokenize.COMMENT, tokenize.INDENT, tokenize.DEDENT}) +_DESC = "direction='desc'" + + +def add_desc_direction(text: str) -> str: + """Give every ``rank(`` / ``dense_rank(`` call lacking a top-level ``direction=`` a ``direction='desc'``; untokenisable text is returned unchanged.""" + try: + tokens = [t for t in tokenize.generate_tokens(io.StringIO(text).readline) if t.type not in _SKIP] + except (tokenize.TokenError, SyntaxError): + return text + if any(t.type == tokenize.ERRORTOKEN for t in tokens): + return text + line_starts = [0] + for line in text.splitlines(keepends=True): + line_starts.append(line_starts[-1] + len(line)) + inserts: list[tuple[int, str]] = [] + for i in range(len(tokens)): + if not _is_directed_call(tokens=tokens, i=i): + continue + close, has_direction = _scan_call(tokens=tokens, open_index=i + 1) + if close is None or has_direction or close == i + 2: + continue + before = tokens[close - 1] + piece = f" {_DESC}" if before.string == "," else f", {_DESC}" + row, col = tokens[close].start + inserts.append((line_starts[row - 1] + col, piece)) + for offset, piece in sorted(inserts, reverse=True): + text = text[:offset] + piece + text[offset:] + return text + + +def _is_directed_call(*, tokens: list[tokenize.TokenInfo], i: int) -> bool: + tok = tokens[i] + return ( + tok.type == tokenize.NAME and tok.string in _DIRECTED + and i + 1 < len(tokens) and tokens[i + 1].string == "(" + and not (i > 0 and tokens[i - 1].string == ".") + ) + + +def _scan_call(*, tokens: list[tokenize.TokenInfo], open_index: int) -> tuple[int | None, bool]: + """Index of the ``)`` matching ``tokens[open_index]``, and whether a top-level ``direction=`` sits inside.""" + depth = 0 + has_direction = False + for j in range(open_index, len(tokens)): + s = tokens[j].string + if tokens[j].type == tokenize.OP and s in _OPEN: + depth += 1 + elif tokens[j].type == tokenize.OP and s in _CLOSE: + depth -= 1 + if depth == 0: + return j, has_direction + elif depth == 1 and s == "direction" and j + 1 < len(tokens) and tokens[j + 1].string == "=": + has_direction = True + return None, has_direction + + +def _rewrite_str(value: Any) -> Any: + return add_desc_direction(value) if isinstance(value, str) else value + + +def _rewrite_key(item: Any, key: str) -> Any: + if isinstance(item, dict) and key in item: + return {**item, key: _rewrite_str(item[key])} + return item + + +def _rewrite_items(value: Any, rewrite) -> Any: + if isinstance(value, list): + return [rewrite(v) for v in value] + return rewrite(value) + + +def _rewrite_measure(item: Any) -> Any: + return _rewrite_str(item) if isinstance(item, str) else _rewrite_key(item, "formula") + + +def _rewrite_dimension(item: Any) -> Any: + return _rewrite_str(item) if isinstance(item, str) else _rewrite_key(item, "expression") + + +def _rewrite_time_dimension(item: Any) -> Any: + if isinstance(item, str): + return _rewrite_str(item) + return _rewrite_key(_rewrite_key(item, "dimension"), "column") + + +def _rewrite_order(item: Any) -> Any: + if not isinstance(item, dict): + return _rewrite_str(item) + if "column" in item or "direction" in item: + return _rewrite_key(item, "column") + return {_rewrite_str(k): v for k, v in item.items()} + + +@register_migration(entity="SlayerModel", source_version=12, stored_only=True) +def _model_v12_to_v13(data: dict) -> dict: + """Fill ``direction='desc'`` into measure formulas; mark nested stored queries as stored.""" + if isinstance(data.get("measures"), list): + data["measures"] = [_rewrite_key(m, "formula") for m in data["measures"]] + if isinstance(data.get("source_queries"), list): + data["source_queries"] = [stamp_stored(q) for q in data["source_queries"]] + return data + + +@register_migration(entity="SlayerQuery", source_version=4, stored_only=True) +def _query_v4_to_v5(data: dict) -> dict: + """Fill ``direction='desc'`` into every Mode-B field; mark an inline model as stored.""" + for field, rewrite in ( + ("measures", _rewrite_measure), ("filters", _rewrite_str), ("dimensions", _rewrite_dimension), + ("time_dimensions", _rewrite_time_dimension), ("order", _rewrite_order), + ("main_time_dimension", _rewrite_str), + ): + if data.get(field) is not None: + data[field] = _rewrite_items(data[field], rewrite) + source = data.get("source_model") + if isinstance(source, dict): + if "source_name" in source: + if isinstance(source.get("measures"), list): + data["source_model"] = {**source, "measures": [_rewrite_key(m, "formula") for m in source["measures"]]} + else: + data["source_model"] = stamp_stored(source) + return data + + +@register_migration(entity="Memory", source_version=2, stored_only=True) +def _memory_v2_to_v3(data: dict) -> dict: + """Mark the bundled query as stored.""" + if isinstance(data.get("query"), dict): + data["query"] = stamp_stored(data["query"]) + return data diff --git a/slayer/storage/sqlite_storage.py b/slayer/storage/sqlite_storage.py index eb830ea4..34e2179d 100644 --- a/slayer/storage/sqlite_storage.py +++ b/slayer/storage/sqlite_storage.py @@ -26,6 +26,7 @@ SidecarEmbeddingStore, ) from slayer.storage.sqlite_conn import open_connection, transaction +from slayer.storage.migrations import stamp_stored from slayer.storage.v4_migration import migrate_sqlite_schema @@ -454,7 +455,7 @@ def _save_memory_atomic_sync( ).fetchone() if existing_row is not None: existing_memory = Memory.model_validate( - json.loads(existing_row[0]) + stamp_stored(json.loads(existing_row[0])) ) preserved_created_at = existing_memory.created_at kwargs: dict[str, Any] = { @@ -564,7 +565,7 @@ def _get_memory_sync(self, memory_id: str) -> str | None: async def _get_memory_row(self, memory_id: str) -> Memory | None: raw = await asyncio.to_thread(self._get_memory_sync, memory_id) - return Memory.model_validate(json.loads(raw)) if raw else None + return Memory.model_validate(stamp_stored(json.loads(raw))) if raw else None def _list_memories_sync( self, entities: list[str] | None @@ -591,7 +592,7 @@ async def _list_memories_rows( self, *, entities: list[str] | None ) -> list[Memory]: raws = await asyncio.to_thread(self._list_memories_sync, entities) - return [Memory.model_validate(json.loads(r)) for r in raws] + return [Memory.model_validate(stamp_stored(json.loads(r))) for r in raws] def _delete_memory_sync(self, memory_id: str) -> bool: with transaction(self.db_path) as conn: diff --git a/slayer/storage/v2_migration.py b/slayer/storage/v2_migration.py index e1cd4d6d..98c7e877 100644 --- a/slayer/storage/v2_migration.py +++ b/slayer/storage/v2_migration.py @@ -157,7 +157,8 @@ def _model_v1_to_v2(data: dict) -> dict: for q in raw_source_queries: if isinstance(q, dict) and int(q.get("version") or 1) < 2: migrated = _query_v1_to_v2(dict(q)) - migrated["version"] = 2 + if "version" in q: + migrated["version"] = 2 migrated_sq.append(migrated) else: migrated_sq.append(q) diff --git a/slayer/storage/v3_migration.py b/slayer/storage/v3_migration.py index c255987b..5a554c34 100644 --- a/slayer/storage/v3_migration.py +++ b/slayer/storage/v3_migration.py @@ -18,7 +18,7 @@ import logging import warnings -from slayer.storage.migrations import migrate, register_migration +from slayer.storage.migrations import migrate_nested, register_migration logger = logging.getLogger(__name__) @@ -52,7 +52,7 @@ def _model_v2_to_v3(data: dict) -> dict: raw = data.get("source_queries") if isinstance(raw, list): data["source_queries"] = [ - migrate("SlayerQuery", q) if isinstance(q, dict) else q + migrate_nested("SlayerQuery", q) if isinstance(q, dict) else q for q in raw ] return data diff --git a/slayer/storage/yaml_storage.py b/slayer/storage/yaml_storage.py index c6e5c707..e4be0b72 100644 --- a/slayer/storage/yaml_storage.py +++ b/slayer/storage/yaml_storage.py @@ -46,6 +46,7 @@ SidecarEmbeddingsMixin, SidecarEmbeddingStore, ) +from slayer.storage.migrations import stamp_stored from slayer.storage.v4_migration import migrate_yaml_layout @@ -93,9 +94,9 @@ def _md_to_memory(memory_id: str, text: str) -> Memory: data = dict(fm) if isinstance(fm, dict) else {} data["id"] = memory_id data["learning"] = body - return Memory.model_validate(data) + return Memory.model_validate(stamp_stored(data)) # No frontmatter fence: whole text is the learning body. - return Memory.model_validate({"id": memory_id, "learning": text}) + return Memory.model_validate(stamp_stored({"id": memory_id, "learning": text})) def _stat_key(path: str) -> tuple[int, int, int]: @@ -237,7 +238,7 @@ def migrate_memories_layout(base_dir: str) -> None: id_by_key[rid.casefold()] = rid os.makedirs(mem_dir, exist_ok=True) for r in normalized: - mem = Memory.model_validate(r) + mem = Memory.model_validate(stamp_stored(r)) _atomic_write_text( path=os.path.join(mem_dir, f"{mem.id}.md"), text=_memory_to_md(mem), diff --git a/tests/golden/dev1824_sql_baseline.json b/tests/golden/dev1824_sql_baseline.json index abd4e18a..cddd6321 100644 --- a/tests/golden/dev1824_sql_baseline.json +++ b/tests/golden/dev1824_sql_baseline.json @@ -99,11 +99,11 @@ "lift/dim_first_last::postgres": "WITH _cm_amount_last_partition_by_region AS (\n SELECT\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.amount_last_partition_by_region\" AS \"amount_last_partition_by_region\"\n FROM (\n SELECT\n _val_0 AS \"orders.region\",\n CAST(MAX(CASE WHEN _ranked_rn = 1 THEN _val_1 END) AS DOUBLE PRECISION) AS \"orders.amount_last_partition_by_region\"\n FROM (\n SELECT\n orders.region AS _val_0,\n orders.amount AS _val_1,\n ROW_NUMBER() OVER (PARTITION BY orders.region ORDER BY orders.ordered_at DESC) AS _ranked_rn\n FROM orders AS orders\n ) AS _ranked_src\n GROUP BY\n _val_0\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n _cm_amount_last_partition_by_region.\"amount_last_partition_by_region\" AS \"orders.la\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_amount_last_partition_by_region\n ON orders.region IS NOT DISTINCT FROM _cm_amount_last_partition_by_region.\"region\"\nGROUP BY\n orders.region,\n _cm_amount_last_partition_by_region.\"amount_last_partition_by_region\"", "lift/dim_first_last::sqlite": "WITH _cm_amount_last_partition_by_region AS (\n SELECT\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.amount_last_partition_by_region\" AS \"amount_last_partition_by_region\"\n FROM (\n SELECT\n _val_0 AS \"orders.region\",\n CAST(MAX(CASE WHEN _ranked_rn = 1 THEN _val_1 END) AS REAL) AS \"orders.amount_last_partition_by_region\"\n FROM (\n SELECT\n orders.region AS _val_0,\n orders.amount AS _val_1,\n ROW_NUMBER() OVER (PARTITION BY orders.region ORDER BY orders.ordered_at DESC) AS _ranked_rn\n FROM orders AS orders\n ) AS _ranked_src\n GROUP BY\n _val_0\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n _cm_amount_last_partition_by_region.\"amount_last_partition_by_region\" AS \"orders.la\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_amount_last_partition_by_region\n ON orders.region IS _cm_amount_last_partition_by_region.\"region\"\nGROUP BY\n orders.region,\n _cm_amount_last_partition_by_region.\"amount_last_partition_by_region\"", "lift/dim_first_last::tsql": "WITH _cm_amount_last_partition_by_region AS (\n SELECT\n _stage_inner.[orders___region] AS [region],\n _stage_inner.[orders___amount_last_partition_by_region] AS [amount_last_partition_by_region]\n FROM (\n SELECT\n _val_0 AS [orders___region],\n CAST(MAX(CASE WHEN _ranked_rn = 1 THEN _val_1 END) AS FLOAT) AS [orders___amount_last_partition_by_region]\n FROM (\n SELECT\n orders.region AS _val_0,\n orders.amount AS _val_1,\n ROW_NUMBER() OVER (PARTITION BY orders.region ORDER BY orders.ordered_at DESC) AS _ranked_rn\n FROM orders AS orders\n ) AS _ranked_src\n GROUP BY\n _val_0\n ) AS _stage_inner\n)\nSELECT\n orders.region AS [orders___region],\n _cm_amount_last_partition_by_region.[amount_last_partition_by_region] AS [orders___la],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___s]\nFROM orders AS orders\nLEFT JOIN _cm_amount_last_partition_by_region\n ON (\n orders.region = _cm_amount_last_partition_by_region.[region]\n OR (\n orders.region IS NULL AND _cm_amount_last_partition_by_region.[region] IS NULL\n )\n )\nGROUP BY\n orders.region,\n _cm_amount_last_partition_by_region.[amount_last_partition_by_region]", - "lift/dim_rank::bigquery": "WITH base_2 AS (\n SELECT\n orders.region AS `orders___region`,\n SUM(orders.amount) AS `orders___amount_sum_partition_by_region`\n FROM orders AS orders\n GROUP BY\n orders.region\n), step1 AS (\n SELECT\n `orders___region`,\n `orders___amount_sum_partition_by_region`,\n RANK() OVER (ORDER BY `orders___amount_sum_partition_by_region` DESC) AS `orders___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`orders___region` AS `region`,\n _stage_inner.`orders___rank` AS `rank`\n FROM (\n SELECT\n `orders___region`,\n `orders___rank`\n FROM (\n SELECT\n `orders___region`,\n `orders___amount_sum_partition_by_region`,\n `orders___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS `orders___region`,\n _cm_rank.`rank` AS `orders___rr`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___s`\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.region IS NOT DISTINCT FROM _cm_rank.`region`\nGROUP BY\n orders.region,\n _cm_rank.`rank`", - "lift/dim_rank::duckdb": "WITH base_2 AS (\n SELECT\n orders.region AS \"orders.region\",\n SUM(orders.amount) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), step1 AS (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n RANK() OVER (ORDER BY \"orders.amount_sum_partition_by_region\" DESC) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n _cm_rank.\"rank\" AS \"orders.rr\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n _cm_rank.\"rank\"", - "lift/dim_rank::postgres": "WITH base_2 AS (\n SELECT\n orders.region AS \"orders.region\",\n SUM(orders.amount) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), step1 AS (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n RANK() OVER (ORDER BY \"orders.amount_sum_partition_by_region\" DESC NULLS LAST) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n _cm_rank.\"rank\" AS \"orders.rr\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n _cm_rank.\"rank\"", - "lift/dim_rank::sqlite": "WITH base_2 AS (\n SELECT\n orders.region AS \"orders.region\",\n SUM(orders.amount) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), step1 AS (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n RANK() OVER (ORDER BY \"orders.amount_sum_partition_by_region\" DESC) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n _cm_rank.\"rank\" AS \"orders.rr\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.region IS _cm_rank.\"region\"\nGROUP BY\n orders.region,\n _cm_rank.\"rank\"", - "lift/dim_rank::tsql": "WITH base_2 AS (\n SELECT\n orders.region AS [orders___region],\n SUM(orders.amount) AS [orders___amount_sum_partition_by_region]\n FROM orders AS orders\n GROUP BY\n orders.region\n), step1 AS (\n SELECT\n [orders___region] AS [orders___region],\n [orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n RANK() OVER (ORDER BY [orders___amount_sum_partition_by_region] DESC) AS [orders___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[orders___region] AS [region],\n _stage_inner.[orders___rank] AS [rank]\n FROM (\n SELECT\n [orders___region] AS [orders___region],\n [orders___rank] AS [orders___rank]\n FROM (\n SELECT\n [orders___region],\n [orders___amount_sum_partition_by_region],\n [orders___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS [orders___region],\n _cm_rank.[rank] AS [orders___rr],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___s]\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON (\n orders.region = _cm_rank.[region]\n OR (\n orders.region IS NULL AND _cm_rank.[region] IS NULL\n )\n )\nGROUP BY\n orders.region,\n _cm_rank.[rank]", + "lift/dim_rank::bigquery": "WITH base_2 AS (\n SELECT\n orders.region AS `orders___region`,\n SUM(orders.amount) AS `orders___amount_sum_partition_by_region`\n FROM orders AS orders\n GROUP BY\n orders.region\n), step1 AS (\n SELECT\n `orders___region`,\n `orders___amount_sum_partition_by_region`,\n CASE\n WHEN `orders___amount_sum_partition_by_region` IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN `orders___amount_sum_partition_by_region` IS NULL THEN 1 ELSE 0 END\n ORDER BY `orders___amount_sum_partition_by_region` DESC\n )\n END AS `orders___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`orders___region` AS `region`,\n _stage_inner.`orders___rank` AS `rank`\n FROM (\n SELECT\n `orders___region`,\n `orders___rank`\n FROM (\n SELECT\n `orders___region`,\n `orders___amount_sum_partition_by_region`,\n `orders___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS `orders___region`,\n _cm_rank.`rank` AS `orders___rr`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___s`\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.region IS NOT DISTINCT FROM _cm_rank.`region`\nGROUP BY\n orders.region,\n _cm_rank.`rank`", + "lift/dim_rank::duckdb": "WITH base_2 AS (\n SELECT\n orders.region AS \"orders.region\",\n SUM(orders.amount) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), step1 AS (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n CASE\n WHEN \"orders.amount_sum_partition_by_region\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN \"orders.amount_sum_partition_by_region\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"orders.amount_sum_partition_by_region\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n _cm_rank.\"rank\" AS \"orders.rr\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n _cm_rank.\"rank\"", + "lift/dim_rank::postgres": "WITH base_2 AS (\n SELECT\n orders.region AS \"orders.region\",\n SUM(orders.amount) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), step1 AS (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n CASE\n WHEN \"orders.amount_sum_partition_by_region\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN \"orders.amount_sum_partition_by_region\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"orders.amount_sum_partition_by_region\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n _cm_rank.\"rank\" AS \"orders.rr\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n _cm_rank.\"rank\"", + "lift/dim_rank::sqlite": "WITH base_2 AS (\n SELECT\n orders.region AS \"orders.region\",\n SUM(orders.amount) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), step1 AS (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n CASE\n WHEN \"orders.amount_sum_partition_by_region\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN \"orders.amount_sum_partition_by_region\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"orders.amount_sum_partition_by_region\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n _cm_rank.\"rank\" AS \"orders.rr\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.region IS _cm_rank.\"region\"\nGROUP BY\n orders.region,\n _cm_rank.\"rank\"", + "lift/dim_rank::tsql": "WITH base_2 AS (\n SELECT\n orders.region AS [orders___region],\n SUM(orders.amount) AS [orders___amount_sum_partition_by_region]\n FROM orders AS orders\n GROUP BY\n orders.region\n), step1 AS (\n SELECT\n [orders___region] AS [orders___region],\n [orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n CASE\n WHEN [orders___amount_sum_partition_by_region] IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN [orders___amount_sum_partition_by_region] IS NULL THEN 1 ELSE 0 END\n ORDER BY [orders___amount_sum_partition_by_region] DESC\n )\n END AS [orders___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[orders___region] AS [region],\n _stage_inner.[orders___rank] AS [rank]\n FROM (\n SELECT\n [orders___region] AS [orders___region],\n [orders___rank] AS [orders___rank]\n FROM (\n SELECT\n [orders___region],\n [orders___amount_sum_partition_by_region],\n [orders___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS [orders___region],\n _cm_rank.[rank] AS [orders___rr],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___s]\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON (\n orders.region = _cm_rank.[region]\n OR (\n orders.region IS NULL AND _cm_rank.[region] IS NULL\n )\n )\nGROUP BY\n orders.region,\n _cm_rank.[rank]", "lift/dim_two_partition_sets::bigquery": "WITH _cm_amount_sum_partition_by_city AS (\n SELECT\n _stage_inner.`orders___city` AS `city`,\n _stage_inner.`orders___amount_sum_partition_by_city` AS `amount_sum_partition_by_city`\n FROM (\n SELECT\n orders.city AS `orders___city`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_city`\n FROM orders AS orders\n GROUP BY\n orders.city\n ) AS _stage_inner\n), _cm_amount_sum_partition_by_region AS (\n SELECT\n _stage_inner.`orders___region` AS `region`,\n _stage_inner.`orders___amount_sum_partition_by_region` AS `amount_sum_partition_by_region`\n FROM (\n SELECT\n orders.region AS `orders___region`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_region`\n FROM orders AS orders\n GROUP BY\n orders.region\n ) AS _stage_inner\n)\nSELECT\n orders.region AS `orders___region`,\n _cm_amount_sum_partition_by_city.`amount_sum_partition_by_city` - _cm_amount_sum_partition_by_region.`amount_sum_partition_by_region` AS `orders___gap`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___s`\nFROM orders AS orders\nLEFT JOIN _cm_amount_sum_partition_by_city\n ON orders.city IS NOT DISTINCT FROM _cm_amount_sum_partition_by_city.`city`\nLEFT JOIN _cm_amount_sum_partition_by_region\n ON orders.region IS NOT DISTINCT FROM _cm_amount_sum_partition_by_region.`region`\nGROUP BY\n orders.region,\n _cm_amount_sum_partition_by_city.`amount_sum_partition_by_city` - _cm_amount_sum_partition_by_region.`amount_sum_partition_by_region`", "lift/dim_two_partition_sets::duckdb": "WITH _cm_amount_sum_partition_by_city AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.amount_sum_partition_by_city\" AS \"amount_sum_partition_by_city\"\n FROM (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n ) AS _stage_inner\n), _cm_amount_sum_partition_by_region AS (\n SELECT\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.amount_sum_partition_by_region\" AS \"amount_sum_partition_by_region\"\n FROM (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n _cm_amount_sum_partition_by_city.\"amount_sum_partition_by_city\" - _cm_amount_sum_partition_by_region.\"amount_sum_partition_by_region\" AS \"orders.gap\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_amount_sum_partition_by_city\n ON orders.city IS NOT DISTINCT FROM _cm_amount_sum_partition_by_city.\"city\"\nLEFT JOIN _cm_amount_sum_partition_by_region\n ON orders.region IS NOT DISTINCT FROM _cm_amount_sum_partition_by_region.\"region\"\nGROUP BY\n orders.region,\n _cm_amount_sum_partition_by_city.\"amount_sum_partition_by_city\" - _cm_amount_sum_partition_by_region.\"amount_sum_partition_by_region\"", "lift/dim_two_partition_sets::postgres": "WITH _cm_amount_sum_partition_by_city AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.amount_sum_partition_by_city\" AS \"amount_sum_partition_by_city\"\n FROM (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n ) AS _stage_inner\n), _cm_amount_sum_partition_by_region AS (\n SELECT\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.amount_sum_partition_by_region\" AS \"amount_sum_partition_by_region\"\n FROM (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n _cm_amount_sum_partition_by_city.\"amount_sum_partition_by_city\" - _cm_amount_sum_partition_by_region.\"amount_sum_partition_by_region\" AS \"orders.gap\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_amount_sum_partition_by_city\n ON orders.city IS NOT DISTINCT FROM _cm_amount_sum_partition_by_city.\"city\"\nLEFT JOIN _cm_amount_sum_partition_by_region\n ON orders.region IS NOT DISTINCT FROM _cm_amount_sum_partition_by_region.\"region\"\nGROUP BY\n orders.region,\n _cm_amount_sum_partition_by_city.\"amount_sum_partition_by_city\" - _cm_amount_sum_partition_by_region.\"amount_sum_partition_by_region\"", @@ -139,11 +139,11 @@ "lift/order_raw_aggregate::postgres": "WITH _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), _base AS (\n SELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n CASE\n WHEN _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" > 35\n THEN 1\n ELSE 0\n END AS \"orders.band\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.s\"\n FROM orders AS orders\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON orders.city IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n GROUP BY\n orders.region,\n orders.city,\n CASE\n WHEN _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" > 35\n THEN 1\n ELSE 0\n END\n)\nSELECT\n _base.\"orders.region\",\n _base.\"orders.city\",\n _base.\"orders.band\",\n _base.\"orders.s\"\nFROM _base\nLEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\nORDER BY\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" ASC", "lift/order_raw_aggregate::sqlite": "WITH _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), _base AS (\n SELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n CASE\n WHEN _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" > 35\n THEN 1\n ELSE 0\n END AS \"orders.band\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.s\"\n FROM orders AS orders\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON orders.city IS _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n GROUP BY\n orders.region,\n orders.city,\n CASE\n WHEN _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" > 35\n THEN 1\n ELSE 0\n END\n)\nSELECT\n _base.\"orders.region\",\n _base.\"orders.city\",\n _base.\"orders.band\",\n _base.\"orders.s\"\nFROM _base\nLEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base.\"orders.city\" IS _cm_orders__amount_sum_partition_by_city.\"orders.city\"\nORDER BY\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" ASC NULLS LAST", "lift/order_raw_aggregate::tsql": "WITH _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS [orders___city],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_city]\n FROM orders AS orders\n GROUP BY\n orders.city\n), _base AS (\n SELECT\n orders.region AS [orders___region],\n orders.city AS [orders___city],\n CASE\n WHEN _cm_orders__amount_sum_partition_by_city.[orders___amount_sum_partition_by_city] > 35\n THEN 1\n ELSE 0\n END AS [orders___band],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___s]\n FROM orders AS orders\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON (\n orders.city = _cm_orders__amount_sum_partition_by_city.[orders___city]\n OR (\n orders.city IS NULL\n AND _cm_orders__amount_sum_partition_by_city.[orders___city] IS NULL\n )\n )\n GROUP BY\n orders.region,\n orders.city,\n CASE\n WHEN _cm_orders__amount_sum_partition_by_city.[orders___amount_sum_partition_by_city] > 35\n THEN 1\n ELSE 0\n END\n)\nSELECT\n _base.[orders___region],\n _base.[orders___city],\n _base.[orders___band],\n _base.[orders___s]\nFROM _base\nLEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON (\n _base.[orders___city] = _cm_orders__amount_sum_partition_by_city.[orders___city]\n OR (\n _base.[orders___city] IS NULL\n AND _cm_orders__amount_sum_partition_by_city.[orders___city] IS NULL\n )\n )\nORDER BY\n _cm_orders__amount_sum_partition_by_city.[orders___amount_sum_partition_by_city] ASC", - "lift/rank_partitioned_measure::bigquery": "WITH _base AS (\n SELECT\n orders.region AS `orders___region`,\n orders.city AS `orders___city`\n FROM orders AS orders\n GROUP BY\n orders.region,\n orders.city\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS `orders___region`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_region`\n FROM orders AS orders\n GROUP BY\n orders.region\n), base AS (\n SELECT\n _base.`orders___region`,\n _base.`orders___city`,\n _cm_orders__amount_sum_partition_by_region.`orders___amount_sum_partition_by_region` AS `orders___amount_sum_partition_by_region`\n FROM _base\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base.`orders___region` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.`orders___region`\n), step1 AS (\n SELECT\n `orders___region`,\n `orders___city`,\n `orders___amount_sum_partition_by_region`,\n RANK() OVER (ORDER BY `orders___amount_sum_partition_by_region` DESC) AS `orders___r`\n FROM base\n)\nSELECT\n `orders___region`,\n `orders___city`,\n `orders___r`\nFROM (\n SELECT\n `orders___region`,\n `orders___city`,\n `orders___amount_sum_partition_by_region`,\n `orders___r`\n FROM step1\n) AS _outer", - "lift/rank_partitioned_measure::duckdb": "WITH _base AS (\n SELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\"\n FROM orders AS orders\n GROUP BY\n orders.region,\n orders.city\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), base AS (\n SELECT\n _base.\"orders.region\",\n _base.\"orders.city\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\"\n FROM _base\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n), step1 AS (\n SELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.amount_sum_partition_by_region\",\n RANK() OVER (ORDER BY \"orders.amount_sum_partition_by_region\" DESC) AS \"orders.r\"\n FROM base\n)\nSELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.r\"\nFROM (\n SELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.r\"\n FROM step1\n) AS _outer", - "lift/rank_partitioned_measure::postgres": "WITH _base AS (\n SELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\"\n FROM orders AS orders\n GROUP BY\n orders.region,\n orders.city\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), base AS (\n SELECT\n _base.\"orders.region\",\n _base.\"orders.city\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\"\n FROM _base\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n), step1 AS (\n SELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.amount_sum_partition_by_region\",\n RANK() OVER (ORDER BY \"orders.amount_sum_partition_by_region\" DESC NULLS LAST) AS \"orders.r\"\n FROM base\n)\nSELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.r\"\nFROM (\n SELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.r\"\n FROM step1\n) AS _outer", - "lift/rank_partitioned_measure::sqlite": "WITH _base AS (\n SELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\"\n FROM orders AS orders\n GROUP BY\n orders.region,\n orders.city\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), base AS (\n SELECT\n _base.\"orders.region\",\n _base.\"orders.city\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\"\n FROM _base\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base.\"orders.region\" IS _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n), step1 AS (\n SELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.amount_sum_partition_by_region\",\n RANK() OVER (ORDER BY \"orders.amount_sum_partition_by_region\" DESC) AS \"orders.r\"\n FROM base\n)\nSELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.r\"\nFROM (\n SELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.r\"\n FROM step1\n) AS _outer", - "lift/rank_partitioned_measure::tsql": "WITH _base AS (\n SELECT\n orders.region AS [orders___region],\n orders.city AS [orders___city]\n FROM orders AS orders\n GROUP BY\n orders.region,\n orders.city\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS [orders___region],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_region]\n FROM orders AS orders\n GROUP BY\n orders.region\n), base AS (\n SELECT\n _base.[orders___region] AS [orders___region],\n _base.[orders___city] AS [orders___city],\n _cm_orders__amount_sum_partition_by_region.[orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region]\n FROM _base\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON (\n _base.[orders___region] = _cm_orders__amount_sum_partition_by_region.[orders___region]\n OR (\n _base.[orders___region] IS NULL\n AND _cm_orders__amount_sum_partition_by_region.[orders___region] IS NULL\n )\n )\n), step1 AS (\n SELECT\n [orders___region] AS [orders___region],\n [orders___city] AS [orders___city],\n [orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n RANK() OVER (ORDER BY [orders___amount_sum_partition_by_region] DESC) AS [orders___r]\n FROM base\n)\nSELECT\n [orders___region],\n [orders___city],\n [orders___r]\nFROM (\n SELECT\n [orders___region],\n [orders___city],\n [orders___amount_sum_partition_by_region],\n [orders___r]\n FROM step1\n) AS _outer", + "lift/rank_partitioned_measure::bigquery": "WITH _base AS (\n SELECT\n orders.region AS `orders___region`,\n orders.city AS `orders___city`\n FROM orders AS orders\n GROUP BY\n orders.region,\n orders.city\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS `orders___region`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_region`\n FROM orders AS orders\n GROUP BY\n orders.region\n), base AS (\n SELECT\n _base.`orders___region`,\n _base.`orders___city`,\n _cm_orders__amount_sum_partition_by_region.`orders___amount_sum_partition_by_region` AS `orders___amount_sum_partition_by_region`\n FROM _base\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base.`orders___region` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.`orders___region`\n), step1 AS (\n SELECT\n `orders___region`,\n `orders___city`,\n `orders___amount_sum_partition_by_region`,\n CASE\n WHEN `orders___amount_sum_partition_by_region` IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN `orders___amount_sum_partition_by_region` IS NULL THEN 1 ELSE 0 END\n ORDER BY `orders___amount_sum_partition_by_region` DESC\n )\n END AS `orders___r`\n FROM base\n)\nSELECT\n `orders___region`,\n `orders___city`,\n `orders___r`\nFROM (\n SELECT\n `orders___region`,\n `orders___city`,\n `orders___amount_sum_partition_by_region`,\n `orders___r`\n FROM step1\n) AS _outer", + "lift/rank_partitioned_measure::duckdb": "WITH _base AS (\n SELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\"\n FROM orders AS orders\n GROUP BY\n orders.region,\n orders.city\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), base AS (\n SELECT\n _base.\"orders.region\",\n _base.\"orders.city\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\"\n FROM _base\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n), step1 AS (\n SELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.amount_sum_partition_by_region\",\n CASE\n WHEN \"orders.amount_sum_partition_by_region\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN \"orders.amount_sum_partition_by_region\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"orders.amount_sum_partition_by_region\" DESC\n )\n END AS \"orders.r\"\n FROM base\n)\nSELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.r\"\nFROM (\n SELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.r\"\n FROM step1\n) AS _outer", + "lift/rank_partitioned_measure::postgres": "WITH _base AS (\n SELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\"\n FROM orders AS orders\n GROUP BY\n orders.region,\n orders.city\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), base AS (\n SELECT\n _base.\"orders.region\",\n _base.\"orders.city\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\"\n FROM _base\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n), step1 AS (\n SELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.amount_sum_partition_by_region\",\n CASE\n WHEN \"orders.amount_sum_partition_by_region\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN \"orders.amount_sum_partition_by_region\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"orders.amount_sum_partition_by_region\" DESC\n )\n END AS \"orders.r\"\n FROM base\n)\nSELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.r\"\nFROM (\n SELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.r\"\n FROM step1\n) AS _outer", + "lift/rank_partitioned_measure::sqlite": "WITH _base AS (\n SELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\"\n FROM orders AS orders\n GROUP BY\n orders.region,\n orders.city\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), base AS (\n SELECT\n _base.\"orders.region\",\n _base.\"orders.city\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\"\n FROM _base\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base.\"orders.region\" IS _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n), step1 AS (\n SELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.amount_sum_partition_by_region\",\n CASE\n WHEN \"orders.amount_sum_partition_by_region\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN \"orders.amount_sum_partition_by_region\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"orders.amount_sum_partition_by_region\" DESC\n )\n END AS \"orders.r\"\n FROM base\n)\nSELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.r\"\nFROM (\n SELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.r\"\n FROM step1\n) AS _outer", + "lift/rank_partitioned_measure::tsql": "WITH _base AS (\n SELECT\n orders.region AS [orders___region],\n orders.city AS [orders___city]\n FROM orders AS orders\n GROUP BY\n orders.region,\n orders.city\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS [orders___region],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_region]\n FROM orders AS orders\n GROUP BY\n orders.region\n), base AS (\n SELECT\n _base.[orders___region] AS [orders___region],\n _base.[orders___city] AS [orders___city],\n _cm_orders__amount_sum_partition_by_region.[orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region]\n FROM _base\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON (\n _base.[orders___region] = _cm_orders__amount_sum_partition_by_region.[orders___region]\n OR (\n _base.[orders___region] IS NULL\n AND _cm_orders__amount_sum_partition_by_region.[orders___region] IS NULL\n )\n )\n), step1 AS (\n SELECT\n [orders___region] AS [orders___region],\n [orders___city] AS [orders___city],\n [orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n CASE\n WHEN [orders___amount_sum_partition_by_region] IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN [orders___amount_sum_partition_by_region] IS NULL THEN 1 ELSE 0 END\n ORDER BY [orders___amount_sum_partition_by_region] DESC\n )\n END AS [orders___r]\n FROM base\n)\nSELECT\n [orders___region],\n [orders___city],\n [orders___r]\nFROM (\n SELECT\n [orders___region],\n [orders___city],\n [orders___amount_sum_partition_by_region],\n [orders___r]\n FROM step1\n) AS _outer", "lift/temporal_partition_last::bigquery": "WITH _base AS (\n SELECT\n orders.region AS `orders___region`,\n orders.city AS `orders___city`,\n DATE_TRUNC(orders.ordered_at, MONTH) AS `orders___ordered_at`\n FROM orders AS orders\n GROUP BY\n orders.region,\n orders.city,\n DATE_TRUNC(orders.ordered_at, MONTH)\n), _cm_orders__amount_last_partition_by_ordered_at_region AS (\n SELECT\n _val_1 AS `orders___ordered_at`,\n _val_0 AS `orders___region`,\n CAST(MAX(CASE WHEN _ranked_rn = 1 THEN _val_2 END) AS FLOAT64) AS `orders___l`\n FROM (\n SELECT\n orders.region AS _val_0,\n DATE_TRUNC(orders.ordered_at, MONTH) AS _val_1,\n orders.amount AS _val_2,\n ROW_NUMBER() OVER (\n PARTITION BY orders.region, DATE_TRUNC(orders.ordered_at, MONTH)\n ORDER BY orders.ordered_at DESC\n ) AS _ranked_rn\n FROM orders AS orders\n ) AS _ranked_src\n GROUP BY\n _val_1,\n _val_0\n)\nSELECT\n _base.`orders___region`,\n _base.`orders___city`,\n _base.`orders___ordered_at`,\n _cm_orders__amount_last_partition_by_ordered_at_region.`orders___l`\nFROM _base\nLEFT JOIN _cm_orders__amount_last_partition_by_ordered_at_region\n ON _base.`orders___region` IS NOT DISTINCT FROM _cm_orders__amount_last_partition_by_ordered_at_region.`orders___region`\n AND _base.`orders___ordered_at` IS NOT DISTINCT FROM _cm_orders__amount_last_partition_by_ordered_at_region.`orders___ordered_at`", "lift/temporal_partition_last::duckdb": "WITH _base AS (\n SELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at\"\n FROM orders AS orders\n GROUP BY\n orders.region,\n orders.city,\n DATE_TRUNC('MONTH', orders.ordered_at)\n), _cm_orders__amount_last_partition_by_ordered_at_region AS (\n SELECT\n _val_1 AS \"orders.ordered_at\",\n _val_0 AS \"orders.region\",\n CAST(MAX(CASE WHEN _ranked_rn = 1 THEN _val_2 END) AS DOUBLE) AS \"orders.l\"\n FROM (\n SELECT\n orders.region AS _val_0,\n DATE_TRUNC('MONTH', orders.ordered_at) AS _val_1,\n orders.amount AS _val_2,\n ROW_NUMBER() OVER (\n PARTITION BY orders.region, DATE_TRUNC('MONTH', orders.ordered_at)\n ORDER BY orders.ordered_at DESC\n ) AS _ranked_rn\n FROM orders AS orders\n ) AS _ranked_src\n GROUP BY\n _val_1,\n _val_0\n)\nSELECT\n _base.\"orders.region\",\n _base.\"orders.city\",\n _base.\"orders.ordered_at\",\n _cm_orders__amount_last_partition_by_ordered_at_region.\"orders.l\"\nFROM _base\nLEFT JOIN _cm_orders__amount_last_partition_by_ordered_at_region\n ON _base.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_last_partition_by_ordered_at_region.\"orders.region\"\n AND _base.\"orders.ordered_at\" IS NOT DISTINCT FROM _cm_orders__amount_last_partition_by_ordered_at_region.\"orders.ordered_at\"", "lift/temporal_partition_last::postgres": "WITH _base AS (\n SELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at\"\n FROM orders AS orders\n GROUP BY\n orders.region,\n orders.city,\n DATE_TRUNC('MONTH', orders.ordered_at)\n), _cm_orders__amount_last_partition_by_ordered_at_region AS (\n SELECT\n _val_1 AS \"orders.ordered_at\",\n _val_0 AS \"orders.region\",\n CAST(MAX(CASE WHEN _ranked_rn = 1 THEN _val_2 END) AS DOUBLE PRECISION) AS \"orders.l\"\n FROM (\n SELECT\n orders.region AS _val_0,\n DATE_TRUNC('MONTH', orders.ordered_at) AS _val_1,\n orders.amount AS _val_2,\n ROW_NUMBER() OVER (\n PARTITION BY orders.region, DATE_TRUNC('MONTH', orders.ordered_at)\n ORDER BY orders.ordered_at DESC\n ) AS _ranked_rn\n FROM orders AS orders\n ) AS _ranked_src\n GROUP BY\n _val_1,\n _val_0\n)\nSELECT\n _base.\"orders.region\",\n _base.\"orders.city\",\n _base.\"orders.ordered_at\",\n _cm_orders__amount_last_partition_by_ordered_at_region.\"orders.l\"\nFROM _base\nLEFT JOIN _cm_orders__amount_last_partition_by_ordered_at_region\n ON _base.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_last_partition_by_ordered_at_region.\"orders.region\"\n AND _base.\"orders.ordered_at\" IS NOT DISTINCT FROM _cm_orders__amount_last_partition_by_ordered_at_region.\"orders.ordered_at\"", diff --git a/tests/golden/dev1832_sql_baseline.json b/tests/golden/dev1832_sql_baseline.json index 00ddb420..870fc62a 100644 --- a/tests/golden/dev1832_sql_baseline.json +++ b/tests/golden/dev1832_sql_baseline.json @@ -41,13 +41,13 @@ "lifted/mixed_reagg::snowflake": "WITH _cm_amount_sum_partition_by_ordered_at_region AS (\n SELECT\n _stage_inner.\"monthly.region\" AS \"region\",\n _stage_inner.\"monthly.ordered_at_month\" AS \"ordered_at_month\",\n _stage_inner.\"monthly.amount_sum_partition_by_ordered_at_region\" AS \"amount_sum_partition_by_ordered_at_region\"\n FROM (\n SELECT\n monthly.region AS \"monthly.region\",\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at_month\",\n CAST(SUM(monthly.amount) AS DOUBLE) AS \"monthly.amount_sum_partition_by_ordered_at_region\"\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n DATE_TRUNC('MONTH', monthly.ordered_at)\n ) AS _stage_inner\n), _cm_min_amount_sum_partition_by_ordered_at_region AS (\n SELECT\n _stage_inner.\"monthly.region\" AS \"region\",\n _stage_inner.\"monthly.min_amount_sum_partition_by_ordered_at_region\" AS \"min_amount_sum_partition_by_ordered_at_region\"\n FROM (\n SELECT\n _base.\"monthly.region\" AS \"monthly.region\",\n MIN(_base._v) AS \"monthly.min_amount_sum_partition_by_ordered_at_region\"\n FROM (\n SELECT\n monthly.region AS \"monthly.region\",\n monthly.region AS _ek0,\n DATE_TRUNC('MONTH', monthly.ordered_at) AS _ek1,\n MAX(\n _cm_amount_sum_partition_by_ordered_at_region.\"amount_sum_partition_by_ordered_at_region\"\n ) AS _v\n FROM monthly AS monthly\n LEFT JOIN _cm_amount_sum_partition_by_ordered_at_region\n ON monthly.region IS NOT DISTINCT FROM _cm_amount_sum_partition_by_ordered_at_region.\"region\"\n AND DATE_TRUNC('MONTH', monthly.ordered_at) IS NOT DISTINCT FROM _cm_amount_sum_partition_by_ordered_at_region.\"ordered_at_month\"\n GROUP BY\n monthly.region,\n monthly.region,\n DATE_TRUNC('MONTH', monthly.ordered_at)\n ) AS _base\n GROUP BY\n _base.\"monthly.region\"\n ) AS _stage_inner\n)\nSELECT\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at\",\n SUM(\n monthly.amount * _cm_min_amount_sum_partition_by_ordered_at_region.\"min_amount_sum_partition_by_ordered_at_region\"\n ) AS \"monthly.m\"\nFROM monthly AS monthly\nLEFT JOIN _cm_min_amount_sum_partition_by_ordered_at_region\n ON monthly.region IS NOT DISTINCT FROM _cm_min_amount_sum_partition_by_ordered_at_region.\"region\"\nGROUP BY\n DATE_TRUNC('MONTH', monthly.ordered_at)", "lifted/mixed_reagg::sqlite": "WITH _cm_amount_sum_partition_by_ordered_at_region AS (\n SELECT\n _stage_inner.\"monthly.region\" AS \"region\",\n _stage_inner.\"monthly.ordered_at_month\" AS \"ordered_at_month\",\n _stage_inner.\"monthly.amount_sum_partition_by_ordered_at_region\" AS \"amount_sum_partition_by_ordered_at_region\"\n FROM (\n SELECT\n monthly.region AS \"monthly.region\",\n STRFTIME('%Y-%m-01', monthly.ordered_at) AS \"monthly.ordered_at_month\",\n CAST(SUM(monthly.amount) AS REAL) AS \"monthly.amount_sum_partition_by_ordered_at_region\"\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n STRFTIME('%Y-%m-01', monthly.ordered_at)\n ) AS _stage_inner\n), _cm_min_amount_sum_partition_by_ordered_at_region AS (\n SELECT\n _stage_inner.\"monthly.region\" AS \"region\",\n _stage_inner.\"monthly.min_amount_sum_partition_by_ordered_at_region\" AS \"min_amount_sum_partition_by_ordered_at_region\"\n FROM (\n SELECT\n _base.\"monthly.region\" AS \"monthly.region\",\n MIN(_base._v) AS \"monthly.min_amount_sum_partition_by_ordered_at_region\"\n FROM (\n SELECT\n monthly.region AS \"monthly.region\",\n monthly.region AS _ek0,\n STRFTIME('%Y-%m-01', monthly.ordered_at) AS _ek1,\n MAX(\n _cm_amount_sum_partition_by_ordered_at_region.\"amount_sum_partition_by_ordered_at_region\"\n ) AS _v\n FROM monthly AS monthly\n LEFT JOIN _cm_amount_sum_partition_by_ordered_at_region\n ON monthly.region IS _cm_amount_sum_partition_by_ordered_at_region.\"region\"\n AND STRFTIME('%Y-%m-01', monthly.ordered_at) IS _cm_amount_sum_partition_by_ordered_at_region.\"ordered_at_month\"\n GROUP BY\n monthly.region,\n monthly.region,\n STRFTIME('%Y-%m-01', monthly.ordered_at)\n ) AS _base\n GROUP BY\n _base.\"monthly.region\"\n ) AS _stage_inner\n)\nSELECT\n STRFTIME('%Y-%m-01', monthly.ordered_at) AS \"monthly.ordered_at\",\n SUM(\n monthly.amount * _cm_min_amount_sum_partition_by_ordered_at_region.\"min_amount_sum_partition_by_ordered_at_region\"\n ) AS \"monthly.m\"\nFROM monthly AS monthly\nLEFT JOIN _cm_min_amount_sum_partition_by_ordered_at_region\n ON monthly.region IS _cm_min_amount_sum_partition_by_ordered_at_region.\"region\"\nGROUP BY\n STRFTIME('%Y-%m-01', monthly.ordered_at)", "lifted/mixed_reagg::tsql": "WITH _cm_amount_sum_partition_by_ordered_at_region AS (\n SELECT\n _stage_inner.[monthly___region] AS [region],\n _stage_inner.[monthly___ordered_at_month] AS [ordered_at_month],\n _stage_inner.[monthly___amount_sum_partition_by_ordered_at_region] AS [amount_sum_partition_by_ordered_at_region]\n FROM (\n SELECT\n monthly.region AS [monthly___region],\n DATETRUNC(month, monthly.ordered_at) AS [monthly___ordered_at_month],\n CAST(SUM(monthly.amount) AS FLOAT) AS [monthly___amount_sum_partition_by_ordered_at_region]\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n DATETRUNC(month, monthly.ordered_at)\n ) AS _stage_inner\n), _cm_min_amount_sum_partition_by_ordered_at_region AS (\n SELECT\n _stage_inner.[monthly___region] AS [region],\n _stage_inner.[monthly___min_amount_sum_partition_by_ordered_at_region] AS [min_amount_sum_partition_by_ordered_at_region]\n FROM (\n SELECT\n _base.[monthly___region] AS [monthly___region],\n MIN(_base._v) AS [monthly___min_amount_sum_partition_by_ordered_at_region]\n FROM (\n SELECT\n monthly.region AS [monthly___region],\n monthly.region AS _ek0,\n DATETRUNC(month, monthly.ordered_at) AS _ek1,\n MAX(\n _cm_amount_sum_partition_by_ordered_at_region.[amount_sum_partition_by_ordered_at_region]\n ) AS _v\n FROM monthly AS monthly\n LEFT JOIN _cm_amount_sum_partition_by_ordered_at_region\n ON (\n monthly.region = _cm_amount_sum_partition_by_ordered_at_region.[region]\n OR (\n monthly.region IS NULL\n AND _cm_amount_sum_partition_by_ordered_at_region.[region] IS NULL\n )\n )\n AND (\n DATETRUNC(month, monthly.ordered_at) = _cm_amount_sum_partition_by_ordered_at_region.[ordered_at_month]\n OR (\n DATETRUNC(month, monthly.ordered_at) IS NULL\n AND _cm_amount_sum_partition_by_ordered_at_region.[ordered_at_month] IS NULL\n )\n )\n GROUP BY\n monthly.region,\n monthly.region,\n DATETRUNC(month, monthly.ordered_at)\n ) AS _base\n GROUP BY\n _base.[monthly___region]\n ) AS _stage_inner\n)\nSELECT\n DATETRUNC(month, monthly.ordered_at) AS [monthly___ordered_at],\n SUM(\n monthly.amount * _cm_min_amount_sum_partition_by_ordered_at_region.[min_amount_sum_partition_by_ordered_at_region]\n ) AS [monthly___m]\nFROM monthly AS monthly\nLEFT JOIN _cm_min_amount_sum_partition_by_ordered_at_region\n ON (\n monthly.region = _cm_min_amount_sum_partition_by_ordered_at_region.[region]\n OR (\n monthly.region IS NULL\n AND _cm_min_amount_sum_partition_by_ordered_at_region.[region] IS NULL\n )\n )\nGROUP BY\n DATETRUNC(month, monthly.ordered_at)", - "lifted/mixed_transform::bigquery": "WITH base_2 AS (\n SELECT\n sales.product AS `sales___product`,\n AVG(sales.unit_price) AS `sales___unit_price_avg_partition_by_product`\n FROM sales AS sales\n GROUP BY\n sales.product\n), step1 AS (\n SELECT\n `sales___product`,\n `sales___unit_price_avg_partition_by_product`,\n RANK() OVER (ORDER BY `sales___unit_price_avg_partition_by_product` DESC) AS `sales___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`sales___product` AS `product`,\n _stage_inner.`sales___rank` AS `rank`\n FROM (\n SELECT\n `sales___product`,\n `sales___rank`\n FROM (\n SELECT\n `sales___product`,\n `sales___unit_price_avg_partition_by_product`,\n `sales___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS `sales___region`,\n SUM(sales.quantity * _cm_rank.`rank`) AS `sales___m`\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON sales.product IS NOT DISTINCT FROM _cm_rank.`product`\nGROUP BY\n sales.region", - "lifted/mixed_transform::duckdb": "WITH base_2 AS (\n SELECT\n sales.product AS \"sales.product\",\n AVG(sales.unit_price) AS \"sales.unit_price_avg_partition_by_product\"\n FROM sales AS sales\n GROUP BY\n sales.product\n), step1 AS (\n SELECT\n \"sales.product\",\n \"sales.unit_price_avg_partition_by_product\",\n RANK() OVER (ORDER BY \"sales.unit_price_avg_partition_by_product\" DESC) AS \"sales.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"sales.product\" AS \"product\",\n _stage_inner.\"sales.rank\" AS \"rank\"\n FROM (\n SELECT\n \"sales.product\",\n \"sales.rank\"\n FROM (\n SELECT\n \"sales.product\",\n \"sales.unit_price_avg_partition_by_product\",\n \"sales.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS \"sales.region\",\n SUM(sales.quantity * _cm_rank.\"rank\") AS \"sales.m\"\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON sales.product IS NOT DISTINCT FROM _cm_rank.\"product\"\nGROUP BY\n sales.region", - "lifted/mixed_transform::mysql": "WITH base_2 AS (\n SELECT\n sales.product AS `sales.product`,\n AVG(sales.unit_price) AS `sales.unit_price_avg_partition_by_product`\n FROM sales AS sales\n GROUP BY\n sales.product\n), step1 AS (\n SELECT\n `sales.product`,\n `sales.unit_price_avg_partition_by_product`,\n RANK() OVER (ORDER BY `sales.unit_price_avg_partition_by_product` DESC) AS `sales.rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`sales.product` AS `product`,\n _stage_inner.`sales.rank` AS `rank`\n FROM (\n SELECT\n `sales.product`,\n `sales.rank`\n FROM (\n SELECT\n `sales.product`,\n `sales.unit_price_avg_partition_by_product`,\n `sales.rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS `sales.region`,\n SUM(sales.quantity * _cm_rank.`rank`) AS `sales.m`\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON sales.product <=> _cm_rank.`product`\nGROUP BY\n sales.region", - "lifted/mixed_transform::postgres": "WITH base_2 AS (\n SELECT\n sales.product AS \"sales.product\",\n AVG(sales.unit_price) AS \"sales.unit_price_avg_partition_by_product\"\n FROM sales AS sales\n GROUP BY\n sales.product\n), step1 AS (\n SELECT\n \"sales.product\",\n \"sales.unit_price_avg_partition_by_product\",\n RANK() OVER (ORDER BY \"sales.unit_price_avg_partition_by_product\" DESC NULLS LAST) AS \"sales.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"sales.product\" AS \"product\",\n _stage_inner.\"sales.rank\" AS \"rank\"\n FROM (\n SELECT\n \"sales.product\",\n \"sales.rank\"\n FROM (\n SELECT\n \"sales.product\",\n \"sales.unit_price_avg_partition_by_product\",\n \"sales.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS \"sales.region\",\n SUM(sales.quantity * _cm_rank.\"rank\") AS \"sales.m\"\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON sales.product IS NOT DISTINCT FROM _cm_rank.\"product\"\nGROUP BY\n sales.region", - "lifted/mixed_transform::snowflake": "WITH base_2 AS (\n SELECT\n sales.product AS \"sales.product\",\n AVG(sales.unit_price) AS \"sales.unit_price_avg_partition_by_product\"\n FROM sales AS sales\n GROUP BY\n sales.product\n), step1 AS (\n SELECT\n \"sales.product\",\n \"sales.unit_price_avg_partition_by_product\",\n RANK() OVER (ORDER BY \"sales.unit_price_avg_partition_by_product\" DESC NULLS LAST) AS \"sales.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"sales.product\" AS \"product\",\n _stage_inner.\"sales.rank\" AS \"rank\"\n FROM (\n SELECT\n \"sales.product\",\n \"sales.rank\"\n FROM (\n SELECT\n \"sales.product\",\n \"sales.unit_price_avg_partition_by_product\",\n \"sales.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS \"sales.region\",\n SUM(sales.quantity * _cm_rank.\"rank\") AS \"sales.m\"\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON sales.product IS NOT DISTINCT FROM _cm_rank.\"product\"\nGROUP BY\n sales.region", - "lifted/mixed_transform::sqlite": "WITH base_2 AS (\n SELECT\n sales.product AS \"sales.product\",\n AVG(sales.unit_price) AS \"sales.unit_price_avg_partition_by_product\"\n FROM sales AS sales\n GROUP BY\n sales.product\n), step1 AS (\n SELECT\n \"sales.product\",\n \"sales.unit_price_avg_partition_by_product\",\n RANK() OVER (ORDER BY \"sales.unit_price_avg_partition_by_product\" DESC) AS \"sales.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"sales.product\" AS \"product\",\n _stage_inner.\"sales.rank\" AS \"rank\"\n FROM (\n SELECT\n \"sales.product\",\n \"sales.rank\"\n FROM (\n SELECT\n \"sales.product\",\n \"sales.unit_price_avg_partition_by_product\",\n \"sales.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS \"sales.region\",\n SUM(sales.quantity * _cm_rank.\"rank\") AS \"sales.m\"\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON sales.product IS _cm_rank.\"product\"\nGROUP BY\n sales.region", - "lifted/mixed_transform::tsql": "WITH base_2 AS (\n SELECT\n sales.product AS [sales___product],\n AVG(sales.unit_price) AS [sales___unit_price_avg_partition_by_product]\n FROM sales AS sales\n GROUP BY\n sales.product\n), step1 AS (\n SELECT\n [sales___product] AS [sales___product],\n [sales___unit_price_avg_partition_by_product] AS [sales___unit_price_avg_partition_by_product],\n RANK() OVER (ORDER BY [sales___unit_price_avg_partition_by_product] DESC) AS [sales___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[sales___product] AS [product],\n _stage_inner.[sales___rank] AS [rank]\n FROM (\n SELECT\n [sales___product] AS [sales___product],\n [sales___rank] AS [sales___rank]\n FROM (\n SELECT\n [sales___product],\n [sales___unit_price_avg_partition_by_product],\n [sales___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS [sales___region],\n SUM(sales.quantity * _cm_rank.[rank]) AS [sales___m]\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON (\n sales.product = _cm_rank.[product]\n OR (\n sales.product IS NULL AND _cm_rank.[product] IS NULL\n )\n )\nGROUP BY\n sales.region", + "lifted/mixed_transform::bigquery": "WITH base_2 AS (\n SELECT\n sales.product AS `sales___product`,\n AVG(sales.unit_price) AS `sales___unit_price_avg_partition_by_product`\n FROM sales AS sales\n GROUP BY\n sales.product\n), step1 AS (\n SELECT\n `sales___product`,\n `sales___unit_price_avg_partition_by_product`,\n CASE\n WHEN `sales___unit_price_avg_partition_by_product` IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN `sales___unit_price_avg_partition_by_product` IS NULL THEN 1 ELSE 0 END\n ORDER BY `sales___unit_price_avg_partition_by_product` DESC\n )\n END AS `sales___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`sales___product` AS `product`,\n _stage_inner.`sales___rank` AS `rank`\n FROM (\n SELECT\n `sales___product`,\n `sales___rank`\n FROM (\n SELECT\n `sales___product`,\n `sales___unit_price_avg_partition_by_product`,\n `sales___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS `sales___region`,\n SUM(sales.quantity * _cm_rank.`rank`) AS `sales___m`\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON sales.product IS NOT DISTINCT FROM _cm_rank.`product`\nGROUP BY\n sales.region", + "lifted/mixed_transform::duckdb": "WITH base_2 AS (\n SELECT\n sales.product AS \"sales.product\",\n AVG(sales.unit_price) AS \"sales.unit_price_avg_partition_by_product\"\n FROM sales AS sales\n GROUP BY\n sales.product\n), step1 AS (\n SELECT\n \"sales.product\",\n \"sales.unit_price_avg_partition_by_product\",\n CASE\n WHEN \"sales.unit_price_avg_partition_by_product\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN \"sales.unit_price_avg_partition_by_product\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.unit_price_avg_partition_by_product\" DESC\n )\n END AS \"sales.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"sales.product\" AS \"product\",\n _stage_inner.\"sales.rank\" AS \"rank\"\n FROM (\n SELECT\n \"sales.product\",\n \"sales.rank\"\n FROM (\n SELECT\n \"sales.product\",\n \"sales.unit_price_avg_partition_by_product\",\n \"sales.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS \"sales.region\",\n SUM(sales.quantity * _cm_rank.\"rank\") AS \"sales.m\"\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON sales.product IS NOT DISTINCT FROM _cm_rank.\"product\"\nGROUP BY\n sales.region", + "lifted/mixed_transform::mysql": "WITH base_2 AS (\n SELECT\n sales.product AS `sales.product`,\n AVG(sales.unit_price) AS `sales.unit_price_avg_partition_by_product`\n FROM sales AS sales\n GROUP BY\n sales.product\n), step1 AS (\n SELECT\n `sales.product`,\n `sales.unit_price_avg_partition_by_product`,\n CASE\n WHEN `sales.unit_price_avg_partition_by_product` IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN `sales.unit_price_avg_partition_by_product` IS NULL THEN 1 ELSE 0 END\n ORDER BY `sales.unit_price_avg_partition_by_product` DESC\n )\n END AS `sales.rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`sales.product` AS `product`,\n _stage_inner.`sales.rank` AS `rank`\n FROM (\n SELECT\n `sales.product`,\n `sales.rank`\n FROM (\n SELECT\n `sales.product`,\n `sales.unit_price_avg_partition_by_product`,\n `sales.rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS `sales.region`,\n SUM(sales.quantity * _cm_rank.`rank`) AS `sales.m`\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON sales.product <=> _cm_rank.`product`\nGROUP BY\n sales.region", + "lifted/mixed_transform::postgres": "WITH base_2 AS (\n SELECT\n sales.product AS \"sales.product\",\n AVG(sales.unit_price) AS \"sales.unit_price_avg_partition_by_product\"\n FROM sales AS sales\n GROUP BY\n sales.product\n), step1 AS (\n SELECT\n \"sales.product\",\n \"sales.unit_price_avg_partition_by_product\",\n CASE\n WHEN \"sales.unit_price_avg_partition_by_product\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN \"sales.unit_price_avg_partition_by_product\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.unit_price_avg_partition_by_product\" DESC\n )\n END AS \"sales.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"sales.product\" AS \"product\",\n _stage_inner.\"sales.rank\" AS \"rank\"\n FROM (\n SELECT\n \"sales.product\",\n \"sales.rank\"\n FROM (\n SELECT\n \"sales.product\",\n \"sales.unit_price_avg_partition_by_product\",\n \"sales.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS \"sales.region\",\n SUM(sales.quantity * _cm_rank.\"rank\") AS \"sales.m\"\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON sales.product IS NOT DISTINCT FROM _cm_rank.\"product\"\nGROUP BY\n sales.region", + "lifted/mixed_transform::snowflake": "WITH base_2 AS (\n SELECT\n sales.product AS \"sales.product\",\n AVG(sales.unit_price) AS \"sales.unit_price_avg_partition_by_product\"\n FROM sales AS sales\n GROUP BY\n sales.product\n), step1 AS (\n SELECT\n \"sales.product\",\n \"sales.unit_price_avg_partition_by_product\",\n CASE\n WHEN \"sales.unit_price_avg_partition_by_product\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN \"sales.unit_price_avg_partition_by_product\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.unit_price_avg_partition_by_product\" DESC\n )\n END AS \"sales.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"sales.product\" AS \"product\",\n _stage_inner.\"sales.rank\" AS \"rank\"\n FROM (\n SELECT\n \"sales.product\",\n \"sales.rank\"\n FROM (\n SELECT\n \"sales.product\",\n \"sales.unit_price_avg_partition_by_product\",\n \"sales.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS \"sales.region\",\n SUM(sales.quantity * _cm_rank.\"rank\") AS \"sales.m\"\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON sales.product IS NOT DISTINCT FROM _cm_rank.\"product\"\nGROUP BY\n sales.region", + "lifted/mixed_transform::sqlite": "WITH base_2 AS (\n SELECT\n sales.product AS \"sales.product\",\n AVG(sales.unit_price) AS \"sales.unit_price_avg_partition_by_product\"\n FROM sales AS sales\n GROUP BY\n sales.product\n), step1 AS (\n SELECT\n \"sales.product\",\n \"sales.unit_price_avg_partition_by_product\",\n CASE\n WHEN \"sales.unit_price_avg_partition_by_product\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN \"sales.unit_price_avg_partition_by_product\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.unit_price_avg_partition_by_product\" DESC\n )\n END AS \"sales.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"sales.product\" AS \"product\",\n _stage_inner.\"sales.rank\" AS \"rank\"\n FROM (\n SELECT\n \"sales.product\",\n \"sales.rank\"\n FROM (\n SELECT\n \"sales.product\",\n \"sales.unit_price_avg_partition_by_product\",\n \"sales.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS \"sales.region\",\n SUM(sales.quantity * _cm_rank.\"rank\") AS \"sales.m\"\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON sales.product IS _cm_rank.\"product\"\nGROUP BY\n sales.region", + "lifted/mixed_transform::tsql": "WITH base_2 AS (\n SELECT\n sales.product AS [sales___product],\n AVG(sales.unit_price) AS [sales___unit_price_avg_partition_by_product]\n FROM sales AS sales\n GROUP BY\n sales.product\n), step1 AS (\n SELECT\n [sales___product] AS [sales___product],\n [sales___unit_price_avg_partition_by_product] AS [sales___unit_price_avg_partition_by_product],\n CASE\n WHEN [sales___unit_price_avg_partition_by_product] IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN [sales___unit_price_avg_partition_by_product] IS NULL THEN 1 ELSE 0 END\n ORDER BY [sales___unit_price_avg_partition_by_product] DESC\n )\n END AS [sales___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[sales___product] AS [product],\n _stage_inner.[sales___rank] AS [rank]\n FROM (\n SELECT\n [sales___product] AS [sales___product],\n [sales___rank] AS [sales___rank]\n FROM (\n SELECT\n [sales___product],\n [sales___unit_price_avg_partition_by_product],\n [sales___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS [sales___region],\n SUM(sales.quantity * _cm_rank.[rank]) AS [sales___m]\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON (\n sales.product = _cm_rank.[product]\n OR (\n sales.product IS NULL AND _cm_rank.[product] IS NULL\n )\n )\nGROUP BY\n sales.region", "lifted/target_homed::bigquery": "WITH _base AS (\n SELECT\n customers.tier AS `orders___customers___tier`\n FROM orders AS orders\n LEFT JOIN customers AS customers\n ON orders.customer_id = customers.id\n GROUP BY\n customers.tier\n), _cm_orders__customers__customers_spend_customers_regions_pop_sum AS (\n SELECT\n customers.tier AS `customers___tier`,\n SUM(customers.spend - regions.pop) AS `customers___spend_regions_pop_sum`\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n GROUP BY\n customers.tier\n)\nSELECT\n _base.`orders___customers___tier`,\n _cm_orders__customers__customers_spend_customers_regions_pop_sum.`customers___spend_regions_pop_sum` AS `orders___m`\nFROM _base\nLEFT JOIN _cm_orders__customers__customers_spend_customers_regions_pop_sum\n ON _base.`orders___customers___tier` IS NOT DISTINCT FROM _cm_orders__customers__customers_spend_customers_regions_pop_sum.`customers___tier`", "lifted/target_homed::duckdb": "WITH _base AS (\n SELECT\n customers.tier AS \"orders.customers.tier\"\n FROM orders AS orders\n LEFT JOIN customers AS customers\n ON orders.customer_id = customers.id\n GROUP BY\n customers.tier\n), _cm_orders__customers__customers_spend_customers_regions_pop_sum AS (\n SELECT\n customers.tier AS \"customers.tier\",\n SUM(customers.spend - regions.pop) AS \"customers.spend_regions_pop_sum\"\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n GROUP BY\n customers.tier\n)\nSELECT\n _base.\"orders.customers.tier\",\n _cm_orders__customers__customers_spend_customers_regions_pop_sum.\"customers.spend_regions_pop_sum\" AS \"orders.m\"\nFROM _base\nLEFT JOIN _cm_orders__customers__customers_spend_customers_regions_pop_sum\n ON _base.\"orders.customers.tier\" IS NOT DISTINCT FROM _cm_orders__customers__customers_spend_customers_regions_pop_sum.\"customers.tier\"", "lifted/target_homed::mysql": "WITH _base AS (\n SELECT\n customers.tier AS `orders.customers.tier`\n FROM orders AS orders\n LEFT JOIN customers AS customers\n ON orders.customer_id = customers.id\n GROUP BY\n customers.tier\n), _cm_orders__customers__customers_spend_customers_regions_pop_sum AS (\n SELECT\n customers.tier AS `customers.tier`,\n SUM(customers.spend - regions.pop) AS `customers.spend_regions_pop_sum`\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n GROUP BY\n customers.tier\n)\nSELECT\n _base.`orders.customers.tier`,\n _cm_orders__customers__customers_spend_customers_regions_pop_sum.`customers.spend_regions_pop_sum` AS `orders.m`\nFROM _base\nLEFT JOIN _cm_orders__customers__customers_spend_customers_regions_pop_sum\n ON _base.`orders.customers.tier` <=> _cm_orders__customers__customers_spend_customers_regions_pop_sum.`customers.tier`", @@ -69,13 +69,13 @@ "lifted/two_branch::snowflake": "WITH _base AS (\n SELECT\n orders.status AS \"orders.status\"\n FROM orders AS orders\n GROUP BY\n orders.status\n), _cm_orders__customers_spend_stores_rent_sum AS (\n SELECT\n orders.status AS \"orders.status\",\n SUM(customers.spend - stores.rent) AS \"orders.m\"\n FROM orders AS orders\n LEFT JOIN customers AS customers\n ON orders.customer_id = customers.id\n LEFT JOIN stores AS stores\n ON orders.store_co = stores.co AND orders.store_no = stores.no\n GROUP BY\n orders.status\n)\nSELECT\n _base.\"orders.status\",\n _cm_orders__customers_spend_stores_rent_sum.\"orders.m\"\nFROM _base\nLEFT JOIN _cm_orders__customers_spend_stores_rent_sum\n ON _base.\"orders.status\" IS NOT DISTINCT FROM _cm_orders__customers_spend_stores_rent_sum.\"orders.status\"", "lifted/two_branch::sqlite": "WITH _base AS (\n SELECT\n orders.status AS \"orders.status\"\n FROM orders AS orders\n GROUP BY\n orders.status\n), _cm_orders__customers_spend_stores_rent_sum AS (\n SELECT\n orders.status AS \"orders.status\",\n SUM(customers.spend - stores.rent) AS \"orders.m\"\n FROM orders AS orders\n LEFT JOIN customers AS customers\n ON orders.customer_id = customers.id\n LEFT JOIN stores AS stores\n ON orders.store_co = stores.co AND orders.store_no = stores.no\n GROUP BY\n orders.status\n)\nSELECT\n _base.\"orders.status\",\n _cm_orders__customers_spend_stores_rent_sum.\"orders.m\"\nFROM _base\nLEFT JOIN _cm_orders__customers_spend_stores_rent_sum\n ON _base.\"orders.status\" IS _cm_orders__customers_spend_stores_rent_sum.\"orders.status\"", "lifted/two_branch::tsql": "WITH _base AS (\n SELECT\n orders.status AS [orders___status]\n FROM orders AS orders\n GROUP BY\n orders.status\n), _cm_orders__customers_spend_stores_rent_sum AS (\n SELECT\n orders.status AS [orders___status],\n SUM(customers.spend - stores.rent) AS [orders___m]\n FROM orders AS orders\n LEFT JOIN customers AS customers\n ON orders.customer_id = customers.id\n LEFT JOIN stores AS stores\n ON orders.store_co = stores.co AND orders.store_no = stores.no\n GROUP BY\n orders.status\n)\nSELECT\n _base.[orders___status],\n _cm_orders__customers_spend_stores_rent_sum.[orders___m]\nFROM _base\nLEFT JOIN _cm_orders__customers_spend_stores_rent_sum\n ON (\n _base.[orders___status] = _cm_orders__customers_spend_stores_rent_sum.[orders___status]\n OR (\n _base.[orders___status] IS NULL\n AND _cm_orders__customers_spend_stores_rent_sum.[orders___status] IS NULL\n )\n )", - "lifted/windowed_inner::bigquery": "WITH _base AS (\n SELECT\n DATE_TRUNC(monthly.ordered_at, MONTH) AS `monthly___ordered_at`\n FROM monthly AS monthly\n GROUP BY\n DATE_TRUNC(monthly.ordered_at, MONTH)\n), _base_2 AS (\n SELECT\n monthly.region AS `monthly___region`,\n DATE_TRUNC(monthly.ordered_at, MONTH) AS `monthly___ordered_at_month`\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n DATE_TRUNC(monthly.ordered_at, MONTH)\n), _cm_monthly__amount_sum_window_90d_partition_by_region AS (\n SELECT\n _base.`monthly___region`,\n _base.`monthly___ordered_at_month`,\n CAST(SUM(_src._w_value) AS FLOAT64) AS `monthly___amount_sum_window_90d_partition_by_region`\n FROM (\n SELECT\n monthly.region AS `monthly___region`,\n DATE_TRUNC(monthly.ordered_at, MONTH) AS `monthly___ordered_at_month`\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n DATE_TRUNC(monthly.ordered_at, MONTH)\n ) AS _base\n LEFT JOIN (\n SELECT\n monthly.region AS _w_dim_0,\n monthly.ordered_at AS _w_time,\n monthly.amount AS _w_value\n FROM monthly AS monthly\n ) AS _src\n ON _src._w_dim_0 IS NOT DISTINCT FROM _base.`monthly___region`\n AND _src._w_time >= TIMESTAMP_ADD(\n CAST(DATETIME_ADD(CAST(_base.`monthly___ordered_at_month` AS DATETIME), INTERVAL 1 MONTH) AS TIMESTAMP),\n INTERVAL -90 DAY\n )\n AND _src._w_time < CAST(DATETIME_ADD(CAST(_base.`monthly___ordered_at_month` AS DATETIME), INTERVAL 1 MONTH) AS TIMESTAMP)\n GROUP BY\n _base.`monthly___region`,\n _base.`monthly___ordered_at_month`\n), base_2 AS (\n SELECT\n _base_2.`monthly___region`,\n _base_2.`monthly___ordered_at_month`,\n _cm_monthly__amount_sum_window_90d_partition_by_region.`monthly___amount_sum_window_90d_partition_by_region` AS `monthly___amount_sum_window_90d_partition_by_region`\n FROM _base_2\n LEFT JOIN _cm_monthly__amount_sum_window_90d_partition_by_region\n ON _base_2.`monthly___region` IS NOT DISTINCT FROM _cm_monthly__amount_sum_window_90d_partition_by_region.`monthly___region`\n AND _base_2.`monthly___ordered_at_month` IS NOT DISTINCT FROM _cm_monthly__amount_sum_window_90d_partition_by_region.`monthly___ordered_at_month`\n), step1 AS (\n SELECT\n `monthly___region`,\n `monthly___ordered_at_month`,\n `monthly___amount_sum_window_90d_partition_by_region`,\n RANK() OVER (ORDER BY `monthly___amount_sum_window_90d_partition_by_region` DESC) AS `monthly___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`monthly___region` AS `region`,\n _stage_inner.`monthly___ordered_at_month` AS `ordered_at_month`,\n _stage_inner.`monthly___rank` AS `rank`\n FROM (\n SELECT\n `monthly___region`,\n `monthly___ordered_at_month`,\n `monthly___rank`\n FROM (\n SELECT\n `monthly___region`,\n `monthly___ordered_at_month`,\n `monthly___amount_sum_window_90d_partition_by_region`,\n `monthly___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_monthly___sum AS (\n SELECT\n _base.`monthly___ordered_at_month` AS `monthly___ordered_at_month`,\n SUM(_base._v) AS `monthly___m`\n FROM (\n SELECT\n DATE_TRUNC(monthly.ordered_at, MONTH) AS `monthly___ordered_at_month`,\n monthly.region AS _ek0,\n DATE_TRUNC(monthly.ordered_at, MONTH) AS _ek1,\n MAX(_cm_rank.`rank`) AS _v\n FROM monthly AS monthly\n LEFT JOIN _cm_rank\n ON monthly.region IS NOT DISTINCT FROM _cm_rank.`region`\n AND DATE_TRUNC(monthly.ordered_at, MONTH) IS NOT DISTINCT FROM _cm_rank.`ordered_at_month`\n GROUP BY\n DATE_TRUNC(monthly.ordered_at, MONTH),\n monthly.region,\n DATE_TRUNC(monthly.ordered_at, MONTH)\n ) AS _base\n GROUP BY\n _base.`monthly___ordered_at_month`\n)\nSELECT\n _base.`monthly___ordered_at`,\n _cm_monthly___sum.`monthly___m`\nFROM _base\nLEFT JOIN _cm_monthly___sum\n ON _base.`monthly___ordered_at` IS NOT DISTINCT FROM _cm_monthly___sum.`monthly___ordered_at_month`", - "lifted/windowed_inner::duckdb": "WITH _base AS (\n SELECT\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at\"\n FROM monthly AS monthly\n GROUP BY\n DATE_TRUNC('MONTH', monthly.ordered_at)\n), _base_2 AS (\n SELECT\n monthly.region AS \"monthly.region\",\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at_month\"\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n DATE_TRUNC('MONTH', monthly.ordered_at)\n), _cm_monthly__amount_sum_window_90d_partition_by_region AS (\n SELECT\n _base.\"monthly.region\",\n _base.\"monthly.ordered_at_month\",\n CAST(SUM(_src._w_value) AS DOUBLE) AS \"monthly.amount_sum_window_90d_partition_by_region\"\n FROM (\n SELECT\n monthly.region AS \"monthly.region\",\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at_month\"\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n DATE_TRUNC('MONTH', monthly.ordered_at)\n ) AS _base\n LEFT JOIN (\n SELECT\n monthly.region AS _w_dim_0,\n monthly.ordered_at AS _w_time,\n monthly.amount AS _w_value\n FROM monthly AS monthly\n ) AS _src\n ON _src._w_dim_0 IS NOT DISTINCT FROM _base.\"monthly.region\"\n AND _src._w_time >= _base.\"monthly.ordered_at_month\" + INTERVAL 1 MONTH - INTERVAL 90 DAY\n AND _src._w_time < _base.\"monthly.ordered_at_month\" + INTERVAL 1 MONTH\n GROUP BY\n _base.\"monthly.region\",\n _base.\"monthly.ordered_at_month\"\n), base_2 AS (\n SELECT\n _base_2.\"monthly.region\",\n _base_2.\"monthly.ordered_at_month\",\n _cm_monthly__amount_sum_window_90d_partition_by_region.\"monthly.amount_sum_window_90d_partition_by_region\" AS \"monthly.amount_sum_window_90d_partition_by_region\"\n FROM _base_2\n LEFT JOIN _cm_monthly__amount_sum_window_90d_partition_by_region\n ON _base_2.\"monthly.region\" IS NOT DISTINCT FROM _cm_monthly__amount_sum_window_90d_partition_by_region.\"monthly.region\"\n AND _base_2.\"monthly.ordered_at_month\" IS NOT DISTINCT FROM _cm_monthly__amount_sum_window_90d_partition_by_region.\"monthly.ordered_at_month\"\n), step1 AS (\n SELECT\n \"monthly.region\",\n \"monthly.ordered_at_month\",\n \"monthly.amount_sum_window_90d_partition_by_region\",\n RANK() OVER (ORDER BY \"monthly.amount_sum_window_90d_partition_by_region\" DESC) AS \"monthly.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"monthly.region\" AS \"region\",\n _stage_inner.\"monthly.ordered_at_month\" AS \"ordered_at_month\",\n _stage_inner.\"monthly.rank\" AS \"rank\"\n FROM (\n SELECT\n \"monthly.region\",\n \"monthly.ordered_at_month\",\n \"monthly.rank\"\n FROM (\n SELECT\n \"monthly.region\",\n \"monthly.ordered_at_month\",\n \"monthly.amount_sum_window_90d_partition_by_region\",\n \"monthly.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_monthly___sum AS (\n SELECT\n _base.\"monthly.ordered_at_month\" AS \"monthly.ordered_at_month\",\n SUM(_base._v) AS \"monthly.m\"\n FROM (\n SELECT\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at_month\",\n monthly.region AS _ek0,\n DATE_TRUNC('MONTH', monthly.ordered_at) AS _ek1,\n MAX(_cm_rank.\"rank\") AS _v\n FROM monthly AS monthly\n LEFT JOIN _cm_rank\n ON monthly.region IS NOT DISTINCT FROM _cm_rank.\"region\"\n AND DATE_TRUNC('MONTH', monthly.ordered_at) IS NOT DISTINCT FROM _cm_rank.\"ordered_at_month\"\n GROUP BY\n DATE_TRUNC('MONTH', monthly.ordered_at),\n monthly.region,\n DATE_TRUNC('MONTH', monthly.ordered_at)\n ) AS _base\n GROUP BY\n _base.\"monthly.ordered_at_month\"\n)\nSELECT\n _base.\"monthly.ordered_at\",\n _cm_monthly___sum.\"monthly.m\"\nFROM _base\nLEFT JOIN _cm_monthly___sum\n ON _base.\"monthly.ordered_at\" IS NOT DISTINCT FROM _cm_monthly___sum.\"monthly.ordered_at_month\"", - "lifted/windowed_inner::mysql": "WITH _base AS (\n SELECT\n STR_TO_DATE(CONCAT(YEAR(monthly.ordered_at), ' ', MONTH(monthly.ordered_at), ' 1'), '%Y %c %e') AS `monthly.ordered_at`\n FROM monthly AS monthly\n GROUP BY\n STR_TO_DATE(CONCAT(YEAR(monthly.ordered_at), ' ', MONTH(monthly.ordered_at), ' 1'), '%Y %c %e')\n), _base_2 AS (\n SELECT\n monthly.region AS `monthly.region`,\n STR_TO_DATE(CONCAT(YEAR(monthly.ordered_at), ' ', MONTH(monthly.ordered_at), ' 1'), '%Y %c %e') AS `monthly.ordered_at_month`\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n STR_TO_DATE(CONCAT(YEAR(monthly.ordered_at), ' ', MONTH(monthly.ordered_at), ' 1'), '%Y %c %e')\n), _cm_monthly__amount_sum_window_90d_partition_by_region AS (\n SELECT\n _base.`monthly.region`,\n _base.`monthly.ordered_at_month`,\n CAST(SUM(_src._w_value) AS DOUBLE) AS `monthly.amount_sum_window_90d_partition_by_region`\n FROM (\n SELECT\n monthly.region AS `monthly.region`,\n STR_TO_DATE(CONCAT(YEAR(monthly.ordered_at), ' ', MONTH(monthly.ordered_at), ' 1'), '%Y %c %e') AS `monthly.ordered_at_month`\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n STR_TO_DATE(CONCAT(YEAR(monthly.ordered_at), ' ', MONTH(monthly.ordered_at), ' 1'), '%Y %c %e')\n ) AS _base\n LEFT JOIN (\n SELECT\n monthly.region AS _w_dim_0,\n monthly.ordered_at AS _w_time,\n monthly.amount AS _w_value\n FROM monthly AS monthly\n ) AS _src\n ON _src._w_dim_0 <=> _base.`monthly.region`\n AND _src._w_time >= DATE_ADD(DATE_ADD(_base.`monthly.ordered_at_month`, INTERVAL 1 MONTH), INTERVAL -90 DAY)\n AND _src._w_time < DATE_ADD(_base.`monthly.ordered_at_month`, INTERVAL 1 MONTH)\n GROUP BY\n _base.`monthly.region`,\n _base.`monthly.ordered_at_month`\n), base_2 AS (\n SELECT\n _base_2.`monthly.region`,\n _base_2.`monthly.ordered_at_month`,\n _cm_monthly__amount_sum_window_90d_partition_by_region.`monthly.amount_sum_window_90d_partition_by_region` AS `monthly.amount_sum_window_90d_partition_by_region`\n FROM _base_2\n LEFT JOIN _cm_monthly__amount_sum_window_90d_partition_by_region\n ON _base_2.`monthly.region` <=> _cm_monthly__amount_sum_window_90d_partition_by_region.`monthly.region`\n AND _base_2.`monthly.ordered_at_month` <=> _cm_monthly__amount_sum_window_90d_partition_by_region.`monthly.ordered_at_month`\n), step1 AS (\n SELECT\n `monthly.region`,\n `monthly.ordered_at_month`,\n `monthly.amount_sum_window_90d_partition_by_region`,\n RANK() OVER (ORDER BY `monthly.amount_sum_window_90d_partition_by_region` DESC) AS `monthly.rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`monthly.region` AS `region`,\n _stage_inner.`monthly.ordered_at_month` AS `ordered_at_month`,\n _stage_inner.`monthly.rank` AS `rank`\n FROM (\n SELECT\n `monthly.region`,\n `monthly.ordered_at_month`,\n `monthly.rank`\n FROM (\n SELECT\n `monthly.region`,\n `monthly.ordered_at_month`,\n `monthly.amount_sum_window_90d_partition_by_region`,\n `monthly.rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_monthly___sum AS (\n SELECT\n _base.`monthly.ordered_at_month` AS `monthly.ordered_at_month`,\n SUM(_base._v) AS `monthly.m`\n FROM (\n SELECT\n STR_TO_DATE(CONCAT(YEAR(monthly.ordered_at), ' ', MONTH(monthly.ordered_at), ' 1'), '%Y %c %e') AS `monthly.ordered_at_month`,\n monthly.region AS _ek0,\n STR_TO_DATE(CONCAT(YEAR(monthly.ordered_at), ' ', MONTH(monthly.ordered_at), ' 1'), '%Y %c %e') AS _ek1,\n MAX(_cm_rank.`rank`) AS _v\n FROM monthly AS monthly\n LEFT JOIN _cm_rank\n ON monthly.region <=> _cm_rank.`region`\n AND STR_TO_DATE(CONCAT(YEAR(monthly.ordered_at), ' ', MONTH(monthly.ordered_at), ' 1'), '%Y %c %e') <=> _cm_rank.`ordered_at_month`\n GROUP BY\n STR_TO_DATE(CONCAT(YEAR(monthly.ordered_at), ' ', MONTH(monthly.ordered_at), ' 1'), '%Y %c %e'),\n monthly.region,\n STR_TO_DATE(CONCAT(YEAR(monthly.ordered_at), ' ', MONTH(monthly.ordered_at), ' 1'), '%Y %c %e')\n ) AS _base\n GROUP BY\n _base.`monthly.ordered_at_month`\n)\nSELECT\n _base.`monthly.ordered_at`,\n _cm_monthly___sum.`monthly.m`\nFROM _base\nLEFT JOIN _cm_monthly___sum\n ON _base.`monthly.ordered_at` <=> _cm_monthly___sum.`monthly.ordered_at_month`", - "lifted/windowed_inner::postgres": "WITH _base AS (\n SELECT\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at\"\n FROM monthly AS monthly\n GROUP BY\n DATE_TRUNC('MONTH', monthly.ordered_at)\n), _base_2 AS (\n SELECT\n monthly.region AS \"monthly.region\",\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at_month\"\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n DATE_TRUNC('MONTH', monthly.ordered_at)\n), _cm_monthly__amount_sum_window_90d_partition_by_region AS (\n SELECT\n _base.\"monthly.region\",\n _base.\"monthly.ordered_at_month\",\n CAST(SUM(_src._w_value) AS DOUBLE PRECISION) AS \"monthly.amount_sum_window_90d_partition_by_region\"\n FROM (\n SELECT\n monthly.region AS \"monthly.region\",\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at_month\"\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n DATE_TRUNC('MONTH', monthly.ordered_at)\n ) AS _base\n LEFT JOIN (\n SELECT\n monthly.region AS _w_dim_0,\n monthly.ordered_at AS _w_time,\n monthly.amount AS _w_value\n FROM monthly AS monthly\n ) AS _src\n ON _src._w_dim_0 IS NOT DISTINCT FROM _base.\"monthly.region\"\n AND _src._w_time >= _base.\"monthly.ordered_at_month\" + INTERVAL '1 MONTH' - INTERVAL '90 DAY'\n AND _src._w_time < _base.\"monthly.ordered_at_month\" + INTERVAL '1 MONTH'\n GROUP BY\n _base.\"monthly.region\",\n _base.\"monthly.ordered_at_month\"\n), base_2 AS (\n SELECT\n _base_2.\"monthly.region\",\n _base_2.\"monthly.ordered_at_month\",\n _cm_monthly__amount_sum_window_90d_partition_by_region.\"monthly.amount_sum_window_90d_partition_by_region\" AS \"monthly.amount_sum_window_90d_partition_by_region\"\n FROM _base_2\n LEFT JOIN _cm_monthly__amount_sum_window_90d_partition_by_region\n ON _base_2.\"monthly.region\" IS NOT DISTINCT FROM _cm_monthly__amount_sum_window_90d_partition_by_region.\"monthly.region\"\n AND _base_2.\"monthly.ordered_at_month\" IS NOT DISTINCT FROM _cm_monthly__amount_sum_window_90d_partition_by_region.\"monthly.ordered_at_month\"\n), step1 AS (\n SELECT\n \"monthly.region\",\n \"monthly.ordered_at_month\",\n \"monthly.amount_sum_window_90d_partition_by_region\",\n RANK() OVER (ORDER BY \"monthly.amount_sum_window_90d_partition_by_region\" DESC NULLS LAST) AS \"monthly.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"monthly.region\" AS \"region\",\n _stage_inner.\"monthly.ordered_at_month\" AS \"ordered_at_month\",\n _stage_inner.\"monthly.rank\" AS \"rank\"\n FROM (\n SELECT\n \"monthly.region\",\n \"monthly.ordered_at_month\",\n \"monthly.rank\"\n FROM (\n SELECT\n \"monthly.region\",\n \"monthly.ordered_at_month\",\n \"monthly.amount_sum_window_90d_partition_by_region\",\n \"monthly.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_monthly___sum AS (\n SELECT\n _base.\"monthly.ordered_at_month\" AS \"monthly.ordered_at_month\",\n SUM(_base._v) AS \"monthly.m\"\n FROM (\n SELECT\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at_month\",\n monthly.region AS _ek0,\n DATE_TRUNC('MONTH', monthly.ordered_at) AS _ek1,\n MAX(_cm_rank.\"rank\") AS _v\n FROM monthly AS monthly\n LEFT JOIN _cm_rank\n ON monthly.region IS NOT DISTINCT FROM _cm_rank.\"region\"\n AND DATE_TRUNC('MONTH', monthly.ordered_at) IS NOT DISTINCT FROM _cm_rank.\"ordered_at_month\"\n GROUP BY\n DATE_TRUNC('MONTH', monthly.ordered_at),\n monthly.region,\n DATE_TRUNC('MONTH', monthly.ordered_at)\n ) AS _base\n GROUP BY\n _base.\"monthly.ordered_at_month\"\n)\nSELECT\n _base.\"monthly.ordered_at\",\n _cm_monthly___sum.\"monthly.m\"\nFROM _base\nLEFT JOIN _cm_monthly___sum\n ON _base.\"monthly.ordered_at\" IS NOT DISTINCT FROM _cm_monthly___sum.\"monthly.ordered_at_month\"", - "lifted/windowed_inner::snowflake": "WITH _base AS (\n SELECT\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at\"\n FROM monthly AS monthly\n GROUP BY\n DATE_TRUNC('MONTH', monthly.ordered_at)\n), _base_2 AS (\n SELECT\n monthly.region AS \"monthly.region\",\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at_month\"\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n DATE_TRUNC('MONTH', monthly.ordered_at)\n), _cm_monthly__amount_sum_window_90d_partition_by_region AS (\n SELECT\n _base.\"monthly.region\",\n _base.\"monthly.ordered_at_month\",\n CAST(SUM(_src._w_value) AS DOUBLE) AS \"monthly.amount_sum_window_90d_partition_by_region\"\n FROM (\n SELECT\n monthly.region AS \"monthly.region\",\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at_month\"\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n DATE_TRUNC('MONTH', monthly.ordered_at)\n ) AS _base\n LEFT JOIN (\n SELECT\n monthly.region AS _w_dim_0,\n monthly.ordered_at AS _w_time,\n monthly.amount AS _w_value\n FROM monthly AS monthly\n ) AS _src\n ON _src._w_dim_0 IS NOT DISTINCT FROM _base.\"monthly.region\"\n AND _src._w_time >= DATEADD(DAY, -90, DATEADD(MONTH, 1, _base.\"monthly.ordered_at_month\"))\n AND _src._w_time < DATEADD(MONTH, 1, _base.\"monthly.ordered_at_month\")\n GROUP BY\n _base.\"monthly.region\",\n _base.\"monthly.ordered_at_month\"\n), base_2 AS (\n SELECT\n _base_2.\"monthly.region\",\n _base_2.\"monthly.ordered_at_month\",\n _cm_monthly__amount_sum_window_90d_partition_by_region.\"monthly.amount_sum_window_90d_partition_by_region\" AS \"monthly.amount_sum_window_90d_partition_by_region\"\n FROM _base_2\n LEFT JOIN _cm_monthly__amount_sum_window_90d_partition_by_region\n ON _base_2.\"monthly.region\" IS NOT DISTINCT FROM _cm_monthly__amount_sum_window_90d_partition_by_region.\"monthly.region\"\n AND _base_2.\"monthly.ordered_at_month\" IS NOT DISTINCT FROM _cm_monthly__amount_sum_window_90d_partition_by_region.\"monthly.ordered_at_month\"\n), step1 AS (\n SELECT\n \"monthly.region\",\n \"monthly.ordered_at_month\",\n \"monthly.amount_sum_window_90d_partition_by_region\",\n RANK() OVER (ORDER BY \"monthly.amount_sum_window_90d_partition_by_region\" DESC NULLS LAST) AS \"monthly.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"monthly.region\" AS \"region\",\n _stage_inner.\"monthly.ordered_at_month\" AS \"ordered_at_month\",\n _stage_inner.\"monthly.rank\" AS \"rank\"\n FROM (\n SELECT\n \"monthly.region\",\n \"monthly.ordered_at_month\",\n \"monthly.rank\"\n FROM (\n SELECT\n \"monthly.region\",\n \"monthly.ordered_at_month\",\n \"monthly.amount_sum_window_90d_partition_by_region\",\n \"monthly.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_monthly___sum AS (\n SELECT\n _base.\"monthly.ordered_at_month\" AS \"monthly.ordered_at_month\",\n SUM(_base._v) AS \"monthly.m\"\n FROM (\n SELECT\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at_month\",\n monthly.region AS _ek0,\n DATE_TRUNC('MONTH', monthly.ordered_at) AS _ek1,\n MAX(_cm_rank.\"rank\") AS _v\n FROM monthly AS monthly\n LEFT JOIN _cm_rank\n ON monthly.region IS NOT DISTINCT FROM _cm_rank.\"region\"\n AND DATE_TRUNC('MONTH', monthly.ordered_at) IS NOT DISTINCT FROM _cm_rank.\"ordered_at_month\"\n GROUP BY\n DATE_TRUNC('MONTH', monthly.ordered_at),\n monthly.region,\n DATE_TRUNC('MONTH', monthly.ordered_at)\n ) AS _base\n GROUP BY\n _base.\"monthly.ordered_at_month\"\n)\nSELECT\n _base.\"monthly.ordered_at\",\n _cm_monthly___sum.\"monthly.m\"\nFROM _base\nLEFT JOIN _cm_monthly___sum\n ON _base.\"monthly.ordered_at\" IS NOT DISTINCT FROM _cm_monthly___sum.\"monthly.ordered_at_month\"", - "lifted/windowed_inner::sqlite": "WITH _base AS (\n SELECT\n STRFTIME('%Y-%m-01', monthly.ordered_at) AS \"monthly.ordered_at\"\n FROM monthly AS monthly\n GROUP BY\n STRFTIME('%Y-%m-01', monthly.ordered_at)\n), _base_2 AS (\n SELECT\n monthly.region AS \"monthly.region\",\n STRFTIME('%Y-%m-01', monthly.ordered_at) AS \"monthly.ordered_at_month\"\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n STRFTIME('%Y-%m-01', monthly.ordered_at)\n), _cm_monthly__amount_sum_window_90d_partition_by_region AS (\n SELECT\n _base.\"monthly.region\",\n _base.\"monthly.ordered_at_month\",\n CAST(SUM(_src._w_value) AS REAL) AS \"monthly.amount_sum_window_90d_partition_by_region\"\n FROM (\n SELECT\n monthly.region AS \"monthly.region\",\n STRFTIME('%Y-%m-01', monthly.ordered_at) AS \"monthly.ordered_at_month\"\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n STRFTIME('%Y-%m-01', monthly.ordered_at)\n ) AS _base\n LEFT JOIN (\n SELECT\n monthly.region AS _w_dim_0,\n monthly.ordered_at AS _w_time,\n monthly.amount AS _w_value\n FROM monthly AS monthly\n ) AS _src\n ON _src._w_dim_0 IS _base.\"monthly.region\"\n AND DATETIME(_src._w_time) >= SLAYER_DATE_ADD(SLAYER_DATE_ADD(_base.\"monthly.ordered_at_month\", 1, 'month'), -90, 'day')\n AND DATETIME(_src._w_time) < SLAYER_DATE_ADD(_base.\"monthly.ordered_at_month\", 1, 'month')\n GROUP BY\n _base.\"monthly.region\",\n _base.\"monthly.ordered_at_month\"\n), base_2 AS (\n SELECT\n _base_2.\"monthly.region\",\n _base_2.\"monthly.ordered_at_month\",\n _cm_monthly__amount_sum_window_90d_partition_by_region.\"monthly.amount_sum_window_90d_partition_by_region\" AS \"monthly.amount_sum_window_90d_partition_by_region\"\n FROM _base_2\n LEFT JOIN _cm_monthly__amount_sum_window_90d_partition_by_region\n ON _base_2.\"monthly.region\" IS _cm_monthly__amount_sum_window_90d_partition_by_region.\"monthly.region\"\n AND _base_2.\"monthly.ordered_at_month\" IS _cm_monthly__amount_sum_window_90d_partition_by_region.\"monthly.ordered_at_month\"\n), step1 AS (\n SELECT\n \"monthly.region\",\n \"monthly.ordered_at_month\",\n \"monthly.amount_sum_window_90d_partition_by_region\",\n RANK() OVER (ORDER BY \"monthly.amount_sum_window_90d_partition_by_region\" DESC) AS \"monthly.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"monthly.region\" AS \"region\",\n _stage_inner.\"monthly.ordered_at_month\" AS \"ordered_at_month\",\n _stage_inner.\"monthly.rank\" AS \"rank\"\n FROM (\n SELECT\n \"monthly.region\",\n \"monthly.ordered_at_month\",\n \"monthly.rank\"\n FROM (\n SELECT\n \"monthly.region\",\n \"monthly.ordered_at_month\",\n \"monthly.amount_sum_window_90d_partition_by_region\",\n \"monthly.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_monthly___sum AS (\n SELECT\n _base.\"monthly.ordered_at_month\" AS \"monthly.ordered_at_month\",\n SUM(_base._v) AS \"monthly.m\"\n FROM (\n SELECT\n STRFTIME('%Y-%m-01', monthly.ordered_at) AS \"monthly.ordered_at_month\",\n monthly.region AS _ek0,\n STRFTIME('%Y-%m-01', monthly.ordered_at) AS _ek1,\n MAX(_cm_rank.\"rank\") AS _v\n FROM monthly AS monthly\n LEFT JOIN _cm_rank\n ON monthly.region IS _cm_rank.\"region\"\n AND STRFTIME('%Y-%m-01', monthly.ordered_at) IS _cm_rank.\"ordered_at_month\"\n GROUP BY\n STRFTIME('%Y-%m-01', monthly.ordered_at),\n monthly.region,\n STRFTIME('%Y-%m-01', monthly.ordered_at)\n ) AS _base\n GROUP BY\n _base.\"monthly.ordered_at_month\"\n)\nSELECT\n _base.\"monthly.ordered_at\",\n _cm_monthly___sum.\"monthly.m\"\nFROM _base\nLEFT JOIN _cm_monthly___sum\n ON _base.\"monthly.ordered_at\" IS _cm_monthly___sum.\"monthly.ordered_at_month\"", - "lifted/windowed_inner::tsql": "WITH _base AS (\n SELECT\n DATETRUNC(month, monthly.ordered_at) AS [monthly___ordered_at]\n FROM monthly AS monthly\n GROUP BY\n DATETRUNC(month, monthly.ordered_at)\n), _base_2 AS (\n SELECT\n monthly.region AS [monthly___region],\n DATETRUNC(month, monthly.ordered_at) AS [monthly___ordered_at_month]\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n DATETRUNC(month, monthly.ordered_at)\n), _cm_monthly__amount_sum_window_90d_partition_by_region AS (\n SELECT\n _base.[monthly___region] AS [monthly___region],\n _base.[monthly___ordered_at_month] AS [monthly___ordered_at_month],\n CAST(SUM(_src._w_value) AS FLOAT) AS [monthly___amount_sum_window_90d_partition_by_region]\n FROM (\n SELECT\n monthly.region AS [monthly___region],\n DATETRUNC(month, monthly.ordered_at) AS [monthly___ordered_at_month]\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n DATETRUNC(month, monthly.ordered_at)\n ) AS _base\n LEFT JOIN (\n SELECT\n monthly.region AS _w_dim_0,\n monthly.ordered_at AS _w_time,\n monthly.amount AS _w_value\n FROM monthly AS monthly\n ) AS _src\n ON (\n _src._w_dim_0 = _base.[monthly___region]\n OR (\n _src._w_dim_0 IS NULL AND _base.[monthly___region] IS NULL\n )\n )\n AND _src._w_time >= DATEADD(DAY, -90, DATEADD(MONTH, 1, _base.[monthly___ordered_at_month]))\n AND _src._w_time < DATEADD(MONTH, 1, _base.[monthly___ordered_at_month])\n GROUP BY\n _base.[monthly___region],\n _base.[monthly___ordered_at_month]\n), base_2 AS (\n SELECT\n _base_2.[monthly___region] AS [monthly___region],\n _base_2.[monthly___ordered_at_month] AS [monthly___ordered_at_month],\n _cm_monthly__amount_sum_window_90d_partition_by_region.[monthly___amount_sum_window_90d_partition_by_region] AS [monthly___amount_sum_window_90d_partition_by_region]\n FROM _base_2\n LEFT JOIN _cm_monthly__amount_sum_window_90d_partition_by_region\n ON (\n _base_2.[monthly___region] = _cm_monthly__amount_sum_window_90d_partition_by_region.[monthly___region]\n OR (\n _base_2.[monthly___region] IS NULL\n AND _cm_monthly__amount_sum_window_90d_partition_by_region.[monthly___region] IS NULL\n )\n )\n AND (\n _base_2.[monthly___ordered_at_month] = _cm_monthly__amount_sum_window_90d_partition_by_region.[monthly___ordered_at_month]\n OR (\n _base_2.[monthly___ordered_at_month] IS NULL\n AND _cm_monthly__amount_sum_window_90d_partition_by_region.[monthly___ordered_at_month] IS NULL\n )\n )\n), step1 AS (\n SELECT\n [monthly___region] AS [monthly___region],\n [monthly___ordered_at_month] AS [monthly___ordered_at_month],\n [monthly___amount_sum_window_90d_partition_by_region] AS [monthly___amount_sum_window_90d_partition_by_region],\n RANK() OVER (ORDER BY [monthly___amount_sum_window_90d_partition_by_region] DESC) AS [monthly___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[monthly___region] AS [region],\n _stage_inner.[monthly___ordered_at_month] AS [ordered_at_month],\n _stage_inner.[monthly___rank] AS [rank]\n FROM (\n SELECT\n [monthly___region] AS [monthly___region],\n [monthly___ordered_at_month] AS [monthly___ordered_at_month],\n [monthly___rank] AS [monthly___rank]\n FROM (\n SELECT\n [monthly___region],\n [monthly___ordered_at_month],\n [monthly___amount_sum_window_90d_partition_by_region],\n [monthly___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_monthly___sum AS (\n SELECT\n _base.[monthly___ordered_at_month] AS [monthly___ordered_at_month],\n SUM(_base._v) AS [monthly___m]\n FROM (\n SELECT\n DATETRUNC(month, monthly.ordered_at) AS [monthly___ordered_at_month],\n monthly.region AS _ek0,\n DATETRUNC(month, monthly.ordered_at) AS _ek1,\n MAX(_cm_rank.[rank]) AS _v\n FROM monthly AS monthly\n LEFT JOIN _cm_rank\n ON (\n monthly.region = _cm_rank.[region]\n OR (\n monthly.region IS NULL AND _cm_rank.[region] IS NULL\n )\n )\n AND (\n DATETRUNC(month, monthly.ordered_at) = _cm_rank.[ordered_at_month]\n OR (\n DATETRUNC(month, monthly.ordered_at) IS NULL\n AND _cm_rank.[ordered_at_month] IS NULL\n )\n )\n GROUP BY\n DATETRUNC(month, monthly.ordered_at),\n monthly.region,\n DATETRUNC(month, monthly.ordered_at)\n ) AS _base\n GROUP BY\n _base.[monthly___ordered_at_month]\n)\nSELECT\n _base.[monthly___ordered_at],\n _cm_monthly___sum.[monthly___m]\nFROM _base\nLEFT JOIN _cm_monthly___sum\n ON (\n _base.[monthly___ordered_at] = _cm_monthly___sum.[monthly___ordered_at_month]\n OR (\n _base.[monthly___ordered_at] IS NULL\n AND _cm_monthly___sum.[monthly___ordered_at_month] IS NULL\n )\n )", + "lifted/windowed_inner::bigquery": "WITH _base AS (\n SELECT\n DATE_TRUNC(monthly.ordered_at, MONTH) AS `monthly___ordered_at`\n FROM monthly AS monthly\n GROUP BY\n DATE_TRUNC(monthly.ordered_at, MONTH)\n), _base_2 AS (\n SELECT\n monthly.region AS `monthly___region`,\n DATE_TRUNC(monthly.ordered_at, MONTH) AS `monthly___ordered_at_month`\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n DATE_TRUNC(monthly.ordered_at, MONTH)\n), _cm_monthly__amount_sum_window_90d_partition_by_region AS (\n SELECT\n _base.`monthly___region`,\n _base.`monthly___ordered_at_month`,\n CAST(SUM(_src._w_value) AS FLOAT64) AS `monthly___amount_sum_window_90d_partition_by_region`\n FROM (\n SELECT\n monthly.region AS `monthly___region`,\n DATE_TRUNC(monthly.ordered_at, MONTH) AS `monthly___ordered_at_month`\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n DATE_TRUNC(monthly.ordered_at, MONTH)\n ) AS _base\n LEFT JOIN (\n SELECT\n monthly.region AS _w_dim_0,\n monthly.ordered_at AS _w_time,\n monthly.amount AS _w_value\n FROM monthly AS monthly\n ) AS _src\n ON _src._w_dim_0 IS NOT DISTINCT FROM _base.`monthly___region`\n AND _src._w_time >= TIMESTAMP_ADD(\n CAST(DATETIME_ADD(CAST(_base.`monthly___ordered_at_month` AS DATETIME), INTERVAL 1 MONTH) AS TIMESTAMP),\n INTERVAL -90 DAY\n )\n AND _src._w_time < CAST(DATETIME_ADD(CAST(_base.`monthly___ordered_at_month` AS DATETIME), INTERVAL 1 MONTH) AS TIMESTAMP)\n GROUP BY\n _base.`monthly___region`,\n _base.`monthly___ordered_at_month`\n), base_2 AS (\n SELECT\n _base_2.`monthly___region`,\n _base_2.`monthly___ordered_at_month`,\n _cm_monthly__amount_sum_window_90d_partition_by_region.`monthly___amount_sum_window_90d_partition_by_region` AS `monthly___amount_sum_window_90d_partition_by_region`\n FROM _base_2\n LEFT JOIN _cm_monthly__amount_sum_window_90d_partition_by_region\n ON _base_2.`monthly___region` IS NOT DISTINCT FROM _cm_monthly__amount_sum_window_90d_partition_by_region.`monthly___region`\n AND _base_2.`monthly___ordered_at_month` IS NOT DISTINCT FROM _cm_monthly__amount_sum_window_90d_partition_by_region.`monthly___ordered_at_month`\n), step1 AS (\n SELECT\n `monthly___region`,\n `monthly___ordered_at_month`,\n `monthly___amount_sum_window_90d_partition_by_region`,\n CASE\n WHEN `monthly___amount_sum_window_90d_partition_by_region` IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN `monthly___amount_sum_window_90d_partition_by_region` IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY `monthly___amount_sum_window_90d_partition_by_region` DESC\n )\n END AS `monthly___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`monthly___region` AS `region`,\n _stage_inner.`monthly___ordered_at_month` AS `ordered_at_month`,\n _stage_inner.`monthly___rank` AS `rank`\n FROM (\n SELECT\n `monthly___region`,\n `monthly___ordered_at_month`,\n `monthly___rank`\n FROM (\n SELECT\n `monthly___region`,\n `monthly___ordered_at_month`,\n `monthly___amount_sum_window_90d_partition_by_region`,\n `monthly___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_monthly___sum AS (\n SELECT\n _base.`monthly___ordered_at_month` AS `monthly___ordered_at_month`,\n SUM(_base._v) AS `monthly___m`\n FROM (\n SELECT\n DATE_TRUNC(monthly.ordered_at, MONTH) AS `monthly___ordered_at_month`,\n monthly.region AS _ek0,\n DATE_TRUNC(monthly.ordered_at, MONTH) AS _ek1,\n MAX(_cm_rank.`rank`) AS _v\n FROM monthly AS monthly\n LEFT JOIN _cm_rank\n ON monthly.region IS NOT DISTINCT FROM _cm_rank.`region`\n AND DATE_TRUNC(monthly.ordered_at, MONTH) IS NOT DISTINCT FROM _cm_rank.`ordered_at_month`\n GROUP BY\n DATE_TRUNC(monthly.ordered_at, MONTH),\n monthly.region,\n DATE_TRUNC(monthly.ordered_at, MONTH)\n ) AS _base\n GROUP BY\n _base.`monthly___ordered_at_month`\n)\nSELECT\n _base.`monthly___ordered_at`,\n _cm_monthly___sum.`monthly___m`\nFROM _base\nLEFT JOIN _cm_monthly___sum\n ON _base.`monthly___ordered_at` IS NOT DISTINCT FROM _cm_monthly___sum.`monthly___ordered_at_month`", + "lifted/windowed_inner::duckdb": "WITH _base AS (\n SELECT\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at\"\n FROM monthly AS monthly\n GROUP BY\n DATE_TRUNC('MONTH', monthly.ordered_at)\n), _base_2 AS (\n SELECT\n monthly.region AS \"monthly.region\",\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at_month\"\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n DATE_TRUNC('MONTH', monthly.ordered_at)\n), _cm_monthly__amount_sum_window_90d_partition_by_region AS (\n SELECT\n _base.\"monthly.region\",\n _base.\"monthly.ordered_at_month\",\n CAST(SUM(_src._w_value) AS DOUBLE) AS \"monthly.amount_sum_window_90d_partition_by_region\"\n FROM (\n SELECT\n monthly.region AS \"monthly.region\",\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at_month\"\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n DATE_TRUNC('MONTH', monthly.ordered_at)\n ) AS _base\n LEFT JOIN (\n SELECT\n monthly.region AS _w_dim_0,\n monthly.ordered_at AS _w_time,\n monthly.amount AS _w_value\n FROM monthly AS monthly\n ) AS _src\n ON _src._w_dim_0 IS NOT DISTINCT FROM _base.\"monthly.region\"\n AND _src._w_time >= _base.\"monthly.ordered_at_month\" + INTERVAL 1 MONTH - INTERVAL 90 DAY\n AND _src._w_time < _base.\"monthly.ordered_at_month\" + INTERVAL 1 MONTH\n GROUP BY\n _base.\"monthly.region\",\n _base.\"monthly.ordered_at_month\"\n), base_2 AS (\n SELECT\n _base_2.\"monthly.region\",\n _base_2.\"monthly.ordered_at_month\",\n _cm_monthly__amount_sum_window_90d_partition_by_region.\"monthly.amount_sum_window_90d_partition_by_region\" AS \"monthly.amount_sum_window_90d_partition_by_region\"\n FROM _base_2\n LEFT JOIN _cm_monthly__amount_sum_window_90d_partition_by_region\n ON _base_2.\"monthly.region\" IS NOT DISTINCT FROM _cm_monthly__amount_sum_window_90d_partition_by_region.\"monthly.region\"\n AND _base_2.\"monthly.ordered_at_month\" IS NOT DISTINCT FROM _cm_monthly__amount_sum_window_90d_partition_by_region.\"monthly.ordered_at_month\"\n), step1 AS (\n SELECT\n \"monthly.region\",\n \"monthly.ordered_at_month\",\n \"monthly.amount_sum_window_90d_partition_by_region\",\n CASE\n WHEN \"monthly.amount_sum_window_90d_partition_by_region\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"monthly.amount_sum_window_90d_partition_by_region\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"monthly.amount_sum_window_90d_partition_by_region\" DESC\n )\n END AS \"monthly.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"monthly.region\" AS \"region\",\n _stage_inner.\"monthly.ordered_at_month\" AS \"ordered_at_month\",\n _stage_inner.\"monthly.rank\" AS \"rank\"\n FROM (\n SELECT\n \"monthly.region\",\n \"monthly.ordered_at_month\",\n \"monthly.rank\"\n FROM (\n SELECT\n \"monthly.region\",\n \"monthly.ordered_at_month\",\n \"monthly.amount_sum_window_90d_partition_by_region\",\n \"monthly.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_monthly___sum AS (\n SELECT\n _base.\"monthly.ordered_at_month\" AS \"monthly.ordered_at_month\",\n SUM(_base._v) AS \"monthly.m\"\n FROM (\n SELECT\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at_month\",\n monthly.region AS _ek0,\n DATE_TRUNC('MONTH', monthly.ordered_at) AS _ek1,\n MAX(_cm_rank.\"rank\") AS _v\n FROM monthly AS monthly\n LEFT JOIN _cm_rank\n ON monthly.region IS NOT DISTINCT FROM _cm_rank.\"region\"\n AND DATE_TRUNC('MONTH', monthly.ordered_at) IS NOT DISTINCT FROM _cm_rank.\"ordered_at_month\"\n GROUP BY\n DATE_TRUNC('MONTH', monthly.ordered_at),\n monthly.region,\n DATE_TRUNC('MONTH', monthly.ordered_at)\n ) AS _base\n GROUP BY\n _base.\"monthly.ordered_at_month\"\n)\nSELECT\n _base.\"monthly.ordered_at\",\n _cm_monthly___sum.\"monthly.m\"\nFROM _base\nLEFT JOIN _cm_monthly___sum\n ON _base.\"monthly.ordered_at\" IS NOT DISTINCT FROM _cm_monthly___sum.\"monthly.ordered_at_month\"", + "lifted/windowed_inner::mysql": "WITH _base AS (\n SELECT\n STR_TO_DATE(CONCAT(YEAR(monthly.ordered_at), ' ', MONTH(monthly.ordered_at), ' 1'), '%Y %c %e') AS `monthly.ordered_at`\n FROM monthly AS monthly\n GROUP BY\n STR_TO_DATE(CONCAT(YEAR(monthly.ordered_at), ' ', MONTH(monthly.ordered_at), ' 1'), '%Y %c %e')\n), _base_2 AS (\n SELECT\n monthly.region AS `monthly.region`,\n STR_TO_DATE(CONCAT(YEAR(monthly.ordered_at), ' ', MONTH(monthly.ordered_at), ' 1'), '%Y %c %e') AS `monthly.ordered_at_month`\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n STR_TO_DATE(CONCAT(YEAR(monthly.ordered_at), ' ', MONTH(monthly.ordered_at), ' 1'), '%Y %c %e')\n), _cm_monthly__amount_sum_window_90d_partition_by_region AS (\n SELECT\n _base.`monthly.region`,\n _base.`monthly.ordered_at_month`,\n CAST(SUM(_src._w_value) AS DOUBLE) AS `monthly.amount_sum_window_90d_partition_by_region`\n FROM (\n SELECT\n monthly.region AS `monthly.region`,\n STR_TO_DATE(CONCAT(YEAR(monthly.ordered_at), ' ', MONTH(monthly.ordered_at), ' 1'), '%Y %c %e') AS `monthly.ordered_at_month`\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n STR_TO_DATE(CONCAT(YEAR(monthly.ordered_at), ' ', MONTH(monthly.ordered_at), ' 1'), '%Y %c %e')\n ) AS _base\n LEFT JOIN (\n SELECT\n monthly.region AS _w_dim_0,\n monthly.ordered_at AS _w_time,\n monthly.amount AS _w_value\n FROM monthly AS monthly\n ) AS _src\n ON _src._w_dim_0 <=> _base.`monthly.region`\n AND _src._w_time >= DATE_ADD(DATE_ADD(_base.`monthly.ordered_at_month`, INTERVAL 1 MONTH), INTERVAL -90 DAY)\n AND _src._w_time < DATE_ADD(_base.`monthly.ordered_at_month`, INTERVAL 1 MONTH)\n GROUP BY\n _base.`monthly.region`,\n _base.`monthly.ordered_at_month`\n), base_2 AS (\n SELECT\n _base_2.`monthly.region`,\n _base_2.`monthly.ordered_at_month`,\n _cm_monthly__amount_sum_window_90d_partition_by_region.`monthly.amount_sum_window_90d_partition_by_region` AS `monthly.amount_sum_window_90d_partition_by_region`\n FROM _base_2\n LEFT JOIN _cm_monthly__amount_sum_window_90d_partition_by_region\n ON _base_2.`monthly.region` <=> _cm_monthly__amount_sum_window_90d_partition_by_region.`monthly.region`\n AND _base_2.`monthly.ordered_at_month` <=> _cm_monthly__amount_sum_window_90d_partition_by_region.`monthly.ordered_at_month`\n), step1 AS (\n SELECT\n `monthly.region`,\n `monthly.ordered_at_month`,\n `monthly.amount_sum_window_90d_partition_by_region`,\n CASE\n WHEN `monthly.amount_sum_window_90d_partition_by_region` IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN `monthly.amount_sum_window_90d_partition_by_region` IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY `monthly.amount_sum_window_90d_partition_by_region` DESC\n )\n END AS `monthly.rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`monthly.region` AS `region`,\n _stage_inner.`monthly.ordered_at_month` AS `ordered_at_month`,\n _stage_inner.`monthly.rank` AS `rank`\n FROM (\n SELECT\n `monthly.region`,\n `monthly.ordered_at_month`,\n `monthly.rank`\n FROM (\n SELECT\n `monthly.region`,\n `monthly.ordered_at_month`,\n `monthly.amount_sum_window_90d_partition_by_region`,\n `monthly.rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_monthly___sum AS (\n SELECT\n _base.`monthly.ordered_at_month` AS `monthly.ordered_at_month`,\n SUM(_base._v) AS `monthly.m`\n FROM (\n SELECT\n STR_TO_DATE(CONCAT(YEAR(monthly.ordered_at), ' ', MONTH(monthly.ordered_at), ' 1'), '%Y %c %e') AS `monthly.ordered_at_month`,\n monthly.region AS _ek0,\n STR_TO_DATE(CONCAT(YEAR(monthly.ordered_at), ' ', MONTH(monthly.ordered_at), ' 1'), '%Y %c %e') AS _ek1,\n MAX(_cm_rank.`rank`) AS _v\n FROM monthly AS monthly\n LEFT JOIN _cm_rank\n ON monthly.region <=> _cm_rank.`region`\n AND STR_TO_DATE(CONCAT(YEAR(monthly.ordered_at), ' ', MONTH(monthly.ordered_at), ' 1'), '%Y %c %e') <=> _cm_rank.`ordered_at_month`\n GROUP BY\n STR_TO_DATE(CONCAT(YEAR(monthly.ordered_at), ' ', MONTH(monthly.ordered_at), ' 1'), '%Y %c %e'),\n monthly.region,\n STR_TO_DATE(CONCAT(YEAR(monthly.ordered_at), ' ', MONTH(monthly.ordered_at), ' 1'), '%Y %c %e')\n ) AS _base\n GROUP BY\n _base.`monthly.ordered_at_month`\n)\nSELECT\n _base.`monthly.ordered_at`,\n _cm_monthly___sum.`monthly.m`\nFROM _base\nLEFT JOIN _cm_monthly___sum\n ON _base.`monthly.ordered_at` <=> _cm_monthly___sum.`monthly.ordered_at_month`", + "lifted/windowed_inner::postgres": "WITH _base AS (\n SELECT\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at\"\n FROM monthly AS monthly\n GROUP BY\n DATE_TRUNC('MONTH', monthly.ordered_at)\n), _base_2 AS (\n SELECT\n monthly.region AS \"monthly.region\",\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at_month\"\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n DATE_TRUNC('MONTH', monthly.ordered_at)\n), _cm_monthly__amount_sum_window_90d_partition_by_region AS (\n SELECT\n _base.\"monthly.region\",\n _base.\"monthly.ordered_at_month\",\n CAST(SUM(_src._w_value) AS DOUBLE PRECISION) AS \"monthly.amount_sum_window_90d_partition_by_region\"\n FROM (\n SELECT\n monthly.region AS \"monthly.region\",\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at_month\"\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n DATE_TRUNC('MONTH', monthly.ordered_at)\n ) AS _base\n LEFT JOIN (\n SELECT\n monthly.region AS _w_dim_0,\n monthly.ordered_at AS _w_time,\n monthly.amount AS _w_value\n FROM monthly AS monthly\n ) AS _src\n ON _src._w_dim_0 IS NOT DISTINCT FROM _base.\"monthly.region\"\n AND _src._w_time >= _base.\"monthly.ordered_at_month\" + INTERVAL '1 MONTH' - INTERVAL '90 DAY'\n AND _src._w_time < _base.\"monthly.ordered_at_month\" + INTERVAL '1 MONTH'\n GROUP BY\n _base.\"monthly.region\",\n _base.\"monthly.ordered_at_month\"\n), base_2 AS (\n SELECT\n _base_2.\"monthly.region\",\n _base_2.\"monthly.ordered_at_month\",\n _cm_monthly__amount_sum_window_90d_partition_by_region.\"monthly.amount_sum_window_90d_partition_by_region\" AS \"monthly.amount_sum_window_90d_partition_by_region\"\n FROM _base_2\n LEFT JOIN _cm_monthly__amount_sum_window_90d_partition_by_region\n ON _base_2.\"monthly.region\" IS NOT DISTINCT FROM _cm_monthly__amount_sum_window_90d_partition_by_region.\"monthly.region\"\n AND _base_2.\"monthly.ordered_at_month\" IS NOT DISTINCT FROM _cm_monthly__amount_sum_window_90d_partition_by_region.\"monthly.ordered_at_month\"\n), step1 AS (\n SELECT\n \"monthly.region\",\n \"monthly.ordered_at_month\",\n \"monthly.amount_sum_window_90d_partition_by_region\",\n CASE\n WHEN \"monthly.amount_sum_window_90d_partition_by_region\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"monthly.amount_sum_window_90d_partition_by_region\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"monthly.amount_sum_window_90d_partition_by_region\" DESC\n )\n END AS \"monthly.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"monthly.region\" AS \"region\",\n _stage_inner.\"monthly.ordered_at_month\" AS \"ordered_at_month\",\n _stage_inner.\"monthly.rank\" AS \"rank\"\n FROM (\n SELECT\n \"monthly.region\",\n \"monthly.ordered_at_month\",\n \"monthly.rank\"\n FROM (\n SELECT\n \"monthly.region\",\n \"monthly.ordered_at_month\",\n \"monthly.amount_sum_window_90d_partition_by_region\",\n \"monthly.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_monthly___sum AS (\n SELECT\n _base.\"monthly.ordered_at_month\" AS \"monthly.ordered_at_month\",\n SUM(_base._v) AS \"monthly.m\"\n FROM (\n SELECT\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at_month\",\n monthly.region AS _ek0,\n DATE_TRUNC('MONTH', monthly.ordered_at) AS _ek1,\n MAX(_cm_rank.\"rank\") AS _v\n FROM monthly AS monthly\n LEFT JOIN _cm_rank\n ON monthly.region IS NOT DISTINCT FROM _cm_rank.\"region\"\n AND DATE_TRUNC('MONTH', monthly.ordered_at) IS NOT DISTINCT FROM _cm_rank.\"ordered_at_month\"\n GROUP BY\n DATE_TRUNC('MONTH', monthly.ordered_at),\n monthly.region,\n DATE_TRUNC('MONTH', monthly.ordered_at)\n ) AS _base\n GROUP BY\n _base.\"monthly.ordered_at_month\"\n)\nSELECT\n _base.\"monthly.ordered_at\",\n _cm_monthly___sum.\"monthly.m\"\nFROM _base\nLEFT JOIN _cm_monthly___sum\n ON _base.\"monthly.ordered_at\" IS NOT DISTINCT FROM _cm_monthly___sum.\"monthly.ordered_at_month\"", + "lifted/windowed_inner::snowflake": "WITH _base AS (\n SELECT\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at\"\n FROM monthly AS monthly\n GROUP BY\n DATE_TRUNC('MONTH', monthly.ordered_at)\n), _base_2 AS (\n SELECT\n monthly.region AS \"monthly.region\",\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at_month\"\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n DATE_TRUNC('MONTH', monthly.ordered_at)\n), _cm_monthly__amount_sum_window_90d_partition_by_region AS (\n SELECT\n _base.\"monthly.region\",\n _base.\"monthly.ordered_at_month\",\n CAST(SUM(_src._w_value) AS DOUBLE) AS \"monthly.amount_sum_window_90d_partition_by_region\"\n FROM (\n SELECT\n monthly.region AS \"monthly.region\",\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at_month\"\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n DATE_TRUNC('MONTH', monthly.ordered_at)\n ) AS _base\n LEFT JOIN (\n SELECT\n monthly.region AS _w_dim_0,\n monthly.ordered_at AS _w_time,\n monthly.amount AS _w_value\n FROM monthly AS monthly\n ) AS _src\n ON _src._w_dim_0 IS NOT DISTINCT FROM _base.\"monthly.region\"\n AND _src._w_time >= DATEADD(DAY, -90, DATEADD(MONTH, 1, _base.\"monthly.ordered_at_month\"))\n AND _src._w_time < DATEADD(MONTH, 1, _base.\"monthly.ordered_at_month\")\n GROUP BY\n _base.\"monthly.region\",\n _base.\"monthly.ordered_at_month\"\n), base_2 AS (\n SELECT\n _base_2.\"monthly.region\",\n _base_2.\"monthly.ordered_at_month\",\n _cm_monthly__amount_sum_window_90d_partition_by_region.\"monthly.amount_sum_window_90d_partition_by_region\" AS \"monthly.amount_sum_window_90d_partition_by_region\"\n FROM _base_2\n LEFT JOIN _cm_monthly__amount_sum_window_90d_partition_by_region\n ON _base_2.\"monthly.region\" IS NOT DISTINCT FROM _cm_monthly__amount_sum_window_90d_partition_by_region.\"monthly.region\"\n AND _base_2.\"monthly.ordered_at_month\" IS NOT DISTINCT FROM _cm_monthly__amount_sum_window_90d_partition_by_region.\"monthly.ordered_at_month\"\n), step1 AS (\n SELECT\n \"monthly.region\",\n \"monthly.ordered_at_month\",\n \"monthly.amount_sum_window_90d_partition_by_region\",\n CASE\n WHEN \"monthly.amount_sum_window_90d_partition_by_region\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"monthly.amount_sum_window_90d_partition_by_region\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"monthly.amount_sum_window_90d_partition_by_region\" DESC\n )\n END AS \"monthly.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"monthly.region\" AS \"region\",\n _stage_inner.\"monthly.ordered_at_month\" AS \"ordered_at_month\",\n _stage_inner.\"monthly.rank\" AS \"rank\"\n FROM (\n SELECT\n \"monthly.region\",\n \"monthly.ordered_at_month\",\n \"monthly.rank\"\n FROM (\n SELECT\n \"monthly.region\",\n \"monthly.ordered_at_month\",\n \"monthly.amount_sum_window_90d_partition_by_region\",\n \"monthly.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_monthly___sum AS (\n SELECT\n _base.\"monthly.ordered_at_month\" AS \"monthly.ordered_at_month\",\n SUM(_base._v) AS \"monthly.m\"\n FROM (\n SELECT\n DATE_TRUNC('MONTH', monthly.ordered_at) AS \"monthly.ordered_at_month\",\n monthly.region AS _ek0,\n DATE_TRUNC('MONTH', monthly.ordered_at) AS _ek1,\n MAX(_cm_rank.\"rank\") AS _v\n FROM monthly AS monthly\n LEFT JOIN _cm_rank\n ON monthly.region IS NOT DISTINCT FROM _cm_rank.\"region\"\n AND DATE_TRUNC('MONTH', monthly.ordered_at) IS NOT DISTINCT FROM _cm_rank.\"ordered_at_month\"\n GROUP BY\n DATE_TRUNC('MONTH', monthly.ordered_at),\n monthly.region,\n DATE_TRUNC('MONTH', monthly.ordered_at)\n ) AS _base\n GROUP BY\n _base.\"monthly.ordered_at_month\"\n)\nSELECT\n _base.\"monthly.ordered_at\",\n _cm_monthly___sum.\"monthly.m\"\nFROM _base\nLEFT JOIN _cm_monthly___sum\n ON _base.\"monthly.ordered_at\" IS NOT DISTINCT FROM _cm_monthly___sum.\"monthly.ordered_at_month\"", + "lifted/windowed_inner::sqlite": "WITH _base AS (\n SELECT\n STRFTIME('%Y-%m-01', monthly.ordered_at) AS \"monthly.ordered_at\"\n FROM monthly AS monthly\n GROUP BY\n STRFTIME('%Y-%m-01', monthly.ordered_at)\n), _base_2 AS (\n SELECT\n monthly.region AS \"monthly.region\",\n STRFTIME('%Y-%m-01', monthly.ordered_at) AS \"monthly.ordered_at_month\"\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n STRFTIME('%Y-%m-01', monthly.ordered_at)\n), _cm_monthly__amount_sum_window_90d_partition_by_region AS (\n SELECT\n _base.\"monthly.region\",\n _base.\"monthly.ordered_at_month\",\n CAST(SUM(_src._w_value) AS REAL) AS \"monthly.amount_sum_window_90d_partition_by_region\"\n FROM (\n SELECT\n monthly.region AS \"monthly.region\",\n STRFTIME('%Y-%m-01', monthly.ordered_at) AS \"monthly.ordered_at_month\"\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n STRFTIME('%Y-%m-01', monthly.ordered_at)\n ) AS _base\n LEFT JOIN (\n SELECT\n monthly.region AS _w_dim_0,\n monthly.ordered_at AS _w_time,\n monthly.amount AS _w_value\n FROM monthly AS monthly\n ) AS _src\n ON _src._w_dim_0 IS _base.\"monthly.region\"\n AND DATETIME(_src._w_time) >= SLAYER_DATE_ADD(SLAYER_DATE_ADD(_base.\"monthly.ordered_at_month\", 1, 'month'), -90, 'day')\n AND DATETIME(_src._w_time) < SLAYER_DATE_ADD(_base.\"monthly.ordered_at_month\", 1, 'month')\n GROUP BY\n _base.\"monthly.region\",\n _base.\"monthly.ordered_at_month\"\n), base_2 AS (\n SELECT\n _base_2.\"monthly.region\",\n _base_2.\"monthly.ordered_at_month\",\n _cm_monthly__amount_sum_window_90d_partition_by_region.\"monthly.amount_sum_window_90d_partition_by_region\" AS \"monthly.amount_sum_window_90d_partition_by_region\"\n FROM _base_2\n LEFT JOIN _cm_monthly__amount_sum_window_90d_partition_by_region\n ON _base_2.\"monthly.region\" IS _cm_monthly__amount_sum_window_90d_partition_by_region.\"monthly.region\"\n AND _base_2.\"monthly.ordered_at_month\" IS _cm_monthly__amount_sum_window_90d_partition_by_region.\"monthly.ordered_at_month\"\n), step1 AS (\n SELECT\n \"monthly.region\",\n \"monthly.ordered_at_month\",\n \"monthly.amount_sum_window_90d_partition_by_region\",\n CASE\n WHEN \"monthly.amount_sum_window_90d_partition_by_region\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"monthly.amount_sum_window_90d_partition_by_region\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"monthly.amount_sum_window_90d_partition_by_region\" DESC\n )\n END AS \"monthly.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"monthly.region\" AS \"region\",\n _stage_inner.\"monthly.ordered_at_month\" AS \"ordered_at_month\",\n _stage_inner.\"monthly.rank\" AS \"rank\"\n FROM (\n SELECT\n \"monthly.region\",\n \"monthly.ordered_at_month\",\n \"monthly.rank\"\n FROM (\n SELECT\n \"monthly.region\",\n \"monthly.ordered_at_month\",\n \"monthly.amount_sum_window_90d_partition_by_region\",\n \"monthly.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_monthly___sum AS (\n SELECT\n _base.\"monthly.ordered_at_month\" AS \"monthly.ordered_at_month\",\n SUM(_base._v) AS \"monthly.m\"\n FROM (\n SELECT\n STRFTIME('%Y-%m-01', monthly.ordered_at) AS \"monthly.ordered_at_month\",\n monthly.region AS _ek0,\n STRFTIME('%Y-%m-01', monthly.ordered_at) AS _ek1,\n MAX(_cm_rank.\"rank\") AS _v\n FROM monthly AS monthly\n LEFT JOIN _cm_rank\n ON monthly.region IS _cm_rank.\"region\"\n AND STRFTIME('%Y-%m-01', monthly.ordered_at) IS _cm_rank.\"ordered_at_month\"\n GROUP BY\n STRFTIME('%Y-%m-01', monthly.ordered_at),\n monthly.region,\n STRFTIME('%Y-%m-01', monthly.ordered_at)\n ) AS _base\n GROUP BY\n _base.\"monthly.ordered_at_month\"\n)\nSELECT\n _base.\"monthly.ordered_at\",\n _cm_monthly___sum.\"monthly.m\"\nFROM _base\nLEFT JOIN _cm_monthly___sum\n ON _base.\"monthly.ordered_at\" IS _cm_monthly___sum.\"monthly.ordered_at_month\"", + "lifted/windowed_inner::tsql": "WITH _base AS (\n SELECT\n DATETRUNC(month, monthly.ordered_at) AS [monthly___ordered_at]\n FROM monthly AS monthly\n GROUP BY\n DATETRUNC(month, monthly.ordered_at)\n), _base_2 AS (\n SELECT\n monthly.region AS [monthly___region],\n DATETRUNC(month, monthly.ordered_at) AS [monthly___ordered_at_month]\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n DATETRUNC(month, monthly.ordered_at)\n), _cm_monthly__amount_sum_window_90d_partition_by_region AS (\n SELECT\n _base.[monthly___region] AS [monthly___region],\n _base.[monthly___ordered_at_month] AS [monthly___ordered_at_month],\n CAST(SUM(_src._w_value) AS FLOAT) AS [monthly___amount_sum_window_90d_partition_by_region]\n FROM (\n SELECT\n monthly.region AS [monthly___region],\n DATETRUNC(month, monthly.ordered_at) AS [monthly___ordered_at_month]\n FROM monthly AS monthly\n GROUP BY\n monthly.region,\n DATETRUNC(month, monthly.ordered_at)\n ) AS _base\n LEFT JOIN (\n SELECT\n monthly.region AS _w_dim_0,\n monthly.ordered_at AS _w_time,\n monthly.amount AS _w_value\n FROM monthly AS monthly\n ) AS _src\n ON (\n _src._w_dim_0 = _base.[monthly___region]\n OR (\n _src._w_dim_0 IS NULL AND _base.[monthly___region] IS NULL\n )\n )\n AND _src._w_time >= DATEADD(DAY, -90, DATEADD(MONTH, 1, _base.[monthly___ordered_at_month]))\n AND _src._w_time < DATEADD(MONTH, 1, _base.[monthly___ordered_at_month])\n GROUP BY\n _base.[monthly___region],\n _base.[monthly___ordered_at_month]\n), base_2 AS (\n SELECT\n _base_2.[monthly___region] AS [monthly___region],\n _base_2.[monthly___ordered_at_month] AS [monthly___ordered_at_month],\n _cm_monthly__amount_sum_window_90d_partition_by_region.[monthly___amount_sum_window_90d_partition_by_region] AS [monthly___amount_sum_window_90d_partition_by_region]\n FROM _base_2\n LEFT JOIN _cm_monthly__amount_sum_window_90d_partition_by_region\n ON (\n _base_2.[monthly___region] = _cm_monthly__amount_sum_window_90d_partition_by_region.[monthly___region]\n OR (\n _base_2.[monthly___region] IS NULL\n AND _cm_monthly__amount_sum_window_90d_partition_by_region.[monthly___region] IS NULL\n )\n )\n AND (\n _base_2.[monthly___ordered_at_month] = _cm_monthly__amount_sum_window_90d_partition_by_region.[monthly___ordered_at_month]\n OR (\n _base_2.[monthly___ordered_at_month] IS NULL\n AND _cm_monthly__amount_sum_window_90d_partition_by_region.[monthly___ordered_at_month] IS NULL\n )\n )\n), step1 AS (\n SELECT\n [monthly___region] AS [monthly___region],\n [monthly___ordered_at_month] AS [monthly___ordered_at_month],\n [monthly___amount_sum_window_90d_partition_by_region] AS [monthly___amount_sum_window_90d_partition_by_region],\n CASE\n WHEN [monthly___amount_sum_window_90d_partition_by_region] IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN [monthly___amount_sum_window_90d_partition_by_region] IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY [monthly___amount_sum_window_90d_partition_by_region] DESC\n )\n END AS [monthly___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[monthly___region] AS [region],\n _stage_inner.[monthly___ordered_at_month] AS [ordered_at_month],\n _stage_inner.[monthly___rank] AS [rank]\n FROM (\n SELECT\n [monthly___region] AS [monthly___region],\n [monthly___ordered_at_month] AS [monthly___ordered_at_month],\n [monthly___rank] AS [monthly___rank]\n FROM (\n SELECT\n [monthly___region],\n [monthly___ordered_at_month],\n [monthly___amount_sum_window_90d_partition_by_region],\n [monthly___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_monthly___sum AS (\n SELECT\n _base.[monthly___ordered_at_month] AS [monthly___ordered_at_month],\n SUM(_base._v) AS [monthly___m]\n FROM (\n SELECT\n DATETRUNC(month, monthly.ordered_at) AS [monthly___ordered_at_month],\n monthly.region AS _ek0,\n DATETRUNC(month, monthly.ordered_at) AS _ek1,\n MAX(_cm_rank.[rank]) AS _v\n FROM monthly AS monthly\n LEFT JOIN _cm_rank\n ON (\n monthly.region = _cm_rank.[region]\n OR (\n monthly.region IS NULL AND _cm_rank.[region] IS NULL\n )\n )\n AND (\n DATETRUNC(month, monthly.ordered_at) = _cm_rank.[ordered_at_month]\n OR (\n DATETRUNC(month, monthly.ordered_at) IS NULL\n AND _cm_rank.[ordered_at_month] IS NULL\n )\n )\n GROUP BY\n DATETRUNC(month, monthly.ordered_at),\n monthly.region,\n DATETRUNC(month, monthly.ordered_at)\n ) AS _base\n GROUP BY\n _base.[monthly___ordered_at_month]\n)\nSELECT\n _base.[monthly___ordered_at],\n _cm_monthly___sum.[monthly___m]\nFROM _base\nLEFT JOIN _cm_monthly___sum\n ON (\n _base.[monthly___ordered_at] = _cm_monthly___sum.[monthly___ordered_at_month]\n OR (\n _base.[monthly___ordered_at] IS NULL\n AND _cm_monthly___sum.[monthly___ordered_at_month] IS NULL\n )\n )", "positive/mixed_row_attached::bigquery": "WITH _cm_unit_price_avg_partition_by_product AS (\n SELECT\n _stage_inner.`sales___product` AS `product`,\n _stage_inner.`sales___unit_price_avg_partition_by_product` AS `unit_price_avg_partition_by_product`\n FROM (\n SELECT\n sales.product AS `sales___product`,\n CAST(AVG(sales.unit_price) AS FLOAT64) AS `sales___unit_price_avg_partition_by_product`\n FROM sales AS sales\n GROUP BY\n sales.product\n ) AS _stage_inner\n)\nSELECT\n sales.region AS `sales___region`,\n SUM(\n sales.quantity * _cm_unit_price_avg_partition_by_product.`unit_price_avg_partition_by_product`\n ) AS `sales___m`\nFROM sales AS sales\nLEFT JOIN _cm_unit_price_avg_partition_by_product\n ON sales.product IS NOT DISTINCT FROM _cm_unit_price_avg_partition_by_product.`product`\nGROUP BY\n sales.region", "positive/mixed_row_attached::duckdb": "WITH _cm_unit_price_avg_partition_by_product AS (\n SELECT\n _stage_inner.\"sales.product\" AS \"product\",\n _stage_inner.\"sales.unit_price_avg_partition_by_product\" AS \"unit_price_avg_partition_by_product\"\n FROM (\n SELECT\n sales.product AS \"sales.product\",\n CAST(AVG(sales.unit_price) AS DOUBLE) AS \"sales.unit_price_avg_partition_by_product\"\n FROM sales AS sales\n GROUP BY\n sales.product\n ) AS _stage_inner\n)\nSELECT\n sales.region AS \"sales.region\",\n SUM(\n sales.quantity * _cm_unit_price_avg_partition_by_product.\"unit_price_avg_partition_by_product\"\n ) AS \"sales.m\"\nFROM sales AS sales\nLEFT JOIN _cm_unit_price_avg_partition_by_product\n ON sales.product IS NOT DISTINCT FROM _cm_unit_price_avg_partition_by_product.\"product\"\nGROUP BY\n sales.region", "positive/mixed_row_attached::mysql": "WITH _cm_unit_price_avg_partition_by_product AS (\n SELECT\n _stage_inner.`sales.product` AS `product`,\n _stage_inner.`sales.unit_price_avg_partition_by_product` AS `unit_price_avg_partition_by_product`\n FROM (\n SELECT\n sales.product AS `sales.product`,\n CAST(AVG(sales.unit_price) AS DOUBLE) AS `sales.unit_price_avg_partition_by_product`\n FROM sales AS sales\n GROUP BY\n sales.product\n ) AS _stage_inner\n)\nSELECT\n sales.region AS `sales.region`,\n SUM(\n sales.quantity * _cm_unit_price_avg_partition_by_product.`unit_price_avg_partition_by_product`\n ) AS `sales.m`\nFROM sales AS sales\nLEFT JOIN _cm_unit_price_avg_partition_by_product\n ON sales.product <=> _cm_unit_price_avg_partition_by_product.`product`\nGROUP BY\n sales.region", diff --git a/tests/golden/dev1839_sql_baseline.json b/tests/golden/dev1839_sql_baseline.json index 2c0f133d..cc394819 100644 --- a/tests/golden/dev1839_sql_baseline.json +++ b/tests/golden/dev1839_sql_baseline.json @@ -39,51 +39,51 @@ "error": "TimeAxisError", "message": "TimeAxisError: A time-ordered transform evaluates at a grain that does not contain its time axis 'ordered_at'; a producer bucketed by time joined back on the coarser grain would duplicate result rows.\n at transform 'cumsum'\n suggestion: Include the time key in the aggregate's partition_by= so the transform accumulates within its own grain." }, - "lift/dim_explicit_partition::bigquery": "WITH _base_2 AS (\n SELECT\n orders.city AS `orders___city`,\n orders.region AS `orders___region`\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS `orders___region`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_region`\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS `orders___city`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_city`\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.`orders___city`,\n _base_2.`orders___region`,\n _cm_orders__amount_sum_partition_by_region.`orders___amount_sum_partition_by_region` AS `orders___amount_sum_partition_by_region`,\n _cm_orders__amount_sum_partition_by_city.`orders___amount_sum_partition_by_city` AS `orders___amount_sum_partition_by_city`\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.`orders___region` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.`orders___region`\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.`orders___city` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.`orders___city`\n), step1 AS (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___amount_sum_partition_by_region`,\n `orders___amount_sum_partition_by_city`,\n RANK() OVER (\n PARTITION BY `orders___region`\n ORDER BY `orders___amount_sum_partition_by_region` - `orders___amount_sum_partition_by_city` DESC\n ) AS `orders___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`orders___city` AS `city`,\n _stage_inner.`orders___region` AS `region`,\n _stage_inner.`orders___rank` AS `rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___amount_sum_partition_by_region`,\n `orders___amount_sum_partition_by_city`,\n `orders___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS `orders___region`,\n orders.city AS `orders___city`,\n _cm_rank.`rank` AS `orders___er`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___s`\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.`city`\n AND orders.region IS NOT DISTINCT FROM _cm_rank.`region`\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.`rank`", - "lift/dim_explicit_partition::duckdb": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n RANK() OVER (\n PARTITION BY \"orders.region\"\n ORDER BY \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n ) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.er\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\"", - "lift/dim_explicit_partition::postgres": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n RANK() OVER (\n PARTITION BY \"orders.region\"\n ORDER BY \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC NULLS LAST\n ) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.er\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\"", - "lift/dim_explicit_partition::sqlite": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.\"orders.region\" IS _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n RANK() OVER (\n PARTITION BY \"orders.region\"\n ORDER BY \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n ) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.er\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS _cm_rank.\"city\" AND orders.region IS _cm_rank.\"region\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\"", - "lift/dim_explicit_partition::tsql": "WITH _base_2 AS (\n SELECT\n orders.city AS [orders___city],\n orders.region AS [orders___region]\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS [orders___region],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_region]\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS [orders___city],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_city]\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.[orders___city] AS [orders___city],\n _base_2.[orders___region] AS [orders___region],\n _cm_orders__amount_sum_partition_by_region.[orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n _cm_orders__amount_sum_partition_by_city.[orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city]\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON (\n _base_2.[orders___region] = _cm_orders__amount_sum_partition_by_region.[orders___region]\n OR (\n _base_2.[orders___region] IS NULL\n AND _cm_orders__amount_sum_partition_by_region.[orders___region] IS NULL\n )\n )\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON (\n _base_2.[orders___city] = _cm_orders__amount_sum_partition_by_city.[orders___city]\n OR (\n _base_2.[orders___city] IS NULL\n AND _cm_orders__amount_sum_partition_by_city.[orders___city] IS NULL\n )\n )\n), step1 AS (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n [orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city],\n RANK() OVER (\n PARTITION BY [orders___region]\n ORDER BY [orders___amount_sum_partition_by_region] - [orders___amount_sum_partition_by_city] DESC\n ) AS [orders___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[orders___city] AS [city],\n _stage_inner.[orders___region] AS [region],\n _stage_inner.[orders___rank] AS [rank]\n FROM (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___rank] AS [orders___rank]\n FROM (\n SELECT\n [orders___city],\n [orders___region],\n [orders___amount_sum_partition_by_region],\n [orders___amount_sum_partition_by_city],\n [orders___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS [orders___region],\n orders.city AS [orders___city],\n _cm_rank.[rank] AS [orders___er],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___s]\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON (\n orders.city = _cm_rank.[city]\n OR (\n orders.city IS NULL AND _cm_rank.[city] IS NULL\n )\n )\n AND (\n orders.region = _cm_rank.[region]\n OR (\n orders.region IS NULL AND _cm_rank.[region] IS NULL\n )\n )\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.[rank]", - "lift/dim_keyless_share::bigquery": "WITH _base_2 AS (\n SELECT\n orders.region AS `orders___region`,\n SUM(orders.amount) AS `orders___amount_sum_partition_by_region`\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by AS (\n SELECT\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by`\n FROM orders AS orders\n), base_2 AS (\n SELECT\n _base_2.`orders___region`,\n _base_2.`orders___amount_sum_partition_by_region`,\n _cm_orders__amount_sum_partition_by.`orders___amount_sum_partition_by` AS `orders___amount_sum_partition_by`\n FROM _base_2\n CROSS JOIN _cm_orders__amount_sum_partition_by\n), step1 AS (\n SELECT\n `orders___region`,\n `orders___amount_sum_partition_by_region`,\n `orders___amount_sum_partition_by`,\n RANK() OVER (\n ORDER BY `orders___amount_sum_partition_by_region` / `orders___amount_sum_partition_by` DESC\n ) AS `orders___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`orders___region` AS `region`,\n _stage_inner.`orders___rank` AS `rank`\n FROM (\n SELECT\n `orders___region`,\n `orders___rank`\n FROM (\n SELECT\n `orders___region`,\n `orders___amount_sum_partition_by_region`,\n `orders___amount_sum_partition_by`,\n `orders___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS `orders___region`,\n _cm_rank.`rank` AS `orders___kr`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___s`\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.region IS NOT DISTINCT FROM _cm_rank.`region`\nGROUP BY\n orders.region,\n _cm_rank.`rank`", - "lift/dim_keyless_share::duckdb": "WITH _base_2 AS (\n SELECT\n orders.region AS \"orders.region\",\n SUM(orders.amount) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by AS (\n SELECT\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by\"\n FROM orders AS orders\n), base_2 AS (\n SELECT\n _base_2.\"orders.region\",\n _base_2.\"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by.\"orders.amount_sum_partition_by\" AS \"orders.amount_sum_partition_by\"\n FROM _base_2\n CROSS JOIN _cm_orders__amount_sum_partition_by\n), step1 AS (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by\",\n RANK() OVER (\n ORDER BY \"orders.amount_sum_partition_by_region\" / \"orders.amount_sum_partition_by\" DESC\n ) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n _cm_rank.\"rank\" AS \"orders.kr\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n _cm_rank.\"rank\"", - "lift/dim_keyless_share::postgres": "WITH _base_2 AS (\n SELECT\n orders.region AS \"orders.region\",\n SUM(orders.amount) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by AS (\n SELECT\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by\"\n FROM orders AS orders\n), base_2 AS (\n SELECT\n _base_2.\"orders.region\",\n _base_2.\"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by.\"orders.amount_sum_partition_by\" AS \"orders.amount_sum_partition_by\"\n FROM _base_2\n CROSS JOIN _cm_orders__amount_sum_partition_by\n), step1 AS (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by\",\n RANK() OVER (\n ORDER BY CAST(\"orders.amount_sum_partition_by_region\" AS DOUBLE PRECISION) / \"orders.amount_sum_partition_by\" DESC NULLS LAST\n ) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n _cm_rank.\"rank\" AS \"orders.kr\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n _cm_rank.\"rank\"", - "lift/dim_keyless_share::sqlite": "WITH _base_2 AS (\n SELECT\n orders.region AS \"orders.region\",\n SUM(orders.amount) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by AS (\n SELECT\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by\"\n FROM orders AS orders\n), base_2 AS (\n SELECT\n _base_2.\"orders.region\",\n _base_2.\"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by.\"orders.amount_sum_partition_by\" AS \"orders.amount_sum_partition_by\"\n FROM _base_2\n CROSS JOIN _cm_orders__amount_sum_partition_by\n), step1 AS (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by\",\n RANK() OVER (\n ORDER BY CAST(\"orders.amount_sum_partition_by_region\" AS REAL) / \"orders.amount_sum_partition_by\" DESC\n ) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n _cm_rank.\"rank\" AS \"orders.kr\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.region IS _cm_rank.\"region\"\nGROUP BY\n orders.region,\n _cm_rank.\"rank\"", - "lift/dim_keyless_share::tsql": "WITH _base_2 AS (\n SELECT\n orders.region AS [orders___region],\n SUM(orders.amount) AS [orders___amount_sum_partition_by_region]\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by AS (\n SELECT\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by]\n FROM orders AS orders\n), base_2 AS (\n SELECT\n _base_2.[orders___region] AS [orders___region],\n _base_2.[orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n _cm_orders__amount_sum_partition_by.[orders___amount_sum_partition_by] AS [orders___amount_sum_partition_by]\n FROM _base_2\n CROSS JOIN _cm_orders__amount_sum_partition_by\n), step1 AS (\n SELECT\n [orders___region] AS [orders___region],\n [orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n [orders___amount_sum_partition_by] AS [orders___amount_sum_partition_by],\n RANK() OVER (\n ORDER BY CAST([orders___amount_sum_partition_by_region] AS FLOAT) / [orders___amount_sum_partition_by] DESC\n ) AS [orders___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[orders___region] AS [region],\n _stage_inner.[orders___rank] AS [rank]\n FROM (\n SELECT\n [orders___region] AS [orders___region],\n [orders___rank] AS [orders___rank]\n FROM (\n SELECT\n [orders___region],\n [orders___amount_sum_partition_by_region],\n [orders___amount_sum_partition_by],\n [orders___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS [orders___region],\n _cm_rank.[rank] AS [orders___kr],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___s]\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON (\n orders.region = _cm_rank.[region]\n OR (\n orders.region IS NULL AND _cm_rank.[region] IS NULL\n )\n )\nGROUP BY\n orders.region,\n _cm_rank.[rank]", - "lift/dim_mixed_first_last::bigquery": "WITH _base_2 AS (\n SELECT\n orders.city AS `orders___city`,\n orders.region AS `orders___region`\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_last_partition_by_region AS (\n SELECT\n _val_0 AS `orders___region`,\n CAST(MAX(CASE WHEN _ranked_rn = 1 THEN _val_1 END) AS FLOAT64) AS `orders___amount_last_partition_by_region`\n FROM (\n SELECT\n orders.region AS _val_0,\n orders.amount AS _val_1,\n ROW_NUMBER() OVER (PARTITION BY orders.region ORDER BY orders.ordered_at DESC) AS _ranked_rn\n FROM orders AS orders\n ) AS _ranked_src\n GROUP BY\n _val_0\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS `orders___city`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_city`\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.`orders___city`,\n _base_2.`orders___region`,\n _cm_orders__amount_last_partition_by_region.`orders___amount_last_partition_by_region` AS `orders___amount_last_partition_by_region`,\n _cm_orders__amount_sum_partition_by_city.`orders___amount_sum_partition_by_city` AS `orders___amount_sum_partition_by_city`\n FROM _base_2\n LEFT JOIN _cm_orders__amount_last_partition_by_region\n ON _base_2.`orders___region` IS NOT DISTINCT FROM _cm_orders__amount_last_partition_by_region.`orders___region`\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.`orders___city` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.`orders___city`\n), step1 AS (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___amount_last_partition_by_region`,\n `orders___amount_sum_partition_by_city`,\n RANK() OVER (\n ORDER BY `orders___amount_last_partition_by_region` - `orders___amount_sum_partition_by_city` DESC\n ) AS `orders___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`orders___city` AS `city`,\n _stage_inner.`orders___region` AS `region`,\n _stage_inner.`orders___rank` AS `rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___amount_last_partition_by_region`,\n `orders___amount_sum_partition_by_city`,\n `orders___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS `orders___region`,\n orders.city AS `orders___city`,\n _cm_rank.`rank` AS `orders___x`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___s`\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.`city`\n AND orders.region IS NOT DISTINCT FROM _cm_rank.`region`\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.`rank`", - "lift/dim_mixed_first_last::duckdb": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_last_partition_by_region AS (\n SELECT\n _val_0 AS \"orders.region\",\n CAST(MAX(CASE WHEN _ranked_rn = 1 THEN _val_1 END) AS DOUBLE) AS \"orders.amount_last_partition_by_region\"\n FROM (\n SELECT\n orders.region AS _val_0,\n orders.amount AS _val_1,\n ROW_NUMBER() OVER (PARTITION BY orders.region ORDER BY orders.ordered_at DESC) AS _ranked_rn\n FROM orders AS orders\n ) AS _ranked_src\n GROUP BY\n _val_0\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _cm_orders__amount_last_partition_by_region.\"orders.amount_last_partition_by_region\" AS \"orders.amount_last_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_last_partition_by_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_last_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_last_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n RANK() OVER (\n ORDER BY \"orders.amount_last_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n ) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_last_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.x\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\"", - "lift/dim_mixed_first_last::postgres": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_last_partition_by_region AS (\n SELECT\n _val_0 AS \"orders.region\",\n CAST(MAX(CASE WHEN _ranked_rn = 1 THEN _val_1 END) AS DOUBLE PRECISION) AS \"orders.amount_last_partition_by_region\"\n FROM (\n SELECT\n orders.region AS _val_0,\n orders.amount AS _val_1,\n ROW_NUMBER() OVER (PARTITION BY orders.region ORDER BY orders.ordered_at DESC) AS _ranked_rn\n FROM orders AS orders\n ) AS _ranked_src\n GROUP BY\n _val_0\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _cm_orders__amount_last_partition_by_region.\"orders.amount_last_partition_by_region\" AS \"orders.amount_last_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_last_partition_by_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_last_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_last_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n RANK() OVER (\n ORDER BY \"orders.amount_last_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC NULLS LAST\n ) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_last_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.x\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\"", - "lift/dim_mixed_first_last::sqlite": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_last_partition_by_region AS (\n SELECT\n _val_0 AS \"orders.region\",\n CAST(MAX(CASE WHEN _ranked_rn = 1 THEN _val_1 END) AS REAL) AS \"orders.amount_last_partition_by_region\"\n FROM (\n SELECT\n orders.region AS _val_0,\n orders.amount AS _val_1,\n ROW_NUMBER() OVER (PARTITION BY orders.region ORDER BY orders.ordered_at DESC) AS _ranked_rn\n FROM orders AS orders\n ) AS _ranked_src\n GROUP BY\n _val_0\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _cm_orders__amount_last_partition_by_region.\"orders.amount_last_partition_by_region\" AS \"orders.amount_last_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_last_partition_by_region\n ON _base_2.\"orders.region\" IS _cm_orders__amount_last_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_last_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n RANK() OVER (\n ORDER BY \"orders.amount_last_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n ) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_last_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.x\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS _cm_rank.\"city\" AND orders.region IS _cm_rank.\"region\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\"", - "lift/dim_mixed_first_last::tsql": "WITH _base_2 AS (\n SELECT\n orders.city AS [orders___city],\n orders.region AS [orders___region]\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_last_partition_by_region AS (\n SELECT\n _val_0 AS [orders___region],\n CAST(MAX(CASE WHEN _ranked_rn = 1 THEN _val_1 END) AS FLOAT) AS [orders___amount_last_partition_by_region]\n FROM (\n SELECT\n orders.region AS _val_0,\n orders.amount AS _val_1,\n ROW_NUMBER() OVER (PARTITION BY orders.region ORDER BY orders.ordered_at DESC) AS _ranked_rn\n FROM orders AS orders\n ) AS _ranked_src\n GROUP BY\n _val_0\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS [orders___city],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_city]\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.[orders___city] AS [orders___city],\n _base_2.[orders___region] AS [orders___region],\n _cm_orders__amount_last_partition_by_region.[orders___amount_last_partition_by_region] AS [orders___amount_last_partition_by_region],\n _cm_orders__amount_sum_partition_by_city.[orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city]\n FROM _base_2\n LEFT JOIN _cm_orders__amount_last_partition_by_region\n ON (\n _base_2.[orders___region] = _cm_orders__amount_last_partition_by_region.[orders___region]\n OR (\n _base_2.[orders___region] IS NULL\n AND _cm_orders__amount_last_partition_by_region.[orders___region] IS NULL\n )\n )\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON (\n _base_2.[orders___city] = _cm_orders__amount_sum_partition_by_city.[orders___city]\n OR (\n _base_2.[orders___city] IS NULL\n AND _cm_orders__amount_sum_partition_by_city.[orders___city] IS NULL\n )\n )\n), step1 AS (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___amount_last_partition_by_region] AS [orders___amount_last_partition_by_region],\n [orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city],\n RANK() OVER (\n ORDER BY [orders___amount_last_partition_by_region] - [orders___amount_sum_partition_by_city] DESC\n ) AS [orders___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[orders___city] AS [city],\n _stage_inner.[orders___region] AS [region],\n _stage_inner.[orders___rank] AS [rank]\n FROM (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___rank] AS [orders___rank]\n FROM (\n SELECT\n [orders___city],\n [orders___region],\n [orders___amount_last_partition_by_region],\n [orders___amount_sum_partition_by_city],\n [orders___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS [orders___region],\n orders.city AS [orders___city],\n _cm_rank.[rank] AS [orders___x],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___s]\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON (\n orders.city = _cm_rank.[city]\n OR (\n orders.city IS NULL AND _cm_rank.[city] IS NULL\n )\n )\n AND (\n orders.region = _cm_rank.[region]\n OR (\n orders.region IS NULL AND _cm_rank.[region] IS NULL\n )\n )\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.[rank]", - "lift/dim_mixed_rank::bigquery": "WITH _base_2 AS (\n SELECT\n orders.city AS `orders___city`,\n orders.region AS `orders___region`\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS `orders___region`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_region`\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS `orders___city`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_city`\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.`orders___city`,\n _base_2.`orders___region`,\n _cm_orders__amount_sum_partition_by_region.`orders___amount_sum_partition_by_region` AS `orders___amount_sum_partition_by_region`,\n _cm_orders__amount_sum_partition_by_city.`orders___amount_sum_partition_by_city` AS `orders___amount_sum_partition_by_city`\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.`orders___region` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.`orders___region`\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.`orders___city` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.`orders___city`\n), step1 AS (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___amount_sum_partition_by_region`,\n `orders___amount_sum_partition_by_city`,\n RANK() OVER (\n ORDER BY `orders___amount_sum_partition_by_region` - `orders___amount_sum_partition_by_city` DESC\n ) AS `orders___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`orders___city` AS `city`,\n _stage_inner.`orders___region` AS `region`,\n _stage_inner.`orders___rank` AS `rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___amount_sum_partition_by_region`,\n `orders___amount_sum_partition_by_city`,\n `orders___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS `orders___region`,\n orders.city AS `orders___city`,\n _cm_rank.`rank` AS `orders___rr`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___s`\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.`city`\n AND orders.region IS NOT DISTINCT FROM _cm_rank.`region`\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.`rank`", - "lift/dim_mixed_rank::duckdb": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n RANK() OVER (\n ORDER BY \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n ) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.rr\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\"", - "lift/dim_mixed_rank::postgres": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n RANK() OVER (\n ORDER BY \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC NULLS LAST\n ) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.rr\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\"", - "lift/dim_mixed_rank::sqlite": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.\"orders.region\" IS _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n RANK() OVER (\n ORDER BY \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n ) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.rr\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS _cm_rank.\"city\" AND orders.region IS _cm_rank.\"region\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\"", - "lift/dim_mixed_rank::tsql": "WITH _base_2 AS (\n SELECT\n orders.city AS [orders___city],\n orders.region AS [orders___region]\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS [orders___region],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_region]\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS [orders___city],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_city]\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.[orders___city] AS [orders___city],\n _base_2.[orders___region] AS [orders___region],\n _cm_orders__amount_sum_partition_by_region.[orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n _cm_orders__amount_sum_partition_by_city.[orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city]\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON (\n _base_2.[orders___region] = _cm_orders__amount_sum_partition_by_region.[orders___region]\n OR (\n _base_2.[orders___region] IS NULL\n AND _cm_orders__amount_sum_partition_by_region.[orders___region] IS NULL\n )\n )\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON (\n _base_2.[orders___city] = _cm_orders__amount_sum_partition_by_city.[orders___city]\n OR (\n _base_2.[orders___city] IS NULL\n AND _cm_orders__amount_sum_partition_by_city.[orders___city] IS NULL\n )\n )\n), step1 AS (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n [orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city],\n RANK() OVER (\n ORDER BY [orders___amount_sum_partition_by_region] - [orders___amount_sum_partition_by_city] DESC\n ) AS [orders___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[orders___city] AS [city],\n _stage_inner.[orders___region] AS [region],\n _stage_inner.[orders___rank] AS [rank]\n FROM (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___rank] AS [orders___rank]\n FROM (\n SELECT\n [orders___city],\n [orders___region],\n [orders___amount_sum_partition_by_region],\n [orders___amount_sum_partition_by_city],\n [orders___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS [orders___region],\n orders.city AS [orders___city],\n _cm_rank.[rank] AS [orders___rr],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___s]\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON (\n orders.city = _cm_rank.[city]\n OR (\n orders.city IS NULL AND _cm_rank.[city] IS NULL\n )\n )\n AND (\n orders.region = _cm_rank.[region]\n OR (\n orders.region IS NULL AND _cm_rank.[region] IS NULL\n )\n )\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.[rank]", - "lift/dim_mixed_windowed::bigquery": "WITH _base_2 AS (\n SELECT\n orders.city AS `orders___city`,\n orders.region AS `orders___region`,\n DATE_TRUNC(orders.ordered_at, MONTH) AS `orders___ordered_at_month`\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region,\n DATE_TRUNC(orders.ordered_at, MONTH)\n), _cm_orders__amount_sum_window_90d_partition_by_region AS (\n SELECT\n _base.`orders___region`,\n _base.`orders___ordered_at_month`,\n CAST(SUM(_src._w_value) AS FLOAT64) AS `orders___amount_sum_window_90d_partition_by_region`\n FROM (\n SELECT\n orders.region AS `orders___region`,\n DATE_TRUNC(orders.ordered_at, MONTH) AS `orders___ordered_at_month`\n FROM orders AS orders\n GROUP BY\n orders.region,\n DATE_TRUNC(orders.ordered_at, MONTH)\n ) AS _base\n LEFT JOIN (\n SELECT\n orders.region AS _w_dim_0,\n orders.ordered_at AS _w_time,\n orders.amount AS _w_value\n FROM orders AS orders\n ) AS _src\n ON _src._w_dim_0 IS NOT DISTINCT FROM _base.`orders___region`\n AND _src._w_time >= TIMESTAMP_ADD(\n CAST(DATETIME_ADD(CAST(_base.`orders___ordered_at_month` AS DATETIME), INTERVAL 1 MONTH) AS TIMESTAMP),\n INTERVAL -90 DAY\n )\n AND _src._w_time < CAST(DATETIME_ADD(CAST(_base.`orders___ordered_at_month` AS DATETIME), INTERVAL 1 MONTH) AS TIMESTAMP)\n GROUP BY\n _base.`orders___region`,\n _base.`orders___ordered_at_month`\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS `orders___city`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_city`\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.`orders___city`,\n _base_2.`orders___region`,\n _base_2.`orders___ordered_at_month`,\n _cm_orders__amount_sum_window_90d_partition_by_region.`orders___amount_sum_window_90d_partition_by_region` AS `orders___amount_sum_window_90d_partition_by_region`,\n _cm_orders__amount_sum_partition_by_city.`orders___amount_sum_partition_by_city` AS `orders___amount_sum_partition_by_city`\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_window_90d_partition_by_region\n ON _base_2.`orders___region` IS NOT DISTINCT FROM _cm_orders__amount_sum_window_90d_partition_by_region.`orders___region`\n AND _base_2.`orders___ordered_at_month` IS NOT DISTINCT FROM _cm_orders__amount_sum_window_90d_partition_by_region.`orders___ordered_at_month`\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.`orders___city` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.`orders___city`\n), step1 AS (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___ordered_at_month`,\n `orders___amount_sum_window_90d_partition_by_region`,\n `orders___amount_sum_partition_by_city`,\n RANK() OVER (\n ORDER BY `orders___amount_sum_window_90d_partition_by_region` - `orders___amount_sum_partition_by_city` DESC\n ) AS `orders___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`orders___city` AS `city`,\n _stage_inner.`orders___region` AS `region`,\n _stage_inner.`orders___ordered_at_month` AS `ordered_at_month`,\n _stage_inner.`orders___rank` AS `rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___ordered_at_month`,\n `orders___rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___ordered_at_month`,\n `orders___amount_sum_window_90d_partition_by_region`,\n `orders___amount_sum_partition_by_city`,\n `orders___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS `orders___region`,\n orders.city AS `orders___city`,\n _cm_rank.`rank` AS `orders___x`,\n DATE_TRUNC(orders.ordered_at, MONTH) AS `orders___ordered_at`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___s`\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.`city`\n AND orders.region IS NOT DISTINCT FROM _cm_rank.`region`\n AND DATE_TRUNC(orders.ordered_at, MONTH) IS NOT DISTINCT FROM _cm_rank.`ordered_at_month`\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.`rank`,\n DATE_TRUNC(orders.ordered_at, MONTH)", - "lift/dim_mixed_windowed::duckdb": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at_month\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region,\n DATE_TRUNC('MONTH', orders.ordered_at)\n), _cm_orders__amount_sum_window_90d_partition_by_region AS (\n SELECT\n _base.\"orders.region\",\n _base.\"orders.ordered_at_month\",\n CAST(SUM(_src._w_value) AS DOUBLE) AS \"orders.amount_sum_window_90d_partition_by_region\"\n FROM (\n SELECT\n orders.region AS \"orders.region\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at_month\"\n FROM orders AS orders\n GROUP BY\n orders.region,\n DATE_TRUNC('MONTH', orders.ordered_at)\n ) AS _base\n LEFT JOIN (\n SELECT\n orders.region AS _w_dim_0,\n orders.ordered_at AS _w_time,\n orders.amount AS _w_value\n FROM orders AS orders\n ) AS _src\n ON _src._w_dim_0 IS NOT DISTINCT FROM _base.\"orders.region\"\n AND _src._w_time >= _base.\"orders.ordered_at_month\" + INTERVAL 1 MONTH - INTERVAL 90 DAY\n AND _src._w_time < _base.\"orders.ordered_at_month\" + INTERVAL 1 MONTH\n GROUP BY\n _base.\"orders.region\",\n _base.\"orders.ordered_at_month\"\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _base_2.\"orders.ordered_at_month\",\n _cm_orders__amount_sum_window_90d_partition_by_region.\"orders.amount_sum_window_90d_partition_by_region\" AS \"orders.amount_sum_window_90d_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_window_90d_partition_by_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_window_90d_partition_by_region.\"orders.region\"\n AND _base_2.\"orders.ordered_at_month\" IS NOT DISTINCT FROM _cm_orders__amount_sum_window_90d_partition_by_region.\"orders.ordered_at_month\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_window_90d_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n RANK() OVER (\n ORDER BY \"orders.amount_sum_window_90d_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n ) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.ordered_at_month\" AS \"ordered_at_month\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_window_90d_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.x\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\n AND DATE_TRUNC('MONTH', orders.ordered_at) IS NOT DISTINCT FROM _cm_rank.\"ordered_at_month\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\",\n DATE_TRUNC('MONTH', orders.ordered_at)", - "lift/dim_mixed_windowed::postgres": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at_month\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region,\n DATE_TRUNC('MONTH', orders.ordered_at)\n), _cm_orders__amount_sum_window_90d_partition_by_region AS (\n SELECT\n _base.\"orders.region\",\n _base.\"orders.ordered_at_month\",\n CAST(SUM(_src._w_value) AS DOUBLE PRECISION) AS \"orders.amount_sum_window_90d_partition_by_region\"\n FROM (\n SELECT\n orders.region AS \"orders.region\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at_month\"\n FROM orders AS orders\n GROUP BY\n orders.region,\n DATE_TRUNC('MONTH', orders.ordered_at)\n ) AS _base\n LEFT JOIN (\n SELECT\n orders.region AS _w_dim_0,\n orders.ordered_at AS _w_time,\n orders.amount AS _w_value\n FROM orders AS orders\n ) AS _src\n ON _src._w_dim_0 IS NOT DISTINCT FROM _base.\"orders.region\"\n AND _src._w_time >= _base.\"orders.ordered_at_month\" + INTERVAL '1 MONTH' - INTERVAL '90 DAY'\n AND _src._w_time < _base.\"orders.ordered_at_month\" + INTERVAL '1 MONTH'\n GROUP BY\n _base.\"orders.region\",\n _base.\"orders.ordered_at_month\"\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _base_2.\"orders.ordered_at_month\",\n _cm_orders__amount_sum_window_90d_partition_by_region.\"orders.amount_sum_window_90d_partition_by_region\" AS \"orders.amount_sum_window_90d_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_window_90d_partition_by_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_window_90d_partition_by_region.\"orders.region\"\n AND _base_2.\"orders.ordered_at_month\" IS NOT DISTINCT FROM _cm_orders__amount_sum_window_90d_partition_by_region.\"orders.ordered_at_month\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_window_90d_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n RANK() OVER (\n ORDER BY \"orders.amount_sum_window_90d_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC NULLS LAST\n ) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.ordered_at_month\" AS \"ordered_at_month\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_window_90d_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.x\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\n AND DATE_TRUNC('MONTH', orders.ordered_at) IS NOT DISTINCT FROM _cm_rank.\"ordered_at_month\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\",\n DATE_TRUNC('MONTH', orders.ordered_at)", - "lift/dim_mixed_windowed::sqlite": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\",\n STRFTIME('%Y-%m-01', orders.ordered_at) AS \"orders.ordered_at_month\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region,\n STRFTIME('%Y-%m-01', orders.ordered_at)\n), _cm_orders__amount_sum_window_90d_partition_by_region AS (\n SELECT\n _base.\"orders.region\",\n _base.\"orders.ordered_at_month\",\n CAST(SUM(_src._w_value) AS REAL) AS \"orders.amount_sum_window_90d_partition_by_region\"\n FROM (\n SELECT\n orders.region AS \"orders.region\",\n STRFTIME('%Y-%m-01', orders.ordered_at) AS \"orders.ordered_at_month\"\n FROM orders AS orders\n GROUP BY\n orders.region,\n STRFTIME('%Y-%m-01', orders.ordered_at)\n ) AS _base\n LEFT JOIN (\n SELECT\n orders.region AS _w_dim_0,\n orders.ordered_at AS _w_time,\n orders.amount AS _w_value\n FROM orders AS orders\n ) AS _src\n ON _src._w_dim_0 IS _base.\"orders.region\"\n AND DATETIME(_src._w_time) >= SLAYER_DATE_ADD(SLAYER_DATE_ADD(_base.\"orders.ordered_at_month\", 1, 'month'), -90, 'day')\n AND DATETIME(_src._w_time) < SLAYER_DATE_ADD(_base.\"orders.ordered_at_month\", 1, 'month')\n GROUP BY\n _base.\"orders.region\",\n _base.\"orders.ordered_at_month\"\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _base_2.\"orders.ordered_at_month\",\n _cm_orders__amount_sum_window_90d_partition_by_region.\"orders.amount_sum_window_90d_partition_by_region\" AS \"orders.amount_sum_window_90d_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_window_90d_partition_by_region\n ON _base_2.\"orders.region\" IS _cm_orders__amount_sum_window_90d_partition_by_region.\"orders.region\"\n AND _base_2.\"orders.ordered_at_month\" IS _cm_orders__amount_sum_window_90d_partition_by_region.\"orders.ordered_at_month\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_window_90d_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n RANK() OVER (\n ORDER BY \"orders.amount_sum_window_90d_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n ) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.ordered_at_month\" AS \"ordered_at_month\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_window_90d_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.x\",\n STRFTIME('%Y-%m-01', orders.ordered_at) AS \"orders.ordered_at\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS _cm_rank.\"city\"\n AND orders.region IS _cm_rank.\"region\"\n AND STRFTIME('%Y-%m-01', orders.ordered_at) IS _cm_rank.\"ordered_at_month\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\",\n STRFTIME('%Y-%m-01', orders.ordered_at)", - "lift/dim_mixed_windowed::tsql": "WITH _base_2 AS (\n SELECT\n orders.city AS [orders___city],\n orders.region AS [orders___region],\n DATETRUNC(month, orders.ordered_at) AS [orders___ordered_at_month]\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region,\n DATETRUNC(month, orders.ordered_at)\n), _cm_orders__amount_sum_window_90d_partition_by_region AS (\n SELECT\n _base.[orders___region] AS [orders___region],\n _base.[orders___ordered_at_month] AS [orders___ordered_at_month],\n CAST(SUM(_src._w_value) AS FLOAT) AS [orders___amount_sum_window_90d_partition_by_region]\n FROM (\n SELECT\n orders.region AS [orders___region],\n DATETRUNC(month, orders.ordered_at) AS [orders___ordered_at_month]\n FROM orders AS orders\n GROUP BY\n orders.region,\n DATETRUNC(month, orders.ordered_at)\n ) AS _base\n LEFT JOIN (\n SELECT\n orders.region AS _w_dim_0,\n orders.ordered_at AS _w_time,\n orders.amount AS _w_value\n FROM orders AS orders\n ) AS _src\n ON (\n _src._w_dim_0 = _base.[orders___region]\n OR (\n _src._w_dim_0 IS NULL AND _base.[orders___region] IS NULL\n )\n )\n AND _src._w_time >= DATEADD(DAY, -90, DATEADD(MONTH, 1, _base.[orders___ordered_at_month]))\n AND _src._w_time < DATEADD(MONTH, 1, _base.[orders___ordered_at_month])\n GROUP BY\n _base.[orders___region],\n _base.[orders___ordered_at_month]\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS [orders___city],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_city]\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.[orders___city] AS [orders___city],\n _base_2.[orders___region] AS [orders___region],\n _base_2.[orders___ordered_at_month] AS [orders___ordered_at_month],\n _cm_orders__amount_sum_window_90d_partition_by_region.[orders___amount_sum_window_90d_partition_by_region] AS [orders___amount_sum_window_90d_partition_by_region],\n _cm_orders__amount_sum_partition_by_city.[orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city]\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_window_90d_partition_by_region\n ON (\n _base_2.[orders___region] = _cm_orders__amount_sum_window_90d_partition_by_region.[orders___region]\n OR (\n _base_2.[orders___region] IS NULL\n AND _cm_orders__amount_sum_window_90d_partition_by_region.[orders___region] IS NULL\n )\n )\n AND (\n _base_2.[orders___ordered_at_month] = _cm_orders__amount_sum_window_90d_partition_by_region.[orders___ordered_at_month]\n OR (\n _base_2.[orders___ordered_at_month] IS NULL\n AND _cm_orders__amount_sum_window_90d_partition_by_region.[orders___ordered_at_month] IS NULL\n )\n )\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON (\n _base_2.[orders___city] = _cm_orders__amount_sum_partition_by_city.[orders___city]\n OR (\n _base_2.[orders___city] IS NULL\n AND _cm_orders__amount_sum_partition_by_city.[orders___city] IS NULL\n )\n )\n), step1 AS (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___ordered_at_month] AS [orders___ordered_at_month],\n [orders___amount_sum_window_90d_partition_by_region] AS [orders___amount_sum_window_90d_partition_by_region],\n [orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city],\n RANK() OVER (\n ORDER BY [orders___amount_sum_window_90d_partition_by_region] - [orders___amount_sum_partition_by_city] DESC\n ) AS [orders___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[orders___city] AS [city],\n _stage_inner.[orders___region] AS [region],\n _stage_inner.[orders___ordered_at_month] AS [ordered_at_month],\n _stage_inner.[orders___rank] AS [rank]\n FROM (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___ordered_at_month] AS [orders___ordered_at_month],\n [orders___rank] AS [orders___rank]\n FROM (\n SELECT\n [orders___city],\n [orders___region],\n [orders___ordered_at_month],\n [orders___amount_sum_window_90d_partition_by_region],\n [orders___amount_sum_partition_by_city],\n [orders___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS [orders___region],\n orders.city AS [orders___city],\n _cm_rank.[rank] AS [orders___x],\n DATETRUNC(month, orders.ordered_at) AS [orders___ordered_at],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___s]\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON (\n orders.city = _cm_rank.[city]\n OR (\n orders.city IS NULL AND _cm_rank.[city] IS NULL\n )\n )\n AND (\n orders.region = _cm_rank.[region]\n OR (\n orders.region IS NULL AND _cm_rank.[region] IS NULL\n )\n )\n AND (\n DATETRUNC(month, orders.ordered_at) = _cm_rank.[ordered_at_month]\n OR (\n DATETRUNC(month, orders.ordered_at) IS NULL\n AND _cm_rank.[ordered_at_month] IS NULL\n )\n )\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.[rank],\n DATETRUNC(month, orders.ordered_at)", - "lift/dim_nested_cumsum::bigquery": "WITH _base_2 AS (\n SELECT\n orders.city AS `orders___city`,\n orders.region AS `orders___region`,\n DATE_TRUNC(orders.ordered_at, MONTH) AS `orders___ordered_at_month`\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region,\n DATE_TRUNC(orders.ordered_at, MONTH)\n), _cm_orders__amount_sum_partition_by_ordered_at_region AS (\n SELECT\n orders.region AS `orders___region`,\n DATE_TRUNC(orders.ordered_at, MONTH) AS `orders___ordered_at_month`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_ordered_at_region`\n FROM orders AS orders\n GROUP BY\n orders.region,\n DATE_TRUNC(orders.ordered_at, MONTH)\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS `orders___city`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_city`\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.`orders___city`,\n _base_2.`orders___region`,\n _base_2.`orders___ordered_at_month`,\n _cm_orders__amount_sum_partition_by_ordered_at_region.`orders___amount_sum_partition_by_ordered_at_region` AS `orders___amount_sum_partition_by_ordered_at_region`,\n _cm_orders__amount_sum_partition_by_city.`orders___amount_sum_partition_by_city` AS `orders___amount_sum_partition_by_city`\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_ordered_at_region\n ON _base_2.`orders___region` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_ordered_at_region.`orders___region`\n AND _base_2.`orders___ordered_at_month` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_ordered_at_region.`orders___ordered_at_month`\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.`orders___city` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.`orders___city`\n), step1 AS (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___ordered_at_month`,\n `orders___amount_sum_partition_by_ordered_at_region`,\n `orders___amount_sum_partition_by_city`,\n SUM(`orders___amount_sum_partition_by_ordered_at_region`) OVER (PARTITION BY `orders___city`, `orders___region` ORDER BY `orders___ordered_at_month`) AS `orders____cumsum_inner`\n FROM base_2\n), step2 AS (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___ordered_at_month`,\n `orders___amount_sum_partition_by_ordered_at_region`,\n `orders___amount_sum_partition_by_city`,\n `orders____cumsum_inner`,\n RANK() OVER (ORDER BY `orders____cumsum_inner` - `orders___amount_sum_partition_by_city` DESC) AS `orders___rank`\n FROM step1\n), _cm_rank AS (\n SELECT\n _stage_inner.`orders___city` AS `city`,\n _stage_inner.`orders___region` AS `region`,\n _stage_inner.`orders___ordered_at_month` AS `ordered_at_month`,\n _stage_inner.`orders___rank` AS `rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___ordered_at_month`,\n `orders___rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___ordered_at_month`,\n `orders___amount_sum_partition_by_ordered_at_region`,\n `orders___amount_sum_partition_by_city`,\n `orders____cumsum_inner`,\n `orders___rank`\n FROM step2\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS `orders___region`,\n orders.city AS `orders___city`,\n _cm_rank.`rank` AS `orders___nr`,\n DATE_TRUNC(orders.ordered_at, MONTH) AS `orders___ordered_at`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___s`\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.`city`\n AND orders.region IS NOT DISTINCT FROM _cm_rank.`region`\n AND DATE_TRUNC(orders.ordered_at, MONTH) IS NOT DISTINCT FROM _cm_rank.`ordered_at_month`\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.`rank`,\n DATE_TRUNC(orders.ordered_at, MONTH)", - "lift/dim_nested_cumsum::duckdb": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at_month\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region,\n DATE_TRUNC('MONTH', orders.ordered_at)\n), _cm_orders__amount_sum_partition_by_ordered_at_region AS (\n SELECT\n orders.region AS \"orders.region\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at_month\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_ordered_at_region\"\n FROM orders AS orders\n GROUP BY\n orders.region,\n DATE_TRUNC('MONTH', orders.ordered_at)\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _base_2.\"orders.ordered_at_month\",\n _cm_orders__amount_sum_partition_by_ordered_at_region.\"orders.amount_sum_partition_by_ordered_at_region\" AS \"orders.amount_sum_partition_by_ordered_at_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_ordered_at_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_ordered_at_region.\"orders.region\"\n AND _base_2.\"orders.ordered_at_month\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_ordered_at_region.\"orders.ordered_at_month\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_partition_by_ordered_at_region\",\n \"orders.amount_sum_partition_by_city\",\n SUM(\"orders.amount_sum_partition_by_ordered_at_region\") OVER (PARTITION BY \"orders.city\", \"orders.region\" ORDER BY \"orders.ordered_at_month\") AS \"orders._cumsum_inner\"\n FROM base_2\n), step2 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_partition_by_ordered_at_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders._cumsum_inner\",\n RANK() OVER (ORDER BY \"orders._cumsum_inner\" - \"orders.amount_sum_partition_by_city\" DESC) AS \"orders.rank\"\n FROM step1\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.ordered_at_month\" AS \"ordered_at_month\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_partition_by_ordered_at_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders._cumsum_inner\",\n \"orders.rank\"\n FROM step2\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.nr\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\n AND DATE_TRUNC('MONTH', orders.ordered_at) IS NOT DISTINCT FROM _cm_rank.\"ordered_at_month\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\",\n DATE_TRUNC('MONTH', orders.ordered_at)", - "lift/dim_nested_cumsum::postgres": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at_month\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region,\n DATE_TRUNC('MONTH', orders.ordered_at)\n), _cm_orders__amount_sum_partition_by_ordered_at_region AS (\n SELECT\n orders.region AS \"orders.region\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at_month\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_ordered_at_region\"\n FROM orders AS orders\n GROUP BY\n orders.region,\n DATE_TRUNC('MONTH', orders.ordered_at)\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _base_2.\"orders.ordered_at_month\",\n _cm_orders__amount_sum_partition_by_ordered_at_region.\"orders.amount_sum_partition_by_ordered_at_region\" AS \"orders.amount_sum_partition_by_ordered_at_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_ordered_at_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_ordered_at_region.\"orders.region\"\n AND _base_2.\"orders.ordered_at_month\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_ordered_at_region.\"orders.ordered_at_month\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_partition_by_ordered_at_region\",\n \"orders.amount_sum_partition_by_city\",\n SUM(\"orders.amount_sum_partition_by_ordered_at_region\") OVER (PARTITION BY \"orders.city\", \"orders.region\" ORDER BY \"orders.ordered_at_month\") AS \"orders._cumsum_inner\"\n FROM base_2\n), step2 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_partition_by_ordered_at_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders._cumsum_inner\",\n RANK() OVER (\n ORDER BY \"orders._cumsum_inner\" - \"orders.amount_sum_partition_by_city\" DESC NULLS LAST\n ) AS \"orders.rank\"\n FROM step1\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.ordered_at_month\" AS \"ordered_at_month\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_partition_by_ordered_at_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders._cumsum_inner\",\n \"orders.rank\"\n FROM step2\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.nr\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\n AND DATE_TRUNC('MONTH', orders.ordered_at) IS NOT DISTINCT FROM _cm_rank.\"ordered_at_month\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\",\n DATE_TRUNC('MONTH', orders.ordered_at)", - "lift/dim_nested_cumsum::sqlite": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\",\n STRFTIME('%Y-%m-01', orders.ordered_at) AS \"orders.ordered_at_month\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region,\n STRFTIME('%Y-%m-01', orders.ordered_at)\n), _cm_orders__amount_sum_partition_by_ordered_at_region AS (\n SELECT\n orders.region AS \"orders.region\",\n STRFTIME('%Y-%m-01', orders.ordered_at) AS \"orders.ordered_at_month\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_ordered_at_region\"\n FROM orders AS orders\n GROUP BY\n orders.region,\n STRFTIME('%Y-%m-01', orders.ordered_at)\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _base_2.\"orders.ordered_at_month\",\n _cm_orders__amount_sum_partition_by_ordered_at_region.\"orders.amount_sum_partition_by_ordered_at_region\" AS \"orders.amount_sum_partition_by_ordered_at_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_ordered_at_region\n ON _base_2.\"orders.region\" IS _cm_orders__amount_sum_partition_by_ordered_at_region.\"orders.region\"\n AND _base_2.\"orders.ordered_at_month\" IS _cm_orders__amount_sum_partition_by_ordered_at_region.\"orders.ordered_at_month\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_partition_by_ordered_at_region\",\n \"orders.amount_sum_partition_by_city\",\n SUM(\"orders.amount_sum_partition_by_ordered_at_region\") OVER (PARTITION BY \"orders.city\", \"orders.region\" ORDER BY \"orders.ordered_at_month\") AS \"orders._cumsum_inner\"\n FROM base_2\n), step2 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_partition_by_ordered_at_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders._cumsum_inner\",\n RANK() OVER (ORDER BY \"orders._cumsum_inner\" - \"orders.amount_sum_partition_by_city\" DESC) AS \"orders.rank\"\n FROM step1\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.ordered_at_month\" AS \"ordered_at_month\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_partition_by_ordered_at_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders._cumsum_inner\",\n \"orders.rank\"\n FROM step2\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.nr\",\n STRFTIME('%Y-%m-01', orders.ordered_at) AS \"orders.ordered_at\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS _cm_rank.\"city\"\n AND orders.region IS _cm_rank.\"region\"\n AND STRFTIME('%Y-%m-01', orders.ordered_at) IS _cm_rank.\"ordered_at_month\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\",\n STRFTIME('%Y-%m-01', orders.ordered_at)", - "lift/dim_nested_cumsum::tsql": "WITH _base_2 AS (\n SELECT\n orders.city AS [orders___city],\n orders.region AS [orders___region],\n DATETRUNC(month, orders.ordered_at) AS [orders___ordered_at_month]\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region,\n DATETRUNC(month, orders.ordered_at)\n), _cm_orders__amount_sum_partition_by_ordered_at_region AS (\n SELECT\n orders.region AS [orders___region],\n DATETRUNC(month, orders.ordered_at) AS [orders___ordered_at_month],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_ordered_at_region]\n FROM orders AS orders\n GROUP BY\n orders.region,\n DATETRUNC(month, orders.ordered_at)\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS [orders___city],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_city]\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.[orders___city] AS [orders___city],\n _base_2.[orders___region] AS [orders___region],\n _base_2.[orders___ordered_at_month] AS [orders___ordered_at_month],\n _cm_orders__amount_sum_partition_by_ordered_at_region.[orders___amount_sum_partition_by_ordered_at_region] AS [orders___amount_sum_partition_by_ordered_at_region],\n _cm_orders__amount_sum_partition_by_city.[orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city]\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_ordered_at_region\n ON (\n _base_2.[orders___region] = _cm_orders__amount_sum_partition_by_ordered_at_region.[orders___region]\n OR (\n _base_2.[orders___region] IS NULL\n AND _cm_orders__amount_sum_partition_by_ordered_at_region.[orders___region] IS NULL\n )\n )\n AND (\n _base_2.[orders___ordered_at_month] = _cm_orders__amount_sum_partition_by_ordered_at_region.[orders___ordered_at_month]\n OR (\n _base_2.[orders___ordered_at_month] IS NULL\n AND _cm_orders__amount_sum_partition_by_ordered_at_region.[orders___ordered_at_month] IS NULL\n )\n )\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON (\n _base_2.[orders___city] = _cm_orders__amount_sum_partition_by_city.[orders___city]\n OR (\n _base_2.[orders___city] IS NULL\n AND _cm_orders__amount_sum_partition_by_city.[orders___city] IS NULL\n )\n )\n), step1 AS (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___ordered_at_month] AS [orders___ordered_at_month],\n [orders___amount_sum_partition_by_ordered_at_region] AS [orders___amount_sum_partition_by_ordered_at_region],\n [orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city],\n SUM([orders___amount_sum_partition_by_ordered_at_region]) OVER (PARTITION BY [orders___city], [orders___region] ORDER BY [orders___ordered_at_month]) AS [orders____cumsum_inner]\n FROM base_2\n), step2 AS (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___ordered_at_month] AS [orders___ordered_at_month],\n [orders___amount_sum_partition_by_ordered_at_region] AS [orders___amount_sum_partition_by_ordered_at_region],\n [orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city],\n [orders____cumsum_inner] AS [orders____cumsum_inner],\n RANK() OVER (ORDER BY [orders____cumsum_inner] - [orders___amount_sum_partition_by_city] DESC) AS [orders___rank]\n FROM step1\n), _cm_rank AS (\n SELECT\n _stage_inner.[orders___city] AS [city],\n _stage_inner.[orders___region] AS [region],\n _stage_inner.[orders___ordered_at_month] AS [ordered_at_month],\n _stage_inner.[orders___rank] AS [rank]\n FROM (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___ordered_at_month] AS [orders___ordered_at_month],\n [orders___rank] AS [orders___rank]\n FROM (\n SELECT\n [orders___city],\n [orders___region],\n [orders___ordered_at_month],\n [orders___amount_sum_partition_by_ordered_at_region],\n [orders___amount_sum_partition_by_city],\n [orders____cumsum_inner],\n [orders___rank]\n FROM step2\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS [orders___region],\n orders.city AS [orders___city],\n _cm_rank.[rank] AS [orders___nr],\n DATETRUNC(month, orders.ordered_at) AS [orders___ordered_at],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___s]\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON (\n orders.city = _cm_rank.[city]\n OR (\n orders.city IS NULL AND _cm_rank.[city] IS NULL\n )\n )\n AND (\n orders.region = _cm_rank.[region]\n OR (\n orders.region IS NULL AND _cm_rank.[region] IS NULL\n )\n )\n AND (\n DATETRUNC(month, orders.ordered_at) = _cm_rank.[ordered_at_month]\n OR (\n DATETRUNC(month, orders.ordered_at) IS NULL\n AND _cm_rank.[ordered_at_month] IS NULL\n )\n )\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.[rank],\n DATETRUNC(month, orders.ordered_at)", - "lift/dim_same_grain_rank::bigquery": "WITH base_2 AS (\n SELECT\n orders.region AS `orders___region`,\n SUM(orders.amount) AS `orders___amount_sum_partition_by_region`,\n SUM(CASE WHEN orders.status = 'ok' THEN orders.amount END) AS `orders___ok_amount_sum_partition_by_region`\n FROM orders AS orders\n GROUP BY\n orders.region\n), step1 AS (\n SELECT\n `orders___region`,\n `orders___amount_sum_partition_by_region`,\n `orders___ok_amount_sum_partition_by_region`,\n RANK() OVER (\n ORDER BY `orders___amount_sum_partition_by_region` + `orders___ok_amount_sum_partition_by_region` DESC\n ) AS `orders___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`orders___region` AS `region`,\n _stage_inner.`orders___rank` AS `rank`\n FROM (\n SELECT\n `orders___region`,\n `orders___rank`\n FROM (\n SELECT\n `orders___region`,\n `orders___amount_sum_partition_by_region`,\n `orders___ok_amount_sum_partition_by_region`,\n `orders___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS `orders___region`,\n _cm_rank.`rank` AS `orders___gr`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___s`\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.region IS NOT DISTINCT FROM _cm_rank.`region`\nGROUP BY\n orders.region,\n _cm_rank.`rank`", - "lift/dim_same_grain_rank::duckdb": "WITH base_2 AS (\n SELECT\n orders.region AS \"orders.region\",\n SUM(orders.amount) AS \"orders.amount_sum_partition_by_region\",\n SUM(CASE WHEN orders.status = 'ok' THEN orders.amount END) AS \"orders.ok_amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), step1 AS (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.ok_amount_sum_partition_by_region\",\n RANK() OVER (\n ORDER BY \"orders.amount_sum_partition_by_region\" + \"orders.ok_amount_sum_partition_by_region\" DESC\n ) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.ok_amount_sum_partition_by_region\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n _cm_rank.\"rank\" AS \"orders.gr\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n _cm_rank.\"rank\"", - "lift/dim_same_grain_rank::postgres": "WITH base_2 AS (\n SELECT\n orders.region AS \"orders.region\",\n SUM(orders.amount) AS \"orders.amount_sum_partition_by_region\",\n SUM(CASE WHEN orders.status = 'ok' THEN orders.amount END) AS \"orders.ok_amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), step1 AS (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.ok_amount_sum_partition_by_region\",\n RANK() OVER (\n ORDER BY \"orders.amount_sum_partition_by_region\" + \"orders.ok_amount_sum_partition_by_region\" DESC NULLS LAST\n ) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.ok_amount_sum_partition_by_region\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n _cm_rank.\"rank\" AS \"orders.gr\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n _cm_rank.\"rank\"", - "lift/dim_same_grain_rank::sqlite": "WITH base_2 AS (\n SELECT\n orders.region AS \"orders.region\",\n SUM(orders.amount) AS \"orders.amount_sum_partition_by_region\",\n SUM(CASE WHEN orders.status = 'ok' THEN orders.amount END) AS \"orders.ok_amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), step1 AS (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.ok_amount_sum_partition_by_region\",\n RANK() OVER (\n ORDER BY \"orders.amount_sum_partition_by_region\" + \"orders.ok_amount_sum_partition_by_region\" DESC\n ) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.ok_amount_sum_partition_by_region\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n _cm_rank.\"rank\" AS \"orders.gr\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.region IS _cm_rank.\"region\"\nGROUP BY\n orders.region,\n _cm_rank.\"rank\"", - "lift/dim_same_grain_rank::tsql": "WITH base_2 AS (\n SELECT\n orders.region AS [orders___region],\n SUM(orders.amount) AS [orders___amount_sum_partition_by_region],\n SUM(CASE WHEN orders.status = 'ok' THEN orders.amount END) AS [orders___ok_amount_sum_partition_by_region]\n FROM orders AS orders\n GROUP BY\n orders.region\n), step1 AS (\n SELECT\n [orders___region] AS [orders___region],\n [orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n [orders___ok_amount_sum_partition_by_region] AS [orders___ok_amount_sum_partition_by_region],\n RANK() OVER (\n ORDER BY [orders___amount_sum_partition_by_region] + [orders___ok_amount_sum_partition_by_region] DESC\n ) AS [orders___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[orders___region] AS [region],\n _stage_inner.[orders___rank] AS [rank]\n FROM (\n SELECT\n [orders___region] AS [orders___region],\n [orders___rank] AS [orders___rank]\n FROM (\n SELECT\n [orders___region],\n [orders___amount_sum_partition_by_region],\n [orders___ok_amount_sum_partition_by_region],\n [orders___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS [orders___region],\n _cm_rank.[rank] AS [orders___gr],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___s]\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON (\n orders.region = _cm_rank.[region]\n OR (\n orders.region IS NULL AND _cm_rank.[region] IS NULL\n )\n )\nGROUP BY\n orders.region,\n _cm_rank.[rank]", - "lift/dim_subset_rank::bigquery": "WITH _base_2 AS (\n SELECT\n orders.city AS `orders___city`,\n orders.region AS `orders___region`,\n SUM(orders.amount) AS `orders___amount_sum_partition_by_city_region`\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS `orders___region`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_region`\n FROM orders AS orders\n GROUP BY\n orders.region\n), base_2 AS (\n SELECT\n _base_2.`orders___city`,\n _base_2.`orders___region`,\n _base_2.`orders___amount_sum_partition_by_city_region`,\n _cm_orders__amount_sum_partition_by_region.`orders___amount_sum_partition_by_region` AS `orders___amount_sum_partition_by_region`\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.`orders___region` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.`orders___region`\n), step1 AS (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___amount_sum_partition_by_city_region`,\n `orders___amount_sum_partition_by_region`,\n RANK() OVER (\n ORDER BY `orders___amount_sum_partition_by_city_region` - `orders___amount_sum_partition_by_region` DESC\n ) AS `orders___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`orders___city` AS `city`,\n _stage_inner.`orders___region` AS `region`,\n _stage_inner.`orders___rank` AS `rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___amount_sum_partition_by_city_region`,\n `orders___amount_sum_partition_by_region`,\n `orders___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS `orders___region`,\n orders.city AS `orders___city`,\n _cm_rank.`rank` AS `orders___sr`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___s`\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.`city`\n AND orders.region IS NOT DISTINCT FROM _cm_rank.`region`\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.`rank`", - "lift/dim_subset_rank::duckdb": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\",\n SUM(orders.amount) AS \"orders.amount_sum_partition_by_city_region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _base_2.\"orders.amount_sum_partition_by_city_region\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_city_region\",\n \"orders.amount_sum_partition_by_region\",\n RANK() OVER (\n ORDER BY \"orders.amount_sum_partition_by_city_region\" - \"orders.amount_sum_partition_by_region\" DESC\n ) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_city_region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.sr\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\"", - "lift/dim_subset_rank::postgres": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\",\n SUM(orders.amount) AS \"orders.amount_sum_partition_by_city_region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _base_2.\"orders.amount_sum_partition_by_city_region\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_city_region\",\n \"orders.amount_sum_partition_by_region\",\n RANK() OVER (\n ORDER BY \"orders.amount_sum_partition_by_city_region\" - \"orders.amount_sum_partition_by_region\" DESC NULLS LAST\n ) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_city_region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.sr\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\"", - "lift/dim_subset_rank::sqlite": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\",\n SUM(orders.amount) AS \"orders.amount_sum_partition_by_city_region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _base_2.\"orders.amount_sum_partition_by_city_region\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.\"orders.region\" IS _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_city_region\",\n \"orders.amount_sum_partition_by_region\",\n RANK() OVER (\n ORDER BY \"orders.amount_sum_partition_by_city_region\" - \"orders.amount_sum_partition_by_region\" DESC\n ) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_city_region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.sr\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS _cm_rank.\"city\" AND orders.region IS _cm_rank.\"region\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\"", - "lift/dim_subset_rank::tsql": "WITH _base_2 AS (\n SELECT\n orders.city AS [orders___city],\n orders.region AS [orders___region],\n SUM(orders.amount) AS [orders___amount_sum_partition_by_city_region]\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS [orders___region],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_region]\n FROM orders AS orders\n GROUP BY\n orders.region\n), base_2 AS (\n SELECT\n _base_2.[orders___city] AS [orders___city],\n _base_2.[orders___region] AS [orders___region],\n _base_2.[orders___amount_sum_partition_by_city_region] AS [orders___amount_sum_partition_by_city_region],\n _cm_orders__amount_sum_partition_by_region.[orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region]\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON (\n _base_2.[orders___region] = _cm_orders__amount_sum_partition_by_region.[orders___region]\n OR (\n _base_2.[orders___region] IS NULL\n AND _cm_orders__amount_sum_partition_by_region.[orders___region] IS NULL\n )\n )\n), step1 AS (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___amount_sum_partition_by_city_region] AS [orders___amount_sum_partition_by_city_region],\n [orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n RANK() OVER (\n ORDER BY [orders___amount_sum_partition_by_city_region] - [orders___amount_sum_partition_by_region] DESC\n ) AS [orders___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[orders___city] AS [city],\n _stage_inner.[orders___region] AS [region],\n _stage_inner.[orders___rank] AS [rank]\n FROM (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___rank] AS [orders___rank]\n FROM (\n SELECT\n [orders___city],\n [orders___region],\n [orders___amount_sum_partition_by_city_region],\n [orders___amount_sum_partition_by_region],\n [orders___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS [orders___region],\n orders.city AS [orders___city],\n _cm_rank.[rank] AS [orders___sr],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___s]\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON (\n orders.city = _cm_rank.[city]\n OR (\n orders.city IS NULL AND _cm_rank.[city] IS NULL\n )\n )\n AND (\n orders.region = _cm_rank.[region]\n OR (\n orders.region IS NULL AND _cm_rank.[region] IS NULL\n )\n )\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.[rank]", - "lift/dual_role::bigquery": "WITH _base_2 AS (\n SELECT\n orders.city AS `orders___city`,\n orders.region AS `orders___region`\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS `orders___region`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_region`\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS `orders___city`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_city`\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.`orders___city`,\n _base_2.`orders___region`,\n _cm_orders__amount_sum_partition_by_region.`orders___amount_sum_partition_by_region` AS `orders___amount_sum_partition_by_region`,\n _cm_orders__amount_sum_partition_by_city.`orders___amount_sum_partition_by_city` AS `orders___amount_sum_partition_by_city`\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.`orders___region` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.`orders___region`\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.`orders___city` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.`orders___city`\n), step1 AS (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___amount_sum_partition_by_region`,\n `orders___amount_sum_partition_by_city`,\n RANK() OVER (\n ORDER BY `orders___amount_sum_partition_by_region` - `orders___amount_sum_partition_by_city` DESC\n ) AS `orders___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`orders___city` AS `city`,\n _stage_inner.`orders___region` AS `region`,\n _stage_inner.`orders___rank` AS `rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___amount_sum_partition_by_region`,\n `orders___amount_sum_partition_by_city`,\n `orders___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _base AS (\n SELECT\n orders.region AS `orders___region`,\n orders.city AS `orders___city`,\n orders.channel AS `orders___channel`,\n _cm_rank.`rank` AS `orders___rr`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___s`\n FROM orders AS orders\n LEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.`city`\n AND orders.region IS NOT DISTINCT FROM _cm_rank.`region`\n GROUP BY\n orders.region,\n orders.city,\n orders.channel,\n _cm_rank.`rank`\n), base AS (\n SELECT\n _base.`orders___region`,\n _base.`orders___city`,\n _base.`orders___channel`,\n _base.`orders___rr`,\n _base.`orders___s`,\n _cm_orders__amount_sum_partition_by_region.`orders___amount_sum_partition_by_region` AS `orders___amount_sum_partition_by_region`,\n _cm_orders__amount_sum_partition_by_city.`orders___amount_sum_partition_by_city` AS `orders___amount_sum_partition_by_city`\n FROM _base\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base.`orders___region` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.`orders___region`\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base.`orders___city` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.`orders___city`\n), step1_2 AS (\n SELECT\n `orders___region`,\n `orders___city`,\n `orders___channel`,\n `orders___rr`,\n `orders___s`,\n `orders___amount_sum_partition_by_region`,\n `orders___amount_sum_partition_by_city`,\n RANK() OVER (\n ORDER BY `orders___amount_sum_partition_by_region` - `orders___amount_sum_partition_by_city` DESC\n ) AS `orders___rm`\n FROM base\n)\nSELECT\n `orders___region`,\n `orders___city`,\n `orders___channel`,\n `orders___rr`,\n `orders___rm`,\n `orders___s`\nFROM (\n SELECT\n `orders___region`,\n `orders___city`,\n `orders___channel`,\n `orders___rr`,\n `orders___s`,\n `orders___amount_sum_partition_by_region`,\n `orders___amount_sum_partition_by_city`,\n `orders___rm`\n FROM step1_2\n) AS _outer", - "lift/dual_role::duckdb": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n RANK() OVER (\n ORDER BY \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n ) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _base AS (\n SELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n orders.channel AS \"orders.channel\",\n _cm_rank.\"rank\" AS \"orders.rr\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.s\"\n FROM orders AS orders\n LEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\n GROUP BY\n orders.region,\n orders.city,\n orders.channel,\n _cm_rank.\"rank\"\n), base AS (\n SELECT\n _base.\"orders.region\",\n _base.\"orders.city\",\n _base.\"orders.channel\",\n _base.\"orders.rr\",\n _base.\"orders.s\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1_2 AS (\n SELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.channel\",\n \"orders.rr\",\n \"orders.s\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n RANK() OVER (\n ORDER BY \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n ) AS \"orders.rm\"\n FROM base\n)\nSELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.channel\",\n \"orders.rr\",\n \"orders.rm\",\n \"orders.s\"\nFROM (\n SELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.channel\",\n \"orders.rr\",\n \"orders.s\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rm\"\n FROM step1_2\n) AS _outer", - "lift/dual_role::postgres": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n RANK() OVER (\n ORDER BY \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC NULLS LAST\n ) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _base AS (\n SELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n orders.channel AS \"orders.channel\",\n _cm_rank.\"rank\" AS \"orders.rr\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.s\"\n FROM orders AS orders\n LEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\n GROUP BY\n orders.region,\n orders.city,\n orders.channel,\n _cm_rank.\"rank\"\n), base AS (\n SELECT\n _base.\"orders.region\",\n _base.\"orders.city\",\n _base.\"orders.channel\",\n _base.\"orders.rr\",\n _base.\"orders.s\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1_2 AS (\n SELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.channel\",\n \"orders.rr\",\n \"orders.s\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n RANK() OVER (\n ORDER BY \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC NULLS LAST\n ) AS \"orders.rm\"\n FROM base\n)\nSELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.channel\",\n \"orders.rr\",\n \"orders.rm\",\n \"orders.s\"\nFROM (\n SELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.channel\",\n \"orders.rr\",\n \"orders.s\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rm\"\n FROM step1_2\n) AS _outer", - "lift/dual_role::sqlite": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.\"orders.region\" IS _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n RANK() OVER (\n ORDER BY \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n ) AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _base AS (\n SELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n orders.channel AS \"orders.channel\",\n _cm_rank.\"rank\" AS \"orders.rr\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.s\"\n FROM orders AS orders\n LEFT JOIN _cm_rank\n ON orders.city IS _cm_rank.\"city\" AND orders.region IS _cm_rank.\"region\"\n GROUP BY\n orders.region,\n orders.city,\n orders.channel,\n _cm_rank.\"rank\"\n), base AS (\n SELECT\n _base.\"orders.region\",\n _base.\"orders.city\",\n _base.\"orders.channel\",\n _base.\"orders.rr\",\n _base.\"orders.s\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base.\"orders.region\" IS _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base.\"orders.city\" IS _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1_2 AS (\n SELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.channel\",\n \"orders.rr\",\n \"orders.s\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n RANK() OVER (\n ORDER BY \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n ) AS \"orders.rm\"\n FROM base\n)\nSELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.channel\",\n \"orders.rr\",\n \"orders.rm\",\n \"orders.s\"\nFROM (\n SELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.channel\",\n \"orders.rr\",\n \"orders.s\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rm\"\n FROM step1_2\n) AS _outer", - "lift/dual_role::tsql": "WITH _base_2 AS (\n SELECT\n orders.city AS [orders___city],\n orders.region AS [orders___region]\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS [orders___region],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_region]\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS [orders___city],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_city]\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.[orders___city] AS [orders___city],\n _base_2.[orders___region] AS [orders___region],\n _cm_orders__amount_sum_partition_by_region.[orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n _cm_orders__amount_sum_partition_by_city.[orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city]\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON (\n _base_2.[orders___region] = _cm_orders__amount_sum_partition_by_region.[orders___region]\n OR (\n _base_2.[orders___region] IS NULL\n AND _cm_orders__amount_sum_partition_by_region.[orders___region] IS NULL\n )\n )\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON (\n _base_2.[orders___city] = _cm_orders__amount_sum_partition_by_city.[orders___city]\n OR (\n _base_2.[orders___city] IS NULL\n AND _cm_orders__amount_sum_partition_by_city.[orders___city] IS NULL\n )\n )\n), step1 AS (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n [orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city],\n RANK() OVER (\n ORDER BY [orders___amount_sum_partition_by_region] - [orders___amount_sum_partition_by_city] DESC\n ) AS [orders___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[orders___city] AS [city],\n _stage_inner.[orders___region] AS [region],\n _stage_inner.[orders___rank] AS [rank]\n FROM (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___rank] AS [orders___rank]\n FROM (\n SELECT\n [orders___city],\n [orders___region],\n [orders___amount_sum_partition_by_region],\n [orders___amount_sum_partition_by_city],\n [orders___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _base AS (\n SELECT\n orders.region AS [orders___region],\n orders.city AS [orders___city],\n orders.channel AS [orders___channel],\n _cm_rank.[rank] AS [orders___rr],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___s]\n FROM orders AS orders\n LEFT JOIN _cm_rank\n ON (\n orders.city = _cm_rank.[city]\n OR (\n orders.city IS NULL AND _cm_rank.[city] IS NULL\n )\n )\n AND (\n orders.region = _cm_rank.[region]\n OR (\n orders.region IS NULL AND _cm_rank.[region] IS NULL\n )\n )\n GROUP BY\n orders.region,\n orders.city,\n orders.channel,\n _cm_rank.[rank]\n), base AS (\n SELECT\n _base.[orders___region] AS [orders___region],\n _base.[orders___city] AS [orders___city],\n _base.[orders___channel] AS [orders___channel],\n _base.[orders___rr] AS [orders___rr],\n _base.[orders___s] AS [orders___s],\n _cm_orders__amount_sum_partition_by_region.[orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n _cm_orders__amount_sum_partition_by_city.[orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city]\n FROM _base\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON (\n _base.[orders___region] = _cm_orders__amount_sum_partition_by_region.[orders___region]\n OR (\n _base.[orders___region] IS NULL\n AND _cm_orders__amount_sum_partition_by_region.[orders___region] IS NULL\n )\n )\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON (\n _base.[orders___city] = _cm_orders__amount_sum_partition_by_city.[orders___city]\n OR (\n _base.[orders___city] IS NULL\n AND _cm_orders__amount_sum_partition_by_city.[orders___city] IS NULL\n )\n )\n), step1_2 AS (\n SELECT\n [orders___region] AS [orders___region],\n [orders___city] AS [orders___city],\n [orders___channel] AS [orders___channel],\n [orders___rr] AS [orders___rr],\n [orders___s] AS [orders___s],\n [orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n [orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city],\n RANK() OVER (\n ORDER BY [orders___amount_sum_partition_by_region] - [orders___amount_sum_partition_by_city] DESC\n ) AS [orders___rm]\n FROM base\n)\nSELECT\n [orders___region],\n [orders___city],\n [orders___channel],\n [orders___rr],\n [orders___rm],\n [orders___s]\nFROM (\n SELECT\n [orders___region],\n [orders___city],\n [orders___channel],\n [orders___rr],\n [orders___s],\n [orders___amount_sum_partition_by_region],\n [orders___amount_sum_partition_by_city],\n [orders___rm]\n FROM step1_2\n) AS _outer", + "lift/dim_explicit_partition::bigquery": "WITH _base_2 AS (\n SELECT\n orders.city AS `orders___city`,\n orders.region AS `orders___region`\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS `orders___region`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_region`\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS `orders___city`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_city`\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.`orders___city`,\n _base_2.`orders___region`,\n _cm_orders__amount_sum_partition_by_region.`orders___amount_sum_partition_by_region` AS `orders___amount_sum_partition_by_region`,\n _cm_orders__amount_sum_partition_by_city.`orders___amount_sum_partition_by_city` AS `orders___amount_sum_partition_by_city`\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.`orders___region` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.`orders___region`\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.`orders___city` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.`orders___city`\n), step1 AS (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___amount_sum_partition_by_region`,\n `orders___amount_sum_partition_by_city`,\n CASE\n WHEN `orders___amount_sum_partition_by_region` - `orders___amount_sum_partition_by_city` IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY `orders___region`, CASE\n WHEN `orders___amount_sum_partition_by_region` - `orders___amount_sum_partition_by_city` IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY `orders___amount_sum_partition_by_region` - `orders___amount_sum_partition_by_city` DESC\n )\n END AS `orders___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`orders___city` AS `city`,\n _stage_inner.`orders___region` AS `region`,\n _stage_inner.`orders___rank` AS `rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___amount_sum_partition_by_region`,\n `orders___amount_sum_partition_by_city`,\n `orders___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS `orders___region`,\n orders.city AS `orders___city`,\n _cm_rank.`rank` AS `orders___er`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___s`\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.`city`\n AND orders.region IS NOT DISTINCT FROM _cm_rank.`region`\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.`rank`", + "lift/dim_explicit_partition::duckdb": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n CASE\n WHEN \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY \"orders.region\", CASE\n WHEN \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.er\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\"", + "lift/dim_explicit_partition::postgres": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n CASE\n WHEN \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY \"orders.region\", CASE\n WHEN \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.er\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\"", + "lift/dim_explicit_partition::sqlite": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.\"orders.region\" IS _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n CASE\n WHEN \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY \"orders.region\", CASE\n WHEN \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.er\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS _cm_rank.\"city\" AND orders.region IS _cm_rank.\"region\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\"", + "lift/dim_explicit_partition::tsql": "WITH _base_2 AS (\n SELECT\n orders.city AS [orders___city],\n orders.region AS [orders___region]\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS [orders___region],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_region]\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS [orders___city],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_city]\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.[orders___city] AS [orders___city],\n _base_2.[orders___region] AS [orders___region],\n _cm_orders__amount_sum_partition_by_region.[orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n _cm_orders__amount_sum_partition_by_city.[orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city]\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON (\n _base_2.[orders___region] = _cm_orders__amount_sum_partition_by_region.[orders___region]\n OR (\n _base_2.[orders___region] IS NULL\n AND _cm_orders__amount_sum_partition_by_region.[orders___region] IS NULL\n )\n )\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON (\n _base_2.[orders___city] = _cm_orders__amount_sum_partition_by_city.[orders___city]\n OR (\n _base_2.[orders___city] IS NULL\n AND _cm_orders__amount_sum_partition_by_city.[orders___city] IS NULL\n )\n )\n), step1 AS (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n [orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city],\n CASE\n WHEN [orders___amount_sum_partition_by_region] - [orders___amount_sum_partition_by_city] IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY [orders___region], CASE\n WHEN [orders___amount_sum_partition_by_region] - [orders___amount_sum_partition_by_city] IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY [orders___amount_sum_partition_by_region] - [orders___amount_sum_partition_by_city] DESC\n )\n END AS [orders___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[orders___city] AS [city],\n _stage_inner.[orders___region] AS [region],\n _stage_inner.[orders___rank] AS [rank]\n FROM (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___rank] AS [orders___rank]\n FROM (\n SELECT\n [orders___city],\n [orders___region],\n [orders___amount_sum_partition_by_region],\n [orders___amount_sum_partition_by_city],\n [orders___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS [orders___region],\n orders.city AS [orders___city],\n _cm_rank.[rank] AS [orders___er],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___s]\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON (\n orders.city = _cm_rank.[city]\n OR (\n orders.city IS NULL AND _cm_rank.[city] IS NULL\n )\n )\n AND (\n orders.region = _cm_rank.[region]\n OR (\n orders.region IS NULL AND _cm_rank.[region] IS NULL\n )\n )\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.[rank]", + "lift/dim_keyless_share::bigquery": "WITH _base_2 AS (\n SELECT\n orders.region AS `orders___region`,\n SUM(orders.amount) AS `orders___amount_sum_partition_by_region`\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by AS (\n SELECT\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by`\n FROM orders AS orders\n), base_2 AS (\n SELECT\n _base_2.`orders___region`,\n _base_2.`orders___amount_sum_partition_by_region`,\n _cm_orders__amount_sum_partition_by.`orders___amount_sum_partition_by` AS `orders___amount_sum_partition_by`\n FROM _base_2\n CROSS JOIN _cm_orders__amount_sum_partition_by\n), step1 AS (\n SELECT\n `orders___region`,\n `orders___amount_sum_partition_by_region`,\n `orders___amount_sum_partition_by`,\n CASE\n WHEN `orders___amount_sum_partition_by_region` / `orders___amount_sum_partition_by` IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN `orders___amount_sum_partition_by_region` / `orders___amount_sum_partition_by` IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY `orders___amount_sum_partition_by_region` / `orders___amount_sum_partition_by` DESC\n )\n END AS `orders___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`orders___region` AS `region`,\n _stage_inner.`orders___rank` AS `rank`\n FROM (\n SELECT\n `orders___region`,\n `orders___rank`\n FROM (\n SELECT\n `orders___region`,\n `orders___amount_sum_partition_by_region`,\n `orders___amount_sum_partition_by`,\n `orders___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS `orders___region`,\n _cm_rank.`rank` AS `orders___kr`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___s`\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.region IS NOT DISTINCT FROM _cm_rank.`region`\nGROUP BY\n orders.region,\n _cm_rank.`rank`", + "lift/dim_keyless_share::duckdb": "WITH _base_2 AS (\n SELECT\n orders.region AS \"orders.region\",\n SUM(orders.amount) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by AS (\n SELECT\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by\"\n FROM orders AS orders\n), base_2 AS (\n SELECT\n _base_2.\"orders.region\",\n _base_2.\"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by.\"orders.amount_sum_partition_by\" AS \"orders.amount_sum_partition_by\"\n FROM _base_2\n CROSS JOIN _cm_orders__amount_sum_partition_by\n), step1 AS (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by\",\n CASE\n WHEN \"orders.amount_sum_partition_by_region\" / \"orders.amount_sum_partition_by\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_sum_partition_by_region\" / \"orders.amount_sum_partition_by\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_partition_by_region\" / \"orders.amount_sum_partition_by\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n _cm_rank.\"rank\" AS \"orders.kr\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n _cm_rank.\"rank\"", + "lift/dim_keyless_share::postgres": "WITH _base_2 AS (\n SELECT\n orders.region AS \"orders.region\",\n SUM(orders.amount) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by AS (\n SELECT\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by\"\n FROM orders AS orders\n), base_2 AS (\n SELECT\n _base_2.\"orders.region\",\n _base_2.\"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by.\"orders.amount_sum_partition_by\" AS \"orders.amount_sum_partition_by\"\n FROM _base_2\n CROSS JOIN _cm_orders__amount_sum_partition_by\n), step1 AS (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by\",\n CASE\n WHEN CAST(\"orders.amount_sum_partition_by_region\" AS DOUBLE PRECISION) / \"orders.amount_sum_partition_by\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN CAST(\"orders.amount_sum_partition_by_region\" AS DOUBLE PRECISION) / \"orders.amount_sum_partition_by\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY CAST(\"orders.amount_sum_partition_by_region\" AS DOUBLE PRECISION) / \"orders.amount_sum_partition_by\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n _cm_rank.\"rank\" AS \"orders.kr\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n _cm_rank.\"rank\"", + "lift/dim_keyless_share::sqlite": "WITH _base_2 AS (\n SELECT\n orders.region AS \"orders.region\",\n SUM(orders.amount) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by AS (\n SELECT\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by\"\n FROM orders AS orders\n), base_2 AS (\n SELECT\n _base_2.\"orders.region\",\n _base_2.\"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by.\"orders.amount_sum_partition_by\" AS \"orders.amount_sum_partition_by\"\n FROM _base_2\n CROSS JOIN _cm_orders__amount_sum_partition_by\n), step1 AS (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by\",\n CASE\n WHEN CAST(\"orders.amount_sum_partition_by_region\" AS REAL) / \"orders.amount_sum_partition_by\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN CAST(\"orders.amount_sum_partition_by_region\" AS REAL) / \"orders.amount_sum_partition_by\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY CAST(\"orders.amount_sum_partition_by_region\" AS REAL) / \"orders.amount_sum_partition_by\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n _cm_rank.\"rank\" AS \"orders.kr\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.region IS _cm_rank.\"region\"\nGROUP BY\n orders.region,\n _cm_rank.\"rank\"", + "lift/dim_keyless_share::tsql": "WITH _base_2 AS (\n SELECT\n orders.region AS [orders___region],\n SUM(orders.amount) AS [orders___amount_sum_partition_by_region]\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by AS (\n SELECT\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by]\n FROM orders AS orders\n), base_2 AS (\n SELECT\n _base_2.[orders___region] AS [orders___region],\n _base_2.[orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n _cm_orders__amount_sum_partition_by.[orders___amount_sum_partition_by] AS [orders___amount_sum_partition_by]\n FROM _base_2\n CROSS JOIN _cm_orders__amount_sum_partition_by\n), step1 AS (\n SELECT\n [orders___region] AS [orders___region],\n [orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n [orders___amount_sum_partition_by] AS [orders___amount_sum_partition_by],\n CASE\n WHEN CAST([orders___amount_sum_partition_by_region] AS FLOAT) / [orders___amount_sum_partition_by] IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN CAST([orders___amount_sum_partition_by_region] AS FLOAT) / [orders___amount_sum_partition_by] IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY CAST([orders___amount_sum_partition_by_region] AS FLOAT) / [orders___amount_sum_partition_by] DESC\n )\n END AS [orders___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[orders___region] AS [region],\n _stage_inner.[orders___rank] AS [rank]\n FROM (\n SELECT\n [orders___region] AS [orders___region],\n [orders___rank] AS [orders___rank]\n FROM (\n SELECT\n [orders___region],\n [orders___amount_sum_partition_by_region],\n [orders___amount_sum_partition_by],\n [orders___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS [orders___region],\n _cm_rank.[rank] AS [orders___kr],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___s]\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON (\n orders.region = _cm_rank.[region]\n OR (\n orders.region IS NULL AND _cm_rank.[region] IS NULL\n )\n )\nGROUP BY\n orders.region,\n _cm_rank.[rank]", + "lift/dim_mixed_first_last::bigquery": "WITH _base_2 AS (\n SELECT\n orders.city AS `orders___city`,\n orders.region AS `orders___region`\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_last_partition_by_region AS (\n SELECT\n _val_0 AS `orders___region`,\n CAST(MAX(CASE WHEN _ranked_rn = 1 THEN _val_1 END) AS FLOAT64) AS `orders___amount_last_partition_by_region`\n FROM (\n SELECT\n orders.region AS _val_0,\n orders.amount AS _val_1,\n ROW_NUMBER() OVER (PARTITION BY orders.region ORDER BY orders.ordered_at DESC) AS _ranked_rn\n FROM orders AS orders\n ) AS _ranked_src\n GROUP BY\n _val_0\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS `orders___city`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_city`\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.`orders___city`,\n _base_2.`orders___region`,\n _cm_orders__amount_last_partition_by_region.`orders___amount_last_partition_by_region` AS `orders___amount_last_partition_by_region`,\n _cm_orders__amount_sum_partition_by_city.`orders___amount_sum_partition_by_city` AS `orders___amount_sum_partition_by_city`\n FROM _base_2\n LEFT JOIN _cm_orders__amount_last_partition_by_region\n ON _base_2.`orders___region` IS NOT DISTINCT FROM _cm_orders__amount_last_partition_by_region.`orders___region`\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.`orders___city` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.`orders___city`\n), step1 AS (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___amount_last_partition_by_region`,\n `orders___amount_sum_partition_by_city`,\n CASE\n WHEN `orders___amount_last_partition_by_region` - `orders___amount_sum_partition_by_city` IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN `orders___amount_last_partition_by_region` - `orders___amount_sum_partition_by_city` IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY `orders___amount_last_partition_by_region` - `orders___amount_sum_partition_by_city` DESC\n )\n END AS `orders___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`orders___city` AS `city`,\n _stage_inner.`orders___region` AS `region`,\n _stage_inner.`orders___rank` AS `rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___amount_last_partition_by_region`,\n `orders___amount_sum_partition_by_city`,\n `orders___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS `orders___region`,\n orders.city AS `orders___city`,\n _cm_rank.`rank` AS `orders___x`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___s`\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.`city`\n AND orders.region IS NOT DISTINCT FROM _cm_rank.`region`\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.`rank`", + "lift/dim_mixed_first_last::duckdb": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_last_partition_by_region AS (\n SELECT\n _val_0 AS \"orders.region\",\n CAST(MAX(CASE WHEN _ranked_rn = 1 THEN _val_1 END) AS DOUBLE) AS \"orders.amount_last_partition_by_region\"\n FROM (\n SELECT\n orders.region AS _val_0,\n orders.amount AS _val_1,\n ROW_NUMBER() OVER (PARTITION BY orders.region ORDER BY orders.ordered_at DESC) AS _ranked_rn\n FROM orders AS orders\n ) AS _ranked_src\n GROUP BY\n _val_0\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _cm_orders__amount_last_partition_by_region.\"orders.amount_last_partition_by_region\" AS \"orders.amount_last_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_last_partition_by_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_last_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_last_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n CASE\n WHEN \"orders.amount_last_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_last_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_last_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_last_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.x\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\"", + "lift/dim_mixed_first_last::postgres": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_last_partition_by_region AS (\n SELECT\n _val_0 AS \"orders.region\",\n CAST(MAX(CASE WHEN _ranked_rn = 1 THEN _val_1 END) AS DOUBLE PRECISION) AS \"orders.amount_last_partition_by_region\"\n FROM (\n SELECT\n orders.region AS _val_0,\n orders.amount AS _val_1,\n ROW_NUMBER() OVER (PARTITION BY orders.region ORDER BY orders.ordered_at DESC) AS _ranked_rn\n FROM orders AS orders\n ) AS _ranked_src\n GROUP BY\n _val_0\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _cm_orders__amount_last_partition_by_region.\"orders.amount_last_partition_by_region\" AS \"orders.amount_last_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_last_partition_by_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_last_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_last_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n CASE\n WHEN \"orders.amount_last_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_last_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_last_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_last_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.x\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\"", + "lift/dim_mixed_first_last::sqlite": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_last_partition_by_region AS (\n SELECT\n _val_0 AS \"orders.region\",\n CAST(MAX(CASE WHEN _ranked_rn = 1 THEN _val_1 END) AS REAL) AS \"orders.amount_last_partition_by_region\"\n FROM (\n SELECT\n orders.region AS _val_0,\n orders.amount AS _val_1,\n ROW_NUMBER() OVER (PARTITION BY orders.region ORDER BY orders.ordered_at DESC) AS _ranked_rn\n FROM orders AS orders\n ) AS _ranked_src\n GROUP BY\n _val_0\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _cm_orders__amount_last_partition_by_region.\"orders.amount_last_partition_by_region\" AS \"orders.amount_last_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_last_partition_by_region\n ON _base_2.\"orders.region\" IS _cm_orders__amount_last_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_last_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n CASE\n WHEN \"orders.amount_last_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_last_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_last_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_last_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.x\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS _cm_rank.\"city\" AND orders.region IS _cm_rank.\"region\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\"", + "lift/dim_mixed_first_last::tsql": "WITH _base_2 AS (\n SELECT\n orders.city AS [orders___city],\n orders.region AS [orders___region]\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_last_partition_by_region AS (\n SELECT\n _val_0 AS [orders___region],\n CAST(MAX(CASE WHEN _ranked_rn = 1 THEN _val_1 END) AS FLOAT) AS [orders___amount_last_partition_by_region]\n FROM (\n SELECT\n orders.region AS _val_0,\n orders.amount AS _val_1,\n ROW_NUMBER() OVER (PARTITION BY orders.region ORDER BY orders.ordered_at DESC) AS _ranked_rn\n FROM orders AS orders\n ) AS _ranked_src\n GROUP BY\n _val_0\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS [orders___city],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_city]\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.[orders___city] AS [orders___city],\n _base_2.[orders___region] AS [orders___region],\n _cm_orders__amount_last_partition_by_region.[orders___amount_last_partition_by_region] AS [orders___amount_last_partition_by_region],\n _cm_orders__amount_sum_partition_by_city.[orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city]\n FROM _base_2\n LEFT JOIN _cm_orders__amount_last_partition_by_region\n ON (\n _base_2.[orders___region] = _cm_orders__amount_last_partition_by_region.[orders___region]\n OR (\n _base_2.[orders___region] IS NULL\n AND _cm_orders__amount_last_partition_by_region.[orders___region] IS NULL\n )\n )\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON (\n _base_2.[orders___city] = _cm_orders__amount_sum_partition_by_city.[orders___city]\n OR (\n _base_2.[orders___city] IS NULL\n AND _cm_orders__amount_sum_partition_by_city.[orders___city] IS NULL\n )\n )\n), step1 AS (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___amount_last_partition_by_region] AS [orders___amount_last_partition_by_region],\n [orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city],\n CASE\n WHEN [orders___amount_last_partition_by_region] - [orders___amount_sum_partition_by_city] IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN [orders___amount_last_partition_by_region] - [orders___amount_sum_partition_by_city] IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY [orders___amount_last_partition_by_region] - [orders___amount_sum_partition_by_city] DESC\n )\n END AS [orders___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[orders___city] AS [city],\n _stage_inner.[orders___region] AS [region],\n _stage_inner.[orders___rank] AS [rank]\n FROM (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___rank] AS [orders___rank]\n FROM (\n SELECT\n [orders___city],\n [orders___region],\n [orders___amount_last_partition_by_region],\n [orders___amount_sum_partition_by_city],\n [orders___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS [orders___region],\n orders.city AS [orders___city],\n _cm_rank.[rank] AS [orders___x],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___s]\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON (\n orders.city = _cm_rank.[city]\n OR (\n orders.city IS NULL AND _cm_rank.[city] IS NULL\n )\n )\n AND (\n orders.region = _cm_rank.[region]\n OR (\n orders.region IS NULL AND _cm_rank.[region] IS NULL\n )\n )\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.[rank]", + "lift/dim_mixed_rank::bigquery": "WITH _base_2 AS (\n SELECT\n orders.city AS `orders___city`,\n orders.region AS `orders___region`\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS `orders___region`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_region`\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS `orders___city`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_city`\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.`orders___city`,\n _base_2.`orders___region`,\n _cm_orders__amount_sum_partition_by_region.`orders___amount_sum_partition_by_region` AS `orders___amount_sum_partition_by_region`,\n _cm_orders__amount_sum_partition_by_city.`orders___amount_sum_partition_by_city` AS `orders___amount_sum_partition_by_city`\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.`orders___region` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.`orders___region`\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.`orders___city` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.`orders___city`\n), step1 AS (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___amount_sum_partition_by_region`,\n `orders___amount_sum_partition_by_city`,\n CASE\n WHEN `orders___amount_sum_partition_by_region` - `orders___amount_sum_partition_by_city` IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN `orders___amount_sum_partition_by_region` - `orders___amount_sum_partition_by_city` IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY `orders___amount_sum_partition_by_region` - `orders___amount_sum_partition_by_city` DESC\n )\n END AS `orders___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`orders___city` AS `city`,\n _stage_inner.`orders___region` AS `region`,\n _stage_inner.`orders___rank` AS `rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___amount_sum_partition_by_region`,\n `orders___amount_sum_partition_by_city`,\n `orders___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS `orders___region`,\n orders.city AS `orders___city`,\n _cm_rank.`rank` AS `orders___rr`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___s`\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.`city`\n AND orders.region IS NOT DISTINCT FROM _cm_rank.`region`\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.`rank`", + "lift/dim_mixed_rank::duckdb": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n CASE\n WHEN \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.rr\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\"", + "lift/dim_mixed_rank::postgres": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n CASE\n WHEN \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.rr\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\"", + "lift/dim_mixed_rank::sqlite": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.\"orders.region\" IS _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n CASE\n WHEN \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.rr\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS _cm_rank.\"city\" AND orders.region IS _cm_rank.\"region\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\"", + "lift/dim_mixed_rank::tsql": "WITH _base_2 AS (\n SELECT\n orders.city AS [orders___city],\n orders.region AS [orders___region]\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS [orders___region],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_region]\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS [orders___city],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_city]\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.[orders___city] AS [orders___city],\n _base_2.[orders___region] AS [orders___region],\n _cm_orders__amount_sum_partition_by_region.[orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n _cm_orders__amount_sum_partition_by_city.[orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city]\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON (\n _base_2.[orders___region] = _cm_orders__amount_sum_partition_by_region.[orders___region]\n OR (\n _base_2.[orders___region] IS NULL\n AND _cm_orders__amount_sum_partition_by_region.[orders___region] IS NULL\n )\n )\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON (\n _base_2.[orders___city] = _cm_orders__amount_sum_partition_by_city.[orders___city]\n OR (\n _base_2.[orders___city] IS NULL\n AND _cm_orders__amount_sum_partition_by_city.[orders___city] IS NULL\n )\n )\n), step1 AS (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n [orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city],\n CASE\n WHEN [orders___amount_sum_partition_by_region] - [orders___amount_sum_partition_by_city] IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN [orders___amount_sum_partition_by_region] - [orders___amount_sum_partition_by_city] IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY [orders___amount_sum_partition_by_region] - [orders___amount_sum_partition_by_city] DESC\n )\n END AS [orders___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[orders___city] AS [city],\n _stage_inner.[orders___region] AS [region],\n _stage_inner.[orders___rank] AS [rank]\n FROM (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___rank] AS [orders___rank]\n FROM (\n SELECT\n [orders___city],\n [orders___region],\n [orders___amount_sum_partition_by_region],\n [orders___amount_sum_partition_by_city],\n [orders___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS [orders___region],\n orders.city AS [orders___city],\n _cm_rank.[rank] AS [orders___rr],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___s]\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON (\n orders.city = _cm_rank.[city]\n OR (\n orders.city IS NULL AND _cm_rank.[city] IS NULL\n )\n )\n AND (\n orders.region = _cm_rank.[region]\n OR (\n orders.region IS NULL AND _cm_rank.[region] IS NULL\n )\n )\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.[rank]", + "lift/dim_mixed_windowed::bigquery": "WITH _base_2 AS (\n SELECT\n orders.city AS `orders___city`,\n orders.region AS `orders___region`,\n DATE_TRUNC(orders.ordered_at, MONTH) AS `orders___ordered_at_month`\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region,\n DATE_TRUNC(orders.ordered_at, MONTH)\n), _cm_orders__amount_sum_window_90d_partition_by_region AS (\n SELECT\n _base.`orders___region`,\n _base.`orders___ordered_at_month`,\n CAST(SUM(_src._w_value) AS FLOAT64) AS `orders___amount_sum_window_90d_partition_by_region`\n FROM (\n SELECT\n orders.region AS `orders___region`,\n DATE_TRUNC(orders.ordered_at, MONTH) AS `orders___ordered_at_month`\n FROM orders AS orders\n GROUP BY\n orders.region,\n DATE_TRUNC(orders.ordered_at, MONTH)\n ) AS _base\n LEFT JOIN (\n SELECT\n orders.region AS _w_dim_0,\n orders.ordered_at AS _w_time,\n orders.amount AS _w_value\n FROM orders AS orders\n ) AS _src\n ON _src._w_dim_0 IS NOT DISTINCT FROM _base.`orders___region`\n AND _src._w_time >= TIMESTAMP_ADD(\n CAST(DATETIME_ADD(CAST(_base.`orders___ordered_at_month` AS DATETIME), INTERVAL 1 MONTH) AS TIMESTAMP),\n INTERVAL -90 DAY\n )\n AND _src._w_time < CAST(DATETIME_ADD(CAST(_base.`orders___ordered_at_month` AS DATETIME), INTERVAL 1 MONTH) AS TIMESTAMP)\n GROUP BY\n _base.`orders___region`,\n _base.`orders___ordered_at_month`\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS `orders___city`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_city`\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.`orders___city`,\n _base_2.`orders___region`,\n _base_2.`orders___ordered_at_month`,\n _cm_orders__amount_sum_window_90d_partition_by_region.`orders___amount_sum_window_90d_partition_by_region` AS `orders___amount_sum_window_90d_partition_by_region`,\n _cm_orders__amount_sum_partition_by_city.`orders___amount_sum_partition_by_city` AS `orders___amount_sum_partition_by_city`\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_window_90d_partition_by_region\n ON _base_2.`orders___region` IS NOT DISTINCT FROM _cm_orders__amount_sum_window_90d_partition_by_region.`orders___region`\n AND _base_2.`orders___ordered_at_month` IS NOT DISTINCT FROM _cm_orders__amount_sum_window_90d_partition_by_region.`orders___ordered_at_month`\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.`orders___city` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.`orders___city`\n), step1 AS (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___ordered_at_month`,\n `orders___amount_sum_window_90d_partition_by_region`,\n `orders___amount_sum_partition_by_city`,\n CASE\n WHEN `orders___amount_sum_window_90d_partition_by_region` - `orders___amount_sum_partition_by_city` IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN `orders___amount_sum_window_90d_partition_by_region` - `orders___amount_sum_partition_by_city` IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY `orders___amount_sum_window_90d_partition_by_region` - `orders___amount_sum_partition_by_city` DESC\n )\n END AS `orders___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`orders___city` AS `city`,\n _stage_inner.`orders___region` AS `region`,\n _stage_inner.`orders___ordered_at_month` AS `ordered_at_month`,\n _stage_inner.`orders___rank` AS `rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___ordered_at_month`,\n `orders___rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___ordered_at_month`,\n `orders___amount_sum_window_90d_partition_by_region`,\n `orders___amount_sum_partition_by_city`,\n `orders___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS `orders___region`,\n orders.city AS `orders___city`,\n _cm_rank.`rank` AS `orders___x`,\n DATE_TRUNC(orders.ordered_at, MONTH) AS `orders___ordered_at`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___s`\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.`city`\n AND orders.region IS NOT DISTINCT FROM _cm_rank.`region`\n AND DATE_TRUNC(orders.ordered_at, MONTH) IS NOT DISTINCT FROM _cm_rank.`ordered_at_month`\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.`rank`,\n DATE_TRUNC(orders.ordered_at, MONTH)", + "lift/dim_mixed_windowed::duckdb": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at_month\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region,\n DATE_TRUNC('MONTH', orders.ordered_at)\n), _cm_orders__amount_sum_window_90d_partition_by_region AS (\n SELECT\n _base.\"orders.region\",\n _base.\"orders.ordered_at_month\",\n CAST(SUM(_src._w_value) AS DOUBLE) AS \"orders.amount_sum_window_90d_partition_by_region\"\n FROM (\n SELECT\n orders.region AS \"orders.region\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at_month\"\n FROM orders AS orders\n GROUP BY\n orders.region,\n DATE_TRUNC('MONTH', orders.ordered_at)\n ) AS _base\n LEFT JOIN (\n SELECT\n orders.region AS _w_dim_0,\n orders.ordered_at AS _w_time,\n orders.amount AS _w_value\n FROM orders AS orders\n ) AS _src\n ON _src._w_dim_0 IS NOT DISTINCT FROM _base.\"orders.region\"\n AND _src._w_time >= _base.\"orders.ordered_at_month\" + INTERVAL 1 MONTH - INTERVAL 90 DAY\n AND _src._w_time < _base.\"orders.ordered_at_month\" + INTERVAL 1 MONTH\n GROUP BY\n _base.\"orders.region\",\n _base.\"orders.ordered_at_month\"\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _base_2.\"orders.ordered_at_month\",\n _cm_orders__amount_sum_window_90d_partition_by_region.\"orders.amount_sum_window_90d_partition_by_region\" AS \"orders.amount_sum_window_90d_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_window_90d_partition_by_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_window_90d_partition_by_region.\"orders.region\"\n AND _base_2.\"orders.ordered_at_month\" IS NOT DISTINCT FROM _cm_orders__amount_sum_window_90d_partition_by_region.\"orders.ordered_at_month\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_window_90d_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n CASE\n WHEN \"orders.amount_sum_window_90d_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_sum_window_90d_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_window_90d_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.ordered_at_month\" AS \"ordered_at_month\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_window_90d_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.x\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\n AND DATE_TRUNC('MONTH', orders.ordered_at) IS NOT DISTINCT FROM _cm_rank.\"ordered_at_month\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\",\n DATE_TRUNC('MONTH', orders.ordered_at)", + "lift/dim_mixed_windowed::postgres": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at_month\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region,\n DATE_TRUNC('MONTH', orders.ordered_at)\n), _cm_orders__amount_sum_window_90d_partition_by_region AS (\n SELECT\n _base.\"orders.region\",\n _base.\"orders.ordered_at_month\",\n CAST(SUM(_src._w_value) AS DOUBLE PRECISION) AS \"orders.amount_sum_window_90d_partition_by_region\"\n FROM (\n SELECT\n orders.region AS \"orders.region\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at_month\"\n FROM orders AS orders\n GROUP BY\n orders.region,\n DATE_TRUNC('MONTH', orders.ordered_at)\n ) AS _base\n LEFT JOIN (\n SELECT\n orders.region AS _w_dim_0,\n orders.ordered_at AS _w_time,\n orders.amount AS _w_value\n FROM orders AS orders\n ) AS _src\n ON _src._w_dim_0 IS NOT DISTINCT FROM _base.\"orders.region\"\n AND _src._w_time >= _base.\"orders.ordered_at_month\" + INTERVAL '1 MONTH' - INTERVAL '90 DAY'\n AND _src._w_time < _base.\"orders.ordered_at_month\" + INTERVAL '1 MONTH'\n GROUP BY\n _base.\"orders.region\",\n _base.\"orders.ordered_at_month\"\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _base_2.\"orders.ordered_at_month\",\n _cm_orders__amount_sum_window_90d_partition_by_region.\"orders.amount_sum_window_90d_partition_by_region\" AS \"orders.amount_sum_window_90d_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_window_90d_partition_by_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_window_90d_partition_by_region.\"orders.region\"\n AND _base_2.\"orders.ordered_at_month\" IS NOT DISTINCT FROM _cm_orders__amount_sum_window_90d_partition_by_region.\"orders.ordered_at_month\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_window_90d_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n CASE\n WHEN \"orders.amount_sum_window_90d_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_sum_window_90d_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_window_90d_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.ordered_at_month\" AS \"ordered_at_month\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_window_90d_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.x\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\n AND DATE_TRUNC('MONTH', orders.ordered_at) IS NOT DISTINCT FROM _cm_rank.\"ordered_at_month\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\",\n DATE_TRUNC('MONTH', orders.ordered_at)", + "lift/dim_mixed_windowed::sqlite": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\",\n STRFTIME('%Y-%m-01', orders.ordered_at) AS \"orders.ordered_at_month\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region,\n STRFTIME('%Y-%m-01', orders.ordered_at)\n), _cm_orders__amount_sum_window_90d_partition_by_region AS (\n SELECT\n _base.\"orders.region\",\n _base.\"orders.ordered_at_month\",\n CAST(SUM(_src._w_value) AS REAL) AS \"orders.amount_sum_window_90d_partition_by_region\"\n FROM (\n SELECT\n orders.region AS \"orders.region\",\n STRFTIME('%Y-%m-01', orders.ordered_at) AS \"orders.ordered_at_month\"\n FROM orders AS orders\n GROUP BY\n orders.region,\n STRFTIME('%Y-%m-01', orders.ordered_at)\n ) AS _base\n LEFT JOIN (\n SELECT\n orders.region AS _w_dim_0,\n orders.ordered_at AS _w_time,\n orders.amount AS _w_value\n FROM orders AS orders\n ) AS _src\n ON _src._w_dim_0 IS _base.\"orders.region\"\n AND DATETIME(_src._w_time) >= SLAYER_DATE_ADD(SLAYER_DATE_ADD(_base.\"orders.ordered_at_month\", 1, 'month'), -90, 'day')\n AND DATETIME(_src._w_time) < SLAYER_DATE_ADD(_base.\"orders.ordered_at_month\", 1, 'month')\n GROUP BY\n _base.\"orders.region\",\n _base.\"orders.ordered_at_month\"\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _base_2.\"orders.ordered_at_month\",\n _cm_orders__amount_sum_window_90d_partition_by_region.\"orders.amount_sum_window_90d_partition_by_region\" AS \"orders.amount_sum_window_90d_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_window_90d_partition_by_region\n ON _base_2.\"orders.region\" IS _cm_orders__amount_sum_window_90d_partition_by_region.\"orders.region\"\n AND _base_2.\"orders.ordered_at_month\" IS _cm_orders__amount_sum_window_90d_partition_by_region.\"orders.ordered_at_month\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_window_90d_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n CASE\n WHEN \"orders.amount_sum_window_90d_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_sum_window_90d_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_window_90d_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.ordered_at_month\" AS \"ordered_at_month\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_window_90d_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.x\",\n STRFTIME('%Y-%m-01', orders.ordered_at) AS \"orders.ordered_at\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS _cm_rank.\"city\"\n AND orders.region IS _cm_rank.\"region\"\n AND STRFTIME('%Y-%m-01', orders.ordered_at) IS _cm_rank.\"ordered_at_month\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\",\n STRFTIME('%Y-%m-01', orders.ordered_at)", + "lift/dim_mixed_windowed::tsql": "WITH _base_2 AS (\n SELECT\n orders.city AS [orders___city],\n orders.region AS [orders___region],\n DATETRUNC(month, orders.ordered_at) AS [orders___ordered_at_month]\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region,\n DATETRUNC(month, orders.ordered_at)\n), _cm_orders__amount_sum_window_90d_partition_by_region AS (\n SELECT\n _base.[orders___region] AS [orders___region],\n _base.[orders___ordered_at_month] AS [orders___ordered_at_month],\n CAST(SUM(_src._w_value) AS FLOAT) AS [orders___amount_sum_window_90d_partition_by_region]\n FROM (\n SELECT\n orders.region AS [orders___region],\n DATETRUNC(month, orders.ordered_at) AS [orders___ordered_at_month]\n FROM orders AS orders\n GROUP BY\n orders.region,\n DATETRUNC(month, orders.ordered_at)\n ) AS _base\n LEFT JOIN (\n SELECT\n orders.region AS _w_dim_0,\n orders.ordered_at AS _w_time,\n orders.amount AS _w_value\n FROM orders AS orders\n ) AS _src\n ON (\n _src._w_dim_0 = _base.[orders___region]\n OR (\n _src._w_dim_0 IS NULL AND _base.[orders___region] IS NULL\n )\n )\n AND _src._w_time >= DATEADD(DAY, -90, DATEADD(MONTH, 1, _base.[orders___ordered_at_month]))\n AND _src._w_time < DATEADD(MONTH, 1, _base.[orders___ordered_at_month])\n GROUP BY\n _base.[orders___region],\n _base.[orders___ordered_at_month]\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS [orders___city],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_city]\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.[orders___city] AS [orders___city],\n _base_2.[orders___region] AS [orders___region],\n _base_2.[orders___ordered_at_month] AS [orders___ordered_at_month],\n _cm_orders__amount_sum_window_90d_partition_by_region.[orders___amount_sum_window_90d_partition_by_region] AS [orders___amount_sum_window_90d_partition_by_region],\n _cm_orders__amount_sum_partition_by_city.[orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city]\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_window_90d_partition_by_region\n ON (\n _base_2.[orders___region] = _cm_orders__amount_sum_window_90d_partition_by_region.[orders___region]\n OR (\n _base_2.[orders___region] IS NULL\n AND _cm_orders__amount_sum_window_90d_partition_by_region.[orders___region] IS NULL\n )\n )\n AND (\n _base_2.[orders___ordered_at_month] = _cm_orders__amount_sum_window_90d_partition_by_region.[orders___ordered_at_month]\n OR (\n _base_2.[orders___ordered_at_month] IS NULL\n AND _cm_orders__amount_sum_window_90d_partition_by_region.[orders___ordered_at_month] IS NULL\n )\n )\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON (\n _base_2.[orders___city] = _cm_orders__amount_sum_partition_by_city.[orders___city]\n OR (\n _base_2.[orders___city] IS NULL\n AND _cm_orders__amount_sum_partition_by_city.[orders___city] IS NULL\n )\n )\n), step1 AS (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___ordered_at_month] AS [orders___ordered_at_month],\n [orders___amount_sum_window_90d_partition_by_region] AS [orders___amount_sum_window_90d_partition_by_region],\n [orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city],\n CASE\n WHEN [orders___amount_sum_window_90d_partition_by_region] - [orders___amount_sum_partition_by_city] IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN [orders___amount_sum_window_90d_partition_by_region] - [orders___amount_sum_partition_by_city] IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY [orders___amount_sum_window_90d_partition_by_region] - [orders___amount_sum_partition_by_city] DESC\n )\n END AS [orders___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[orders___city] AS [city],\n _stage_inner.[orders___region] AS [region],\n _stage_inner.[orders___ordered_at_month] AS [ordered_at_month],\n _stage_inner.[orders___rank] AS [rank]\n FROM (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___ordered_at_month] AS [orders___ordered_at_month],\n [orders___rank] AS [orders___rank]\n FROM (\n SELECT\n [orders___city],\n [orders___region],\n [orders___ordered_at_month],\n [orders___amount_sum_window_90d_partition_by_region],\n [orders___amount_sum_partition_by_city],\n [orders___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS [orders___region],\n orders.city AS [orders___city],\n _cm_rank.[rank] AS [orders___x],\n DATETRUNC(month, orders.ordered_at) AS [orders___ordered_at],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___s]\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON (\n orders.city = _cm_rank.[city]\n OR (\n orders.city IS NULL AND _cm_rank.[city] IS NULL\n )\n )\n AND (\n orders.region = _cm_rank.[region]\n OR (\n orders.region IS NULL AND _cm_rank.[region] IS NULL\n )\n )\n AND (\n DATETRUNC(month, orders.ordered_at) = _cm_rank.[ordered_at_month]\n OR (\n DATETRUNC(month, orders.ordered_at) IS NULL\n AND _cm_rank.[ordered_at_month] IS NULL\n )\n )\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.[rank],\n DATETRUNC(month, orders.ordered_at)", + "lift/dim_nested_cumsum::bigquery": "WITH _base_2 AS (\n SELECT\n orders.city AS `orders___city`,\n orders.region AS `orders___region`,\n DATE_TRUNC(orders.ordered_at, MONTH) AS `orders___ordered_at_month`\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region,\n DATE_TRUNC(orders.ordered_at, MONTH)\n), _cm_orders__amount_sum_partition_by_ordered_at_region AS (\n SELECT\n orders.region AS `orders___region`,\n DATE_TRUNC(orders.ordered_at, MONTH) AS `orders___ordered_at_month`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_ordered_at_region`\n FROM orders AS orders\n GROUP BY\n orders.region,\n DATE_TRUNC(orders.ordered_at, MONTH)\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS `orders___city`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_city`\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.`orders___city`,\n _base_2.`orders___region`,\n _base_2.`orders___ordered_at_month`,\n _cm_orders__amount_sum_partition_by_ordered_at_region.`orders___amount_sum_partition_by_ordered_at_region` AS `orders___amount_sum_partition_by_ordered_at_region`,\n _cm_orders__amount_sum_partition_by_city.`orders___amount_sum_partition_by_city` AS `orders___amount_sum_partition_by_city`\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_ordered_at_region\n ON _base_2.`orders___region` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_ordered_at_region.`orders___region`\n AND _base_2.`orders___ordered_at_month` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_ordered_at_region.`orders___ordered_at_month`\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.`orders___city` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.`orders___city`\n), step1 AS (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___ordered_at_month`,\n `orders___amount_sum_partition_by_ordered_at_region`,\n `orders___amount_sum_partition_by_city`,\n SUM(`orders___amount_sum_partition_by_ordered_at_region`) OVER (PARTITION BY `orders___city`, `orders___region` ORDER BY `orders___ordered_at_month`) AS `orders____cumsum_inner`\n FROM base_2\n), step2 AS (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___ordered_at_month`,\n `orders___amount_sum_partition_by_ordered_at_region`,\n `orders___amount_sum_partition_by_city`,\n `orders____cumsum_inner`,\n CASE\n WHEN `orders____cumsum_inner` - `orders___amount_sum_partition_by_city` IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN `orders____cumsum_inner` - `orders___amount_sum_partition_by_city` IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY `orders____cumsum_inner` - `orders___amount_sum_partition_by_city` DESC\n )\n END AS `orders___rank`\n FROM step1\n), _cm_rank AS (\n SELECT\n _stage_inner.`orders___city` AS `city`,\n _stage_inner.`orders___region` AS `region`,\n _stage_inner.`orders___ordered_at_month` AS `ordered_at_month`,\n _stage_inner.`orders___rank` AS `rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___ordered_at_month`,\n `orders___rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___ordered_at_month`,\n `orders___amount_sum_partition_by_ordered_at_region`,\n `orders___amount_sum_partition_by_city`,\n `orders____cumsum_inner`,\n `orders___rank`\n FROM step2\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS `orders___region`,\n orders.city AS `orders___city`,\n _cm_rank.`rank` AS `orders___nr`,\n DATE_TRUNC(orders.ordered_at, MONTH) AS `orders___ordered_at`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___s`\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.`city`\n AND orders.region IS NOT DISTINCT FROM _cm_rank.`region`\n AND DATE_TRUNC(orders.ordered_at, MONTH) IS NOT DISTINCT FROM _cm_rank.`ordered_at_month`\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.`rank`,\n DATE_TRUNC(orders.ordered_at, MONTH)", + "lift/dim_nested_cumsum::duckdb": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at_month\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region,\n DATE_TRUNC('MONTH', orders.ordered_at)\n), _cm_orders__amount_sum_partition_by_ordered_at_region AS (\n SELECT\n orders.region AS \"orders.region\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at_month\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_ordered_at_region\"\n FROM orders AS orders\n GROUP BY\n orders.region,\n DATE_TRUNC('MONTH', orders.ordered_at)\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _base_2.\"orders.ordered_at_month\",\n _cm_orders__amount_sum_partition_by_ordered_at_region.\"orders.amount_sum_partition_by_ordered_at_region\" AS \"orders.amount_sum_partition_by_ordered_at_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_ordered_at_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_ordered_at_region.\"orders.region\"\n AND _base_2.\"orders.ordered_at_month\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_ordered_at_region.\"orders.ordered_at_month\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_partition_by_ordered_at_region\",\n \"orders.amount_sum_partition_by_city\",\n SUM(\"orders.amount_sum_partition_by_ordered_at_region\") OVER (PARTITION BY \"orders.city\", \"orders.region\" ORDER BY \"orders.ordered_at_month\") AS \"orders._cumsum_inner\"\n FROM base_2\n), step2 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_partition_by_ordered_at_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders._cumsum_inner\",\n CASE\n WHEN \"orders._cumsum_inner\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders._cumsum_inner\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders._cumsum_inner\" - \"orders.amount_sum_partition_by_city\" DESC\n )\n END AS \"orders.rank\"\n FROM step1\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.ordered_at_month\" AS \"ordered_at_month\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_partition_by_ordered_at_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders._cumsum_inner\",\n \"orders.rank\"\n FROM step2\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.nr\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\n AND DATE_TRUNC('MONTH', orders.ordered_at) IS NOT DISTINCT FROM _cm_rank.\"ordered_at_month\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\",\n DATE_TRUNC('MONTH', orders.ordered_at)", + "lift/dim_nested_cumsum::postgres": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at_month\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region,\n DATE_TRUNC('MONTH', orders.ordered_at)\n), _cm_orders__amount_sum_partition_by_ordered_at_region AS (\n SELECT\n orders.region AS \"orders.region\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at_month\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_ordered_at_region\"\n FROM orders AS orders\n GROUP BY\n orders.region,\n DATE_TRUNC('MONTH', orders.ordered_at)\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _base_2.\"orders.ordered_at_month\",\n _cm_orders__amount_sum_partition_by_ordered_at_region.\"orders.amount_sum_partition_by_ordered_at_region\" AS \"orders.amount_sum_partition_by_ordered_at_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_ordered_at_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_ordered_at_region.\"orders.region\"\n AND _base_2.\"orders.ordered_at_month\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_ordered_at_region.\"orders.ordered_at_month\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_partition_by_ordered_at_region\",\n \"orders.amount_sum_partition_by_city\",\n SUM(\"orders.amount_sum_partition_by_ordered_at_region\") OVER (PARTITION BY \"orders.city\", \"orders.region\" ORDER BY \"orders.ordered_at_month\") AS \"orders._cumsum_inner\"\n FROM base_2\n), step2 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_partition_by_ordered_at_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders._cumsum_inner\",\n CASE\n WHEN \"orders._cumsum_inner\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders._cumsum_inner\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders._cumsum_inner\" - \"orders.amount_sum_partition_by_city\" DESC\n )\n END AS \"orders.rank\"\n FROM step1\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.ordered_at_month\" AS \"ordered_at_month\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_partition_by_ordered_at_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders._cumsum_inner\",\n \"orders.rank\"\n FROM step2\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.nr\",\n DATE_TRUNC('MONTH', orders.ordered_at) AS \"orders.ordered_at\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\n AND DATE_TRUNC('MONTH', orders.ordered_at) IS NOT DISTINCT FROM _cm_rank.\"ordered_at_month\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\",\n DATE_TRUNC('MONTH', orders.ordered_at)", + "lift/dim_nested_cumsum::sqlite": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\",\n STRFTIME('%Y-%m-01', orders.ordered_at) AS \"orders.ordered_at_month\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region,\n STRFTIME('%Y-%m-01', orders.ordered_at)\n), _cm_orders__amount_sum_partition_by_ordered_at_region AS (\n SELECT\n orders.region AS \"orders.region\",\n STRFTIME('%Y-%m-01', orders.ordered_at) AS \"orders.ordered_at_month\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_ordered_at_region\"\n FROM orders AS orders\n GROUP BY\n orders.region,\n STRFTIME('%Y-%m-01', orders.ordered_at)\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _base_2.\"orders.ordered_at_month\",\n _cm_orders__amount_sum_partition_by_ordered_at_region.\"orders.amount_sum_partition_by_ordered_at_region\" AS \"orders.amount_sum_partition_by_ordered_at_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_ordered_at_region\n ON _base_2.\"orders.region\" IS _cm_orders__amount_sum_partition_by_ordered_at_region.\"orders.region\"\n AND _base_2.\"orders.ordered_at_month\" IS _cm_orders__amount_sum_partition_by_ordered_at_region.\"orders.ordered_at_month\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_partition_by_ordered_at_region\",\n \"orders.amount_sum_partition_by_city\",\n SUM(\"orders.amount_sum_partition_by_ordered_at_region\") OVER (PARTITION BY \"orders.city\", \"orders.region\" ORDER BY \"orders.ordered_at_month\") AS \"orders._cumsum_inner\"\n FROM base_2\n), step2 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_partition_by_ordered_at_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders._cumsum_inner\",\n CASE\n WHEN \"orders._cumsum_inner\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders._cumsum_inner\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders._cumsum_inner\" - \"orders.amount_sum_partition_by_city\" DESC\n )\n END AS \"orders.rank\"\n FROM step1\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.ordered_at_month\" AS \"ordered_at_month\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.ordered_at_month\",\n \"orders.amount_sum_partition_by_ordered_at_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders._cumsum_inner\",\n \"orders.rank\"\n FROM step2\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.nr\",\n STRFTIME('%Y-%m-01', orders.ordered_at) AS \"orders.ordered_at\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS _cm_rank.\"city\"\n AND orders.region IS _cm_rank.\"region\"\n AND STRFTIME('%Y-%m-01', orders.ordered_at) IS _cm_rank.\"ordered_at_month\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\",\n STRFTIME('%Y-%m-01', orders.ordered_at)", + "lift/dim_nested_cumsum::tsql": "WITH _base_2 AS (\n SELECT\n orders.city AS [orders___city],\n orders.region AS [orders___region],\n DATETRUNC(month, orders.ordered_at) AS [orders___ordered_at_month]\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region,\n DATETRUNC(month, orders.ordered_at)\n), _cm_orders__amount_sum_partition_by_ordered_at_region AS (\n SELECT\n orders.region AS [orders___region],\n DATETRUNC(month, orders.ordered_at) AS [orders___ordered_at_month],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_ordered_at_region]\n FROM orders AS orders\n GROUP BY\n orders.region,\n DATETRUNC(month, orders.ordered_at)\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS [orders___city],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_city]\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.[orders___city] AS [orders___city],\n _base_2.[orders___region] AS [orders___region],\n _base_2.[orders___ordered_at_month] AS [orders___ordered_at_month],\n _cm_orders__amount_sum_partition_by_ordered_at_region.[orders___amount_sum_partition_by_ordered_at_region] AS [orders___amount_sum_partition_by_ordered_at_region],\n _cm_orders__amount_sum_partition_by_city.[orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city]\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_ordered_at_region\n ON (\n _base_2.[orders___region] = _cm_orders__amount_sum_partition_by_ordered_at_region.[orders___region]\n OR (\n _base_2.[orders___region] IS NULL\n AND _cm_orders__amount_sum_partition_by_ordered_at_region.[orders___region] IS NULL\n )\n )\n AND (\n _base_2.[orders___ordered_at_month] = _cm_orders__amount_sum_partition_by_ordered_at_region.[orders___ordered_at_month]\n OR (\n _base_2.[orders___ordered_at_month] IS NULL\n AND _cm_orders__amount_sum_partition_by_ordered_at_region.[orders___ordered_at_month] IS NULL\n )\n )\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON (\n _base_2.[orders___city] = _cm_orders__amount_sum_partition_by_city.[orders___city]\n OR (\n _base_2.[orders___city] IS NULL\n AND _cm_orders__amount_sum_partition_by_city.[orders___city] IS NULL\n )\n )\n), step1 AS (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___ordered_at_month] AS [orders___ordered_at_month],\n [orders___amount_sum_partition_by_ordered_at_region] AS [orders___amount_sum_partition_by_ordered_at_region],\n [orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city],\n SUM([orders___amount_sum_partition_by_ordered_at_region]) OVER (PARTITION BY [orders___city], [orders___region] ORDER BY [orders___ordered_at_month]) AS [orders____cumsum_inner]\n FROM base_2\n), step2 AS (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___ordered_at_month] AS [orders___ordered_at_month],\n [orders___amount_sum_partition_by_ordered_at_region] AS [orders___amount_sum_partition_by_ordered_at_region],\n [orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city],\n [orders____cumsum_inner] AS [orders____cumsum_inner],\n CASE\n WHEN [orders____cumsum_inner] - [orders___amount_sum_partition_by_city] IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN [orders____cumsum_inner] - [orders___amount_sum_partition_by_city] IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY [orders____cumsum_inner] - [orders___amount_sum_partition_by_city] DESC\n )\n END AS [orders___rank]\n FROM step1\n), _cm_rank AS (\n SELECT\n _stage_inner.[orders___city] AS [city],\n _stage_inner.[orders___region] AS [region],\n _stage_inner.[orders___ordered_at_month] AS [ordered_at_month],\n _stage_inner.[orders___rank] AS [rank]\n FROM (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___ordered_at_month] AS [orders___ordered_at_month],\n [orders___rank] AS [orders___rank]\n FROM (\n SELECT\n [orders___city],\n [orders___region],\n [orders___ordered_at_month],\n [orders___amount_sum_partition_by_ordered_at_region],\n [orders___amount_sum_partition_by_city],\n [orders____cumsum_inner],\n [orders___rank]\n FROM step2\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS [orders___region],\n orders.city AS [orders___city],\n _cm_rank.[rank] AS [orders___nr],\n DATETRUNC(month, orders.ordered_at) AS [orders___ordered_at],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___s]\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON (\n orders.city = _cm_rank.[city]\n OR (\n orders.city IS NULL AND _cm_rank.[city] IS NULL\n )\n )\n AND (\n orders.region = _cm_rank.[region]\n OR (\n orders.region IS NULL AND _cm_rank.[region] IS NULL\n )\n )\n AND (\n DATETRUNC(month, orders.ordered_at) = _cm_rank.[ordered_at_month]\n OR (\n DATETRUNC(month, orders.ordered_at) IS NULL\n AND _cm_rank.[ordered_at_month] IS NULL\n )\n )\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.[rank],\n DATETRUNC(month, orders.ordered_at)", + "lift/dim_same_grain_rank::bigquery": "WITH base_2 AS (\n SELECT\n orders.region AS `orders___region`,\n SUM(orders.amount) AS `orders___amount_sum_partition_by_region`,\n SUM(CASE WHEN orders.status = 'ok' THEN orders.amount END) AS `orders___ok_amount_sum_partition_by_region`\n FROM orders AS orders\n GROUP BY\n orders.region\n), step1 AS (\n SELECT\n `orders___region`,\n `orders___amount_sum_partition_by_region`,\n `orders___ok_amount_sum_partition_by_region`,\n CASE\n WHEN `orders___amount_sum_partition_by_region` + `orders___ok_amount_sum_partition_by_region` IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN `orders___amount_sum_partition_by_region` + `orders___ok_amount_sum_partition_by_region` IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY `orders___amount_sum_partition_by_region` + `orders___ok_amount_sum_partition_by_region` DESC\n )\n END AS `orders___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`orders___region` AS `region`,\n _stage_inner.`orders___rank` AS `rank`\n FROM (\n SELECT\n `orders___region`,\n `orders___rank`\n FROM (\n SELECT\n `orders___region`,\n `orders___amount_sum_partition_by_region`,\n `orders___ok_amount_sum_partition_by_region`,\n `orders___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS `orders___region`,\n _cm_rank.`rank` AS `orders___gr`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___s`\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.region IS NOT DISTINCT FROM _cm_rank.`region`\nGROUP BY\n orders.region,\n _cm_rank.`rank`", + "lift/dim_same_grain_rank::duckdb": "WITH base_2 AS (\n SELECT\n orders.region AS \"orders.region\",\n SUM(orders.amount) AS \"orders.amount_sum_partition_by_region\",\n SUM(CASE WHEN orders.status = 'ok' THEN orders.amount END) AS \"orders.ok_amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), step1 AS (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.ok_amount_sum_partition_by_region\",\n CASE\n WHEN \"orders.amount_sum_partition_by_region\" + \"orders.ok_amount_sum_partition_by_region\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_sum_partition_by_region\" + \"orders.ok_amount_sum_partition_by_region\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_partition_by_region\" + \"orders.ok_amount_sum_partition_by_region\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.ok_amount_sum_partition_by_region\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n _cm_rank.\"rank\" AS \"orders.gr\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n _cm_rank.\"rank\"", + "lift/dim_same_grain_rank::postgres": "WITH base_2 AS (\n SELECT\n orders.region AS \"orders.region\",\n SUM(orders.amount) AS \"orders.amount_sum_partition_by_region\",\n SUM(CASE WHEN orders.status = 'ok' THEN orders.amount END) AS \"orders.ok_amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), step1 AS (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.ok_amount_sum_partition_by_region\",\n CASE\n WHEN \"orders.amount_sum_partition_by_region\" + \"orders.ok_amount_sum_partition_by_region\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_sum_partition_by_region\" + \"orders.ok_amount_sum_partition_by_region\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_partition_by_region\" + \"orders.ok_amount_sum_partition_by_region\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.ok_amount_sum_partition_by_region\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n _cm_rank.\"rank\" AS \"orders.gr\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n _cm_rank.\"rank\"", + "lift/dim_same_grain_rank::sqlite": "WITH base_2 AS (\n SELECT\n orders.region AS \"orders.region\",\n SUM(orders.amount) AS \"orders.amount_sum_partition_by_region\",\n SUM(CASE WHEN orders.status = 'ok' THEN orders.amount END) AS \"orders.ok_amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), step1 AS (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.ok_amount_sum_partition_by_region\",\n CASE\n WHEN \"orders.amount_sum_partition_by_region\" + \"orders.ok_amount_sum_partition_by_region\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_sum_partition_by_region\" + \"orders.ok_amount_sum_partition_by_region\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_partition_by_region\" + \"orders.ok_amount_sum_partition_by_region\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.ok_amount_sum_partition_by_region\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n _cm_rank.\"rank\" AS \"orders.gr\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.region IS _cm_rank.\"region\"\nGROUP BY\n orders.region,\n _cm_rank.\"rank\"", + "lift/dim_same_grain_rank::tsql": "WITH base_2 AS (\n SELECT\n orders.region AS [orders___region],\n SUM(orders.amount) AS [orders___amount_sum_partition_by_region],\n SUM(CASE WHEN orders.status = 'ok' THEN orders.amount END) AS [orders___ok_amount_sum_partition_by_region]\n FROM orders AS orders\n GROUP BY\n orders.region\n), step1 AS (\n SELECT\n [orders___region] AS [orders___region],\n [orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n [orders___ok_amount_sum_partition_by_region] AS [orders___ok_amount_sum_partition_by_region],\n CASE\n WHEN [orders___amount_sum_partition_by_region] + [orders___ok_amount_sum_partition_by_region] IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN [orders___amount_sum_partition_by_region] + [orders___ok_amount_sum_partition_by_region] IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY [orders___amount_sum_partition_by_region] + [orders___ok_amount_sum_partition_by_region] DESC\n )\n END AS [orders___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[orders___region] AS [region],\n _stage_inner.[orders___rank] AS [rank]\n FROM (\n SELECT\n [orders___region] AS [orders___region],\n [orders___rank] AS [orders___rank]\n FROM (\n SELECT\n [orders___region],\n [orders___amount_sum_partition_by_region],\n [orders___ok_amount_sum_partition_by_region],\n [orders___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS [orders___region],\n _cm_rank.[rank] AS [orders___gr],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___s]\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON (\n orders.region = _cm_rank.[region]\n OR (\n orders.region IS NULL AND _cm_rank.[region] IS NULL\n )\n )\nGROUP BY\n orders.region,\n _cm_rank.[rank]", + "lift/dim_subset_rank::bigquery": "WITH _base_2 AS (\n SELECT\n orders.city AS `orders___city`,\n orders.region AS `orders___region`,\n SUM(orders.amount) AS `orders___amount_sum_partition_by_city_region`\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS `orders___region`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_region`\n FROM orders AS orders\n GROUP BY\n orders.region\n), base_2 AS (\n SELECT\n _base_2.`orders___city`,\n _base_2.`orders___region`,\n _base_2.`orders___amount_sum_partition_by_city_region`,\n _cm_orders__amount_sum_partition_by_region.`orders___amount_sum_partition_by_region` AS `orders___amount_sum_partition_by_region`\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.`orders___region` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.`orders___region`\n), step1 AS (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___amount_sum_partition_by_city_region`,\n `orders___amount_sum_partition_by_region`,\n CASE\n WHEN `orders___amount_sum_partition_by_city_region` - `orders___amount_sum_partition_by_region` IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN `orders___amount_sum_partition_by_city_region` - `orders___amount_sum_partition_by_region` IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY `orders___amount_sum_partition_by_city_region` - `orders___amount_sum_partition_by_region` DESC\n )\n END AS `orders___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`orders___city` AS `city`,\n _stage_inner.`orders___region` AS `region`,\n _stage_inner.`orders___rank` AS `rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___amount_sum_partition_by_city_region`,\n `orders___amount_sum_partition_by_region`,\n `orders___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS `orders___region`,\n orders.city AS `orders___city`,\n _cm_rank.`rank` AS `orders___sr`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___s`\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.`city`\n AND orders.region IS NOT DISTINCT FROM _cm_rank.`region`\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.`rank`", + "lift/dim_subset_rank::duckdb": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\",\n SUM(orders.amount) AS \"orders.amount_sum_partition_by_city_region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _base_2.\"orders.amount_sum_partition_by_city_region\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_city_region\",\n \"orders.amount_sum_partition_by_region\",\n CASE\n WHEN \"orders.amount_sum_partition_by_city_region\" - \"orders.amount_sum_partition_by_region\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_sum_partition_by_city_region\" - \"orders.amount_sum_partition_by_region\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_partition_by_city_region\" - \"orders.amount_sum_partition_by_region\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_city_region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.sr\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\"", + "lift/dim_subset_rank::postgres": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\",\n SUM(orders.amount) AS \"orders.amount_sum_partition_by_city_region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _base_2.\"orders.amount_sum_partition_by_city_region\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_city_region\",\n \"orders.amount_sum_partition_by_region\",\n CASE\n WHEN \"orders.amount_sum_partition_by_city_region\" - \"orders.amount_sum_partition_by_region\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_sum_partition_by_city_region\" - \"orders.amount_sum_partition_by_region\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_partition_by_city_region\" - \"orders.amount_sum_partition_by_region\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_city_region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.sr\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\"", + "lift/dim_subset_rank::sqlite": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\",\n SUM(orders.amount) AS \"orders.amount_sum_partition_by_city_region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _base_2.\"orders.amount_sum_partition_by_city_region\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.\"orders.region\" IS _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_city_region\",\n \"orders.amount_sum_partition_by_region\",\n CASE\n WHEN \"orders.amount_sum_partition_by_city_region\" - \"orders.amount_sum_partition_by_region\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_sum_partition_by_city_region\" - \"orders.amount_sum_partition_by_region\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_partition_by_city_region\" - \"orders.amount_sum_partition_by_region\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_city_region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n _cm_rank.\"rank\" AS \"orders.sr\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.s\"\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON orders.city IS _cm_rank.\"city\" AND orders.region IS _cm_rank.\"region\"\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.\"rank\"", + "lift/dim_subset_rank::tsql": "WITH _base_2 AS (\n SELECT\n orders.city AS [orders___city],\n orders.region AS [orders___region],\n SUM(orders.amount) AS [orders___amount_sum_partition_by_city_region]\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS [orders___region],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_region]\n FROM orders AS orders\n GROUP BY\n orders.region\n), base_2 AS (\n SELECT\n _base_2.[orders___city] AS [orders___city],\n _base_2.[orders___region] AS [orders___region],\n _base_2.[orders___amount_sum_partition_by_city_region] AS [orders___amount_sum_partition_by_city_region],\n _cm_orders__amount_sum_partition_by_region.[orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region]\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON (\n _base_2.[orders___region] = _cm_orders__amount_sum_partition_by_region.[orders___region]\n OR (\n _base_2.[orders___region] IS NULL\n AND _cm_orders__amount_sum_partition_by_region.[orders___region] IS NULL\n )\n )\n), step1 AS (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___amount_sum_partition_by_city_region] AS [orders___amount_sum_partition_by_city_region],\n [orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n CASE\n WHEN [orders___amount_sum_partition_by_city_region] - [orders___amount_sum_partition_by_region] IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN [orders___amount_sum_partition_by_city_region] - [orders___amount_sum_partition_by_region] IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY [orders___amount_sum_partition_by_city_region] - [orders___amount_sum_partition_by_region] DESC\n )\n END AS [orders___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[orders___city] AS [city],\n _stage_inner.[orders___region] AS [region],\n _stage_inner.[orders___rank] AS [rank]\n FROM (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___rank] AS [orders___rank]\n FROM (\n SELECT\n [orders___city],\n [orders___region],\n [orders___amount_sum_partition_by_city_region],\n [orders___amount_sum_partition_by_region],\n [orders___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n orders.region AS [orders___region],\n orders.city AS [orders___city],\n _cm_rank.[rank] AS [orders___sr],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___s]\nFROM orders AS orders\nLEFT JOIN _cm_rank\n ON (\n orders.city = _cm_rank.[city]\n OR (\n orders.city IS NULL AND _cm_rank.[city] IS NULL\n )\n )\n AND (\n orders.region = _cm_rank.[region]\n OR (\n orders.region IS NULL AND _cm_rank.[region] IS NULL\n )\n )\nGROUP BY\n orders.region,\n orders.city,\n _cm_rank.[rank]", + "lift/dual_role::bigquery": "WITH _base_2 AS (\n SELECT\n orders.city AS `orders___city`,\n orders.region AS `orders___region`\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS `orders___region`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_region`\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS `orders___city`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_city`\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.`orders___city`,\n _base_2.`orders___region`,\n _cm_orders__amount_sum_partition_by_region.`orders___amount_sum_partition_by_region` AS `orders___amount_sum_partition_by_region`,\n _cm_orders__amount_sum_partition_by_city.`orders___amount_sum_partition_by_city` AS `orders___amount_sum_partition_by_city`\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.`orders___region` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.`orders___region`\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.`orders___city` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.`orders___city`\n), step1 AS (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___amount_sum_partition_by_region`,\n `orders___amount_sum_partition_by_city`,\n CASE\n WHEN `orders___amount_sum_partition_by_region` - `orders___amount_sum_partition_by_city` IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN `orders___amount_sum_partition_by_region` - `orders___amount_sum_partition_by_city` IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY `orders___amount_sum_partition_by_region` - `orders___amount_sum_partition_by_city` DESC\n )\n END AS `orders___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`orders___city` AS `city`,\n _stage_inner.`orders___region` AS `region`,\n _stage_inner.`orders___rank` AS `rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___rank`\n FROM (\n SELECT\n `orders___city`,\n `orders___region`,\n `orders___amount_sum_partition_by_region`,\n `orders___amount_sum_partition_by_city`,\n `orders___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _base AS (\n SELECT\n orders.region AS `orders___region`,\n orders.city AS `orders___city`,\n orders.channel AS `orders___channel`,\n _cm_rank.`rank` AS `orders___rr`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___s`\n FROM orders AS orders\n LEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.`city`\n AND orders.region IS NOT DISTINCT FROM _cm_rank.`region`\n GROUP BY\n orders.region,\n orders.city,\n orders.channel,\n _cm_rank.`rank`\n), base AS (\n SELECT\n _base.`orders___region`,\n _base.`orders___city`,\n _base.`orders___channel`,\n _base.`orders___rr`,\n _base.`orders___s`,\n _cm_orders__amount_sum_partition_by_region.`orders___amount_sum_partition_by_region` AS `orders___amount_sum_partition_by_region`,\n _cm_orders__amount_sum_partition_by_city.`orders___amount_sum_partition_by_city` AS `orders___amount_sum_partition_by_city`\n FROM _base\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base.`orders___region` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.`orders___region`\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base.`orders___city` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.`orders___city`\n), step1_2 AS (\n SELECT\n `orders___region`,\n `orders___city`,\n `orders___channel`,\n `orders___rr`,\n `orders___s`,\n `orders___amount_sum_partition_by_region`,\n `orders___amount_sum_partition_by_city`,\n CASE\n WHEN `orders___amount_sum_partition_by_region` - `orders___amount_sum_partition_by_city` IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN `orders___amount_sum_partition_by_region` - `orders___amount_sum_partition_by_city` IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY `orders___amount_sum_partition_by_region` - `orders___amount_sum_partition_by_city` DESC\n )\n END AS `orders___rm`\n FROM base\n)\nSELECT\n `orders___region`,\n `orders___city`,\n `orders___channel`,\n `orders___rr`,\n `orders___rm`,\n `orders___s`\nFROM (\n SELECT\n `orders___region`,\n `orders___city`,\n `orders___channel`,\n `orders___rr`,\n `orders___s`,\n `orders___amount_sum_partition_by_region`,\n `orders___amount_sum_partition_by_city`,\n `orders___rm`\n FROM step1_2\n) AS _outer", + "lift/dual_role::duckdb": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n CASE\n WHEN \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _base AS (\n SELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n orders.channel AS \"orders.channel\",\n _cm_rank.\"rank\" AS \"orders.rr\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.s\"\n FROM orders AS orders\n LEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\n GROUP BY\n orders.region,\n orders.city,\n orders.channel,\n _cm_rank.\"rank\"\n), base AS (\n SELECT\n _base.\"orders.region\",\n _base.\"orders.city\",\n _base.\"orders.channel\",\n _base.\"orders.rr\",\n _base.\"orders.s\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1_2 AS (\n SELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.channel\",\n \"orders.rr\",\n \"orders.s\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n CASE\n WHEN \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n )\n END AS \"orders.rm\"\n FROM base\n)\nSELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.channel\",\n \"orders.rr\",\n \"orders.rm\",\n \"orders.s\"\nFROM (\n SELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.channel\",\n \"orders.rr\",\n \"orders.s\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rm\"\n FROM step1_2\n) AS _outer", + "lift/dual_role::postgres": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n CASE\n WHEN \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _base AS (\n SELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n orders.channel AS \"orders.channel\",\n _cm_rank.\"rank\" AS \"orders.rr\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.s\"\n FROM orders AS orders\n LEFT JOIN _cm_rank\n ON orders.city IS NOT DISTINCT FROM _cm_rank.\"city\"\n AND orders.region IS NOT DISTINCT FROM _cm_rank.\"region\"\n GROUP BY\n orders.region,\n orders.city,\n orders.channel,\n _cm_rank.\"rank\"\n), base AS (\n SELECT\n _base.\"orders.region\",\n _base.\"orders.city\",\n _base.\"orders.channel\",\n _base.\"orders.rr\",\n _base.\"orders.s\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1_2 AS (\n SELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.channel\",\n \"orders.rr\",\n \"orders.s\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n CASE\n WHEN \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n )\n END AS \"orders.rm\"\n FROM base\n)\nSELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.channel\",\n \"orders.rr\",\n \"orders.rm\",\n \"orders.s\"\nFROM (\n SELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.channel\",\n \"orders.rr\",\n \"orders.s\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rm\"\n FROM step1_2\n) AS _outer", + "lift/dual_role::sqlite": "WITH _base_2 AS (\n SELECT\n orders.city AS \"orders.city\",\n orders.region AS \"orders.region\"\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.\"orders.city\",\n _base_2.\"orders.region\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base_2.\"orders.region\" IS _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base_2.\"orders.city\" IS _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1 AS (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n CASE\n WHEN \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n )\n END AS \"orders.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"orders.city\" AS \"city\",\n _stage_inner.\"orders.region\" AS \"region\",\n _stage_inner.\"orders.rank\" AS \"rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.rank\"\n FROM (\n SELECT\n \"orders.city\",\n \"orders.region\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _base AS (\n SELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n orders.channel AS \"orders.channel\",\n _cm_rank.\"rank\" AS \"orders.rr\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.s\"\n FROM orders AS orders\n LEFT JOIN _cm_rank\n ON orders.city IS _cm_rank.\"city\" AND orders.region IS _cm_rank.\"region\"\n GROUP BY\n orders.region,\n orders.city,\n orders.channel,\n _cm_rank.\"rank\"\n), base AS (\n SELECT\n _base.\"orders.region\",\n _base.\"orders.city\",\n _base.\"orders.channel\",\n _base.\"orders.rr\",\n _base.\"orders.s\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" AS \"orders.amount_sum_partition_by_region\",\n _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.amount_sum_partition_by_city\"\n FROM _base\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base.\"orders.region\" IS _cm_orders__amount_sum_partition_by_region.\"orders.region\"\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base.\"orders.city\" IS _cm_orders__amount_sum_partition_by_city.\"orders.city\"\n), step1_2 AS (\n SELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.channel\",\n \"orders.rr\",\n \"orders.s\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n CASE\n WHEN \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_partition_by_region\" - \"orders.amount_sum_partition_by_city\" DESC\n )\n END AS \"orders.rm\"\n FROM base\n)\nSELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.channel\",\n \"orders.rr\",\n \"orders.rm\",\n \"orders.s\"\nFROM (\n SELECT\n \"orders.region\",\n \"orders.city\",\n \"orders.channel\",\n \"orders.rr\",\n \"orders.s\",\n \"orders.amount_sum_partition_by_region\",\n \"orders.amount_sum_partition_by_city\",\n \"orders.rm\"\n FROM step1_2\n) AS _outer", + "lift/dual_role::tsql": "WITH _base_2 AS (\n SELECT\n orders.city AS [orders___city],\n orders.region AS [orders___region]\n FROM orders AS orders\n GROUP BY\n orders.city,\n orders.region\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS [orders___region],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_region]\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS [orders___city],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_city]\n FROM orders AS orders\n GROUP BY\n orders.city\n), base_2 AS (\n SELECT\n _base_2.[orders___city] AS [orders___city],\n _base_2.[orders___region] AS [orders___region],\n _cm_orders__amount_sum_partition_by_region.[orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n _cm_orders__amount_sum_partition_by_city.[orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city]\n FROM _base_2\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON (\n _base_2.[orders___region] = _cm_orders__amount_sum_partition_by_region.[orders___region]\n OR (\n _base_2.[orders___region] IS NULL\n AND _cm_orders__amount_sum_partition_by_region.[orders___region] IS NULL\n )\n )\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON (\n _base_2.[orders___city] = _cm_orders__amount_sum_partition_by_city.[orders___city]\n OR (\n _base_2.[orders___city] IS NULL\n AND _cm_orders__amount_sum_partition_by_city.[orders___city] IS NULL\n )\n )\n), step1 AS (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n [orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city],\n CASE\n WHEN [orders___amount_sum_partition_by_region] - [orders___amount_sum_partition_by_city] IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN [orders___amount_sum_partition_by_region] - [orders___amount_sum_partition_by_city] IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY [orders___amount_sum_partition_by_region] - [orders___amount_sum_partition_by_city] DESC\n )\n END AS [orders___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[orders___city] AS [city],\n _stage_inner.[orders___region] AS [region],\n _stage_inner.[orders___rank] AS [rank]\n FROM (\n SELECT\n [orders___city] AS [orders___city],\n [orders___region] AS [orders___region],\n [orders___rank] AS [orders___rank]\n FROM (\n SELECT\n [orders___city],\n [orders___region],\n [orders___amount_sum_partition_by_region],\n [orders___amount_sum_partition_by_city],\n [orders___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _base AS (\n SELECT\n orders.region AS [orders___region],\n orders.city AS [orders___city],\n orders.channel AS [orders___channel],\n _cm_rank.[rank] AS [orders___rr],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___s]\n FROM orders AS orders\n LEFT JOIN _cm_rank\n ON (\n orders.city = _cm_rank.[city]\n OR (\n orders.city IS NULL AND _cm_rank.[city] IS NULL\n )\n )\n AND (\n orders.region = _cm_rank.[region]\n OR (\n orders.region IS NULL AND _cm_rank.[region] IS NULL\n )\n )\n GROUP BY\n orders.region,\n orders.city,\n orders.channel,\n _cm_rank.[rank]\n), base AS (\n SELECT\n _base.[orders___region] AS [orders___region],\n _base.[orders___city] AS [orders___city],\n _base.[orders___channel] AS [orders___channel],\n _base.[orders___rr] AS [orders___rr],\n _base.[orders___s] AS [orders___s],\n _cm_orders__amount_sum_partition_by_region.[orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n _cm_orders__amount_sum_partition_by_city.[orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city]\n FROM _base\n LEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON (\n _base.[orders___region] = _cm_orders__amount_sum_partition_by_region.[orders___region]\n OR (\n _base.[orders___region] IS NULL\n AND _cm_orders__amount_sum_partition_by_region.[orders___region] IS NULL\n )\n )\n LEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON (\n _base.[orders___city] = _cm_orders__amount_sum_partition_by_city.[orders___city]\n OR (\n _base.[orders___city] IS NULL\n AND _cm_orders__amount_sum_partition_by_city.[orders___city] IS NULL\n )\n )\n), step1_2 AS (\n SELECT\n [orders___region] AS [orders___region],\n [orders___city] AS [orders___city],\n [orders___channel] AS [orders___channel],\n [orders___rr] AS [orders___rr],\n [orders___s] AS [orders___s],\n [orders___amount_sum_partition_by_region] AS [orders___amount_sum_partition_by_region],\n [orders___amount_sum_partition_by_city] AS [orders___amount_sum_partition_by_city],\n CASE\n WHEN [orders___amount_sum_partition_by_region] - [orders___amount_sum_partition_by_city] IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN [orders___amount_sum_partition_by_region] - [orders___amount_sum_partition_by_city] IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY [orders___amount_sum_partition_by_region] - [orders___amount_sum_partition_by_city] DESC\n )\n END AS [orders___rm]\n FROM base\n)\nSELECT\n [orders___region],\n [orders___city],\n [orders___channel],\n [orders___rr],\n [orders___rm],\n [orders___s]\nFROM (\n SELECT\n [orders___region],\n [orders___city],\n [orders___channel],\n [orders___rr],\n [orders___s],\n [orders___amount_sum_partition_by_region],\n [orders___amount_sum_partition_by_city],\n [orders___rm]\n FROM step1_2\n) AS _outer", "lift/measure_mixed_arithmetic::bigquery": "WITH _base AS (\n SELECT\n orders.region AS `orders___region`,\n orders.city AS `orders___city`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___s`\n FROM orders AS orders\n GROUP BY\n orders.region,\n orders.city\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS `orders___region`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_region`\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS `orders___city`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_city`\n FROM orders AS orders\n GROUP BY\n orders.city\n)\nSELECT\n _base.`orders___region`,\n _base.`orders___city`,\n _cm_orders__amount_sum_partition_by_region.`orders___amount_sum_partition_by_region` - _cm_orders__amount_sum_partition_by_city.`orders___amount_sum_partition_by_city` AS `orders___d`,\n _base.`orders___s`\nFROM _base\nLEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base.`orders___region` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.`orders___region`\nLEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base.`orders___city` IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.`orders___city`", "lift/measure_mixed_arithmetic::duckdb": "WITH _base AS (\n SELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.s\"\n FROM orders AS orders\n GROUP BY\n orders.region,\n orders.city\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n)\nSELECT\n _base.\"orders.region\",\n _base.\"orders.city\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" - _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.d\",\n _base.\"orders.s\"\nFROM _base\nLEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\nLEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"", "lift/measure_mixed_arithmetic::postgres": "WITH _base AS (\n SELECT\n orders.region AS \"orders.region\",\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.s\"\n FROM orders AS orders\n GROUP BY\n orders.region,\n orders.city\n), _cm_orders__amount_sum_partition_by_region AS (\n SELECT\n orders.region AS \"orders.region\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_region\"\n FROM orders AS orders\n GROUP BY\n orders.region\n), _cm_orders__amount_sum_partition_by_city AS (\n SELECT\n orders.city AS \"orders.city\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_city\"\n FROM orders AS orders\n GROUP BY\n orders.city\n)\nSELECT\n _base.\"orders.region\",\n _base.\"orders.city\",\n _cm_orders__amount_sum_partition_by_region.\"orders.amount_sum_partition_by_region\" - _cm_orders__amount_sum_partition_by_city.\"orders.amount_sum_partition_by_city\" AS \"orders.d\",\n _base.\"orders.s\"\nFROM _base\nLEFT JOIN _cm_orders__amount_sum_partition_by_region\n ON _base.\"orders.region\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_region.\"orders.region\"\nLEFT JOIN _cm_orders__amount_sum_partition_by_city\n ON _base.\"orders.city\" IS NOT DISTINCT FROM _cm_orders__amount_sum_partition_by_city.\"orders.city\"", diff --git a/tests/golden/dev1859_sql_baseline.json b/tests/golden/dev1859_sql_baseline.json index 721833e5..966dea95 100644 --- a/tests/golden/dev1859_sql_baseline.json +++ b/tests/golden/dev1859_sql_baseline.json @@ -41,13 +41,13 @@ "param/literal::snowflake": "WITH _cm_amount_sum_partition_by_region AS (\n SELECT\n _stage_inner.\"sales.region\" AS \"region\",\n _stage_inner.\"sales.amount_sum_partition_by_region\" AS \"amount_sum_partition_by_region\"\n FROM (\n SELECT\n sales.region AS \"sales.region\",\n CAST(SUM(sales.amount) AS DOUBLE) AS \"sales.amount_sum_partition_by_region\"\n FROM sales AS sales\n GROUP BY\n sales.region\n ) AS _stage_inner\n)\nSELECT\n sales.region AS \"sales.region\",\n SUM(1 * _cm_amount_sum_partition_by_region.\"amount_sum_partition_by_region\") AS \"sales.w\"\nFROM sales AS sales\nLEFT JOIN _cm_amount_sum_partition_by_region\n ON sales.region IS NOT DISTINCT FROM _cm_amount_sum_partition_by_region.\"region\"\nGROUP BY\n sales.region", "param/literal::sqlite": "WITH _cm_amount_sum_partition_by_region AS (\n SELECT\n _stage_inner.\"sales.region\" AS \"region\",\n _stage_inner.\"sales.amount_sum_partition_by_region\" AS \"amount_sum_partition_by_region\"\n FROM (\n SELECT\n sales.region AS \"sales.region\",\n CAST(SUM(sales.amount) AS REAL) AS \"sales.amount_sum_partition_by_region\"\n FROM sales AS sales\n GROUP BY\n sales.region\n ) AS _stage_inner\n)\nSELECT\n sales.region AS \"sales.region\",\n SUM(1 * _cm_amount_sum_partition_by_region.\"amount_sum_partition_by_region\") AS \"sales.w\"\nFROM sales AS sales\nLEFT JOIN _cm_amount_sum_partition_by_region\n ON sales.region IS _cm_amount_sum_partition_by_region.\"region\"\nGROUP BY\n sales.region", "param/literal::tsql": "WITH _cm_amount_sum_partition_by_region AS (\n SELECT\n _stage_inner.[sales___region] AS [region],\n _stage_inner.[sales___amount_sum_partition_by_region] AS [amount_sum_partition_by_region]\n FROM (\n SELECT\n sales.region AS [sales___region],\n CAST(SUM(sales.amount) AS FLOAT) AS [sales___amount_sum_partition_by_region]\n FROM sales AS sales\n GROUP BY\n sales.region\n ) AS _stage_inner\n)\nSELECT\n sales.region AS [sales___region],\n SUM(1 * _cm_amount_sum_partition_by_region.[amount_sum_partition_by_region]) AS [sales___w]\nFROM sales AS sales\nLEFT JOIN _cm_amount_sum_partition_by_region\n ON (\n sales.region = _cm_amount_sum_partition_by_region.[region]\n OR (\n sales.region IS NULL AND _cm_amount_sum_partition_by_region.[region] IS NULL\n )\n )\nGROUP BY\n sales.region", - "param/local_ranked_transform::bigquery": "WITH base_2 AS (\n SELECT\n sales.region AS `sales___region`,\n SUM(sales.amount) AS `sales___amount_sum_partition_by_region`\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n `sales___region`,\n `sales___amount_sum_partition_by_region`,\n RANK() OVER (ORDER BY `sales___amount_sum_partition_by_region` DESC) AS `sales___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`sales___region` AS `region`,\n _stage_inner.`sales___rank` AS `rank`\n FROM (\n SELECT\n `sales___region`,\n `sales___rank`\n FROM (\n SELECT\n `sales___region`,\n `sales___amount_sum_partition_by_region`,\n `sales___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS `sales___region`,\n CAST(SUM(sales.amount * _cm_rank.`rank`) / NULLIF(SUM(_cm_rank.`rank`), 0) AS FLOAT64) AS `sales___w`\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON sales.region IS NOT DISTINCT FROM _cm_rank.`region`\nGROUP BY\n sales.region", - "param/local_ranked_transform::duckdb": "WITH base_2 AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum_partition_by_region\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum_partition_by_region\",\n RANK() OVER (ORDER BY \"sales.amount_sum_partition_by_region\" DESC) AS \"sales.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"sales.region\" AS \"region\",\n _stage_inner.\"sales.rank\" AS \"rank\"\n FROM (\n SELECT\n \"sales.region\",\n \"sales.rank\"\n FROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum_partition_by_region\",\n \"sales.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS \"sales.region\",\n CAST(SUM(sales.amount * _cm_rank.\"rank\") / NULLIF(SUM(_cm_rank.\"rank\"), 0) AS DOUBLE) AS \"sales.w\"\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON sales.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n sales.region", - "param/local_ranked_transform::mysql": "WITH base_2 AS (\n SELECT\n sales.region AS `sales.region`,\n SUM(sales.amount) AS `sales.amount_sum_partition_by_region`\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n `sales.region`,\n `sales.amount_sum_partition_by_region`,\n RANK() OVER (ORDER BY `sales.amount_sum_partition_by_region` DESC) AS `sales.rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`sales.region` AS `region`,\n _stage_inner.`sales.rank` AS `rank`\n FROM (\n SELECT\n `sales.region`,\n `sales.rank`\n FROM (\n SELECT\n `sales.region`,\n `sales.amount_sum_partition_by_region`,\n `sales.rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS `sales.region`,\n CAST(SUM(sales.amount * _cm_rank.`rank`) / NULLIF(SUM(_cm_rank.`rank`), 0) AS DOUBLE) AS `sales.w`\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON sales.region <=> _cm_rank.`region`\nGROUP BY\n sales.region", - "param/local_ranked_transform::postgres": "WITH base_2 AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum_partition_by_region\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum_partition_by_region\",\n RANK() OVER (ORDER BY \"sales.amount_sum_partition_by_region\" DESC NULLS LAST) AS \"sales.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"sales.region\" AS \"region\",\n _stage_inner.\"sales.rank\" AS \"rank\"\n FROM (\n SELECT\n \"sales.region\",\n \"sales.rank\"\n FROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum_partition_by_region\",\n \"sales.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS \"sales.region\",\n CAST(SUM(sales.amount * _cm_rank.\"rank\") / NULLIF(SUM(_cm_rank.\"rank\"), 0) AS DOUBLE PRECISION) AS \"sales.w\"\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON sales.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n sales.region", - "param/local_ranked_transform::snowflake": "WITH base_2 AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum_partition_by_region\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum_partition_by_region\",\n RANK() OVER (ORDER BY \"sales.amount_sum_partition_by_region\" DESC NULLS LAST) AS \"sales.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"sales.region\" AS \"region\",\n _stage_inner.\"sales.rank\" AS \"rank\"\n FROM (\n SELECT\n \"sales.region\",\n \"sales.rank\"\n FROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum_partition_by_region\",\n \"sales.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS \"sales.region\",\n CAST(SUM(sales.amount * _cm_rank.\"rank\") / NULLIF(SUM(_cm_rank.\"rank\"), 0) AS DOUBLE) AS \"sales.w\"\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON sales.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n sales.region", - "param/local_ranked_transform::sqlite": "WITH base_2 AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum_partition_by_region\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum_partition_by_region\",\n RANK() OVER (ORDER BY \"sales.amount_sum_partition_by_region\" DESC) AS \"sales.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"sales.region\" AS \"region\",\n _stage_inner.\"sales.rank\" AS \"rank\"\n FROM (\n SELECT\n \"sales.region\",\n \"sales.rank\"\n FROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum_partition_by_region\",\n \"sales.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS \"sales.region\",\n CAST(SUM(sales.amount * _cm_rank.\"rank\") / NULLIF(SUM(_cm_rank.\"rank\"), 0) AS REAL) AS \"sales.w\"\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON sales.region IS _cm_rank.\"region\"\nGROUP BY\n sales.region", - "param/local_ranked_transform::tsql": "WITH base_2 AS (\n SELECT\n sales.region AS [sales___region],\n SUM(sales.amount) AS [sales___amount_sum_partition_by_region]\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n [sales___region] AS [sales___region],\n [sales___amount_sum_partition_by_region] AS [sales___amount_sum_partition_by_region],\n RANK() OVER (ORDER BY [sales___amount_sum_partition_by_region] DESC) AS [sales___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[sales___region] AS [region],\n _stage_inner.[sales___rank] AS [rank]\n FROM (\n SELECT\n [sales___region] AS [sales___region],\n [sales___rank] AS [sales___rank]\n FROM (\n SELECT\n [sales___region],\n [sales___amount_sum_partition_by_region],\n [sales___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS [sales___region],\n CAST(SUM(sales.amount * _cm_rank.[rank]) / NULLIF(SUM(_cm_rank.[rank]), 0) AS FLOAT) AS [sales___w]\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON (\n sales.region = _cm_rank.[region]\n OR (\n sales.region IS NULL AND _cm_rank.[region] IS NULL\n )\n )\nGROUP BY\n sales.region", + "param/local_ranked_transform::bigquery": "WITH base_2 AS (\n SELECT\n sales.region AS `sales___region`,\n SUM(sales.amount) AS `sales___amount_sum_partition_by_region`\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n `sales___region`,\n `sales___amount_sum_partition_by_region`,\n CASE\n WHEN `sales___amount_sum_partition_by_region` IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN `sales___amount_sum_partition_by_region` IS NULL THEN 1 ELSE 0 END\n ORDER BY `sales___amount_sum_partition_by_region` DESC\n )\n END AS `sales___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`sales___region` AS `region`,\n _stage_inner.`sales___rank` AS `rank`\n FROM (\n SELECT\n `sales___region`,\n `sales___rank`\n FROM (\n SELECT\n `sales___region`,\n `sales___amount_sum_partition_by_region`,\n `sales___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS `sales___region`,\n CAST(SUM(sales.amount * _cm_rank.`rank`) / NULLIF(SUM(_cm_rank.`rank`), 0) AS FLOAT64) AS `sales___w`\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON sales.region IS NOT DISTINCT FROM _cm_rank.`region`\nGROUP BY\n sales.region", + "param/local_ranked_transform::duckdb": "WITH base_2 AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum_partition_by_region\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum_partition_by_region\",\n CASE\n WHEN \"sales.amount_sum_partition_by_region\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN \"sales.amount_sum_partition_by_region\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum_partition_by_region\" DESC\n )\n END AS \"sales.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"sales.region\" AS \"region\",\n _stage_inner.\"sales.rank\" AS \"rank\"\n FROM (\n SELECT\n \"sales.region\",\n \"sales.rank\"\n FROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum_partition_by_region\",\n \"sales.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS \"sales.region\",\n CAST(SUM(sales.amount * _cm_rank.\"rank\") / NULLIF(SUM(_cm_rank.\"rank\"), 0) AS DOUBLE) AS \"sales.w\"\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON sales.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n sales.region", + "param/local_ranked_transform::mysql": "WITH base_2 AS (\n SELECT\n sales.region AS `sales.region`,\n SUM(sales.amount) AS `sales.amount_sum_partition_by_region`\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n `sales.region`,\n `sales.amount_sum_partition_by_region`,\n CASE\n WHEN `sales.amount_sum_partition_by_region` IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN `sales.amount_sum_partition_by_region` IS NULL THEN 1 ELSE 0 END\n ORDER BY `sales.amount_sum_partition_by_region` DESC\n )\n END AS `sales.rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`sales.region` AS `region`,\n _stage_inner.`sales.rank` AS `rank`\n FROM (\n SELECT\n `sales.region`,\n `sales.rank`\n FROM (\n SELECT\n `sales.region`,\n `sales.amount_sum_partition_by_region`,\n `sales.rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS `sales.region`,\n CAST(SUM(sales.amount * _cm_rank.`rank`) / NULLIF(SUM(_cm_rank.`rank`), 0) AS DOUBLE) AS `sales.w`\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON sales.region <=> _cm_rank.`region`\nGROUP BY\n sales.region", + "param/local_ranked_transform::postgres": "WITH base_2 AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum_partition_by_region\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum_partition_by_region\",\n CASE\n WHEN \"sales.amount_sum_partition_by_region\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN \"sales.amount_sum_partition_by_region\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum_partition_by_region\" DESC\n )\n END AS \"sales.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"sales.region\" AS \"region\",\n _stage_inner.\"sales.rank\" AS \"rank\"\n FROM (\n SELECT\n \"sales.region\",\n \"sales.rank\"\n FROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum_partition_by_region\",\n \"sales.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS \"sales.region\",\n CAST(SUM(sales.amount * _cm_rank.\"rank\") / NULLIF(SUM(_cm_rank.\"rank\"), 0) AS DOUBLE PRECISION) AS \"sales.w\"\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON sales.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n sales.region", + "param/local_ranked_transform::snowflake": "WITH base_2 AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum_partition_by_region\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum_partition_by_region\",\n CASE\n WHEN \"sales.amount_sum_partition_by_region\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN \"sales.amount_sum_partition_by_region\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum_partition_by_region\" DESC\n )\n END AS \"sales.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"sales.region\" AS \"region\",\n _stage_inner.\"sales.rank\" AS \"rank\"\n FROM (\n SELECT\n \"sales.region\",\n \"sales.rank\"\n FROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum_partition_by_region\",\n \"sales.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS \"sales.region\",\n CAST(SUM(sales.amount * _cm_rank.\"rank\") / NULLIF(SUM(_cm_rank.\"rank\"), 0) AS DOUBLE) AS \"sales.w\"\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON sales.region IS NOT DISTINCT FROM _cm_rank.\"region\"\nGROUP BY\n sales.region", + "param/local_ranked_transform::sqlite": "WITH base_2 AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum_partition_by_region\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum_partition_by_region\",\n CASE\n WHEN \"sales.amount_sum_partition_by_region\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN \"sales.amount_sum_partition_by_region\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum_partition_by_region\" DESC\n )\n END AS \"sales.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"sales.region\" AS \"region\",\n _stage_inner.\"sales.rank\" AS \"rank\"\n FROM (\n SELECT\n \"sales.region\",\n \"sales.rank\"\n FROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum_partition_by_region\",\n \"sales.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS \"sales.region\",\n CAST(SUM(sales.amount * _cm_rank.\"rank\") / NULLIF(SUM(_cm_rank.\"rank\"), 0) AS REAL) AS \"sales.w\"\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON sales.region IS _cm_rank.\"region\"\nGROUP BY\n sales.region", + "param/local_ranked_transform::tsql": "WITH base_2 AS (\n SELECT\n sales.region AS [sales___region],\n SUM(sales.amount) AS [sales___amount_sum_partition_by_region]\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n [sales___region] AS [sales___region],\n [sales___amount_sum_partition_by_region] AS [sales___amount_sum_partition_by_region],\n CASE\n WHEN [sales___amount_sum_partition_by_region] IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN [sales___amount_sum_partition_by_region] IS NULL THEN 1 ELSE 0 END\n ORDER BY [sales___amount_sum_partition_by_region] DESC\n )\n END AS [sales___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[sales___region] AS [region],\n _stage_inner.[sales___rank] AS [rank]\n FROM (\n SELECT\n [sales___region] AS [sales___region],\n [sales___rank] AS [sales___rank]\n FROM (\n SELECT\n [sales___region],\n [sales___amount_sum_partition_by_region],\n [sales___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n)\nSELECT\n sales.region AS [sales___region],\n CAST(SUM(sales.amount * _cm_rank.[rank]) / NULLIF(SUM(_cm_rank.[rank]), 0) AS FLOAT) AS [sales___w]\nFROM sales AS sales\nLEFT JOIN _cm_rank\n ON (\n sales.region = _cm_rank.[region]\n OR (\n sales.region IS NULL AND _cm_rank.[region] IS NULL\n )\n )\nGROUP BY\n sales.region", "param/mixed_plus::bigquery": "WITH _cm_unit_price_avg_partition_by_product AS (\n SELECT\n _stage_inner.`sales___product` AS `product`,\n _stage_inner.`sales___unit_price_avg_partition_by_product` AS `unit_price_avg_partition_by_product`\n FROM (\n SELECT\n sales.product AS `sales___product`,\n CAST(AVG(sales.unit_price) AS FLOAT64) AS `sales___unit_price_avg_partition_by_product`\n FROM sales AS sales\n GROUP BY\n sales.product\n ) AS _stage_inner\n), _cm_amount_sum_partition_by_region AS (\n SELECT\n _stage_inner.`sales___region` AS `region`,\n _stage_inner.`sales___amount_sum_partition_by_region` AS `amount_sum_partition_by_region`\n FROM (\n SELECT\n sales.region AS `sales___region`,\n CAST(SUM(sales.amount) AS FLOAT64) AS `sales___amount_sum_partition_by_region`\n FROM sales AS sales\n GROUP BY\n sales.region\n ) AS _stage_inner\n)\nSELECT\n sales.region AS `sales___region`,\n CAST(SUM(\n (\n sales.quantity * _cm_unit_price_avg_partition_by_product.`unit_price_avg_partition_by_product`\n ) * _cm_amount_sum_partition_by_region.`amount_sum_partition_by_region`\n ) / NULLIF(SUM(_cm_amount_sum_partition_by_region.`amount_sum_partition_by_region`), 0) AS FLOAT64) AS `sales___w`\nFROM sales AS sales\nLEFT JOIN _cm_unit_price_avg_partition_by_product\n ON sales.product IS NOT DISTINCT FROM _cm_unit_price_avg_partition_by_product.`product`\nLEFT JOIN _cm_amount_sum_partition_by_region\n ON sales.region IS NOT DISTINCT FROM _cm_amount_sum_partition_by_region.`region`\nGROUP BY\n sales.region", "param/mixed_plus::duckdb": "WITH _cm_unit_price_avg_partition_by_product AS (\n SELECT\n _stage_inner.\"sales.product\" AS \"product\",\n _stage_inner.\"sales.unit_price_avg_partition_by_product\" AS \"unit_price_avg_partition_by_product\"\n FROM (\n SELECT\n sales.product AS \"sales.product\",\n CAST(AVG(sales.unit_price) AS DOUBLE) AS \"sales.unit_price_avg_partition_by_product\"\n FROM sales AS sales\n GROUP BY\n sales.product\n ) AS _stage_inner\n), _cm_amount_sum_partition_by_region AS (\n SELECT\n _stage_inner.\"sales.region\" AS \"region\",\n _stage_inner.\"sales.amount_sum_partition_by_region\" AS \"amount_sum_partition_by_region\"\n FROM (\n SELECT\n sales.region AS \"sales.region\",\n CAST(SUM(sales.amount) AS DOUBLE) AS \"sales.amount_sum_partition_by_region\"\n FROM sales AS sales\n GROUP BY\n sales.region\n ) AS _stage_inner\n)\nSELECT\n sales.region AS \"sales.region\",\n CAST(SUM(\n (\n sales.quantity * _cm_unit_price_avg_partition_by_product.\"unit_price_avg_partition_by_product\"\n ) * _cm_amount_sum_partition_by_region.\"amount_sum_partition_by_region\"\n ) / NULLIF(SUM(_cm_amount_sum_partition_by_region.\"amount_sum_partition_by_region\"), 0) AS DOUBLE) AS \"sales.w\"\nFROM sales AS sales\nLEFT JOIN _cm_unit_price_avg_partition_by_product\n ON sales.product IS NOT DISTINCT FROM _cm_unit_price_avg_partition_by_product.\"product\"\nLEFT JOIN _cm_amount_sum_partition_by_region\n ON sales.region IS NOT DISTINCT FROM _cm_amount_sum_partition_by_region.\"region\"\nGROUP BY\n sales.region", "param/mixed_plus::mysql": "WITH _cm_unit_price_avg_partition_by_product AS (\n SELECT\n _stage_inner.`sales.product` AS `product`,\n _stage_inner.`sales.unit_price_avg_partition_by_product` AS `unit_price_avg_partition_by_product`\n FROM (\n SELECT\n sales.product AS `sales.product`,\n CAST(AVG(sales.unit_price) AS DOUBLE) AS `sales.unit_price_avg_partition_by_product`\n FROM sales AS sales\n GROUP BY\n sales.product\n ) AS _stage_inner\n), _cm_amount_sum_partition_by_region AS (\n SELECT\n _stage_inner.`sales.region` AS `region`,\n _stage_inner.`sales.amount_sum_partition_by_region` AS `amount_sum_partition_by_region`\n FROM (\n SELECT\n sales.region AS `sales.region`,\n CAST(SUM(sales.amount) AS DOUBLE) AS `sales.amount_sum_partition_by_region`\n FROM sales AS sales\n GROUP BY\n sales.region\n ) AS _stage_inner\n)\nSELECT\n sales.region AS `sales.region`,\n CAST(SUM(\n (\n sales.quantity * _cm_unit_price_avg_partition_by_product.`unit_price_avg_partition_by_product`\n ) * _cm_amount_sum_partition_by_region.`amount_sum_partition_by_region`\n ) / NULLIF(SUM(_cm_amount_sum_partition_by_region.`amount_sum_partition_by_region`), 0) AS DOUBLE) AS `sales.w`\nFROM sales AS sales\nLEFT JOIN _cm_unit_price_avg_partition_by_product\n ON sales.product <=> _cm_unit_price_avg_partition_by_product.`product`\nLEFT JOIN _cm_amount_sum_partition_by_region\n ON sales.region <=> _cm_amount_sum_partition_by_region.`region`\nGROUP BY\n sales.region", @@ -62,13 +62,13 @@ "param/ordinary::snowflake": "WITH _cm_amount_sum_partition_by_region AS (\n SELECT\n _stage_inner.\"sales.region\" AS \"region\",\n _stage_inner.\"sales.amount_sum_partition_by_region\" AS \"amount_sum_partition_by_region\"\n FROM (\n SELECT\n sales.region AS \"sales.region\",\n CAST(SUM(sales.amount) AS DOUBLE) AS \"sales.amount_sum_partition_by_region\"\n FROM sales AS sales\n GROUP BY\n sales.region\n ) AS _stage_inner\n)\nSELECT\n sales.region AS \"sales.region\",\n CAST(SUM(\n sales.amount * _cm_amount_sum_partition_by_region.\"amount_sum_partition_by_region\"\n ) / NULLIF(SUM(_cm_amount_sum_partition_by_region.\"amount_sum_partition_by_region\"), 0) AS DOUBLE) AS \"sales.w\"\nFROM sales AS sales\nLEFT JOIN _cm_amount_sum_partition_by_region\n ON sales.region IS NOT DISTINCT FROM _cm_amount_sum_partition_by_region.\"region\"\nGROUP BY\n sales.region", "param/ordinary::sqlite": "WITH _cm_amount_sum_partition_by_region AS (\n SELECT\n _stage_inner.\"sales.region\" AS \"region\",\n _stage_inner.\"sales.amount_sum_partition_by_region\" AS \"amount_sum_partition_by_region\"\n FROM (\n SELECT\n sales.region AS \"sales.region\",\n CAST(SUM(sales.amount) AS REAL) AS \"sales.amount_sum_partition_by_region\"\n FROM sales AS sales\n GROUP BY\n sales.region\n ) AS _stage_inner\n)\nSELECT\n sales.region AS \"sales.region\",\n CAST(SUM(\n sales.amount * _cm_amount_sum_partition_by_region.\"amount_sum_partition_by_region\"\n ) / NULLIF(SUM(_cm_amount_sum_partition_by_region.\"amount_sum_partition_by_region\"), 0) AS REAL) AS \"sales.w\"\nFROM sales AS sales\nLEFT JOIN _cm_amount_sum_partition_by_region\n ON sales.region IS _cm_amount_sum_partition_by_region.\"region\"\nGROUP BY\n sales.region", "param/ordinary::tsql": "WITH _cm_amount_sum_partition_by_region AS (\n SELECT\n _stage_inner.[sales___region] AS [region],\n _stage_inner.[sales___amount_sum_partition_by_region] AS [amount_sum_partition_by_region]\n FROM (\n SELECT\n sales.region AS [sales___region],\n CAST(SUM(sales.amount) AS FLOAT) AS [sales___amount_sum_partition_by_region]\n FROM sales AS sales\n GROUP BY\n sales.region\n ) AS _stage_inner\n)\nSELECT\n sales.region AS [sales___region],\n CAST(SUM(\n sales.amount * _cm_amount_sum_partition_by_region.[amount_sum_partition_by_region]\n ) / NULLIF(SUM(_cm_amount_sum_partition_by_region.[amount_sum_partition_by_region]), 0) AS FLOAT) AS [sales___w]\nFROM sales AS sales\nLEFT JOIN _cm_amount_sum_partition_by_region\n ON (\n sales.region = _cm_amount_sum_partition_by_region.[region]\n OR (\n sales.region IS NULL AND _cm_amount_sum_partition_by_region.[region] IS NULL\n )\n )\nGROUP BY\n sales.region", - "param/ranked_transform::bigquery": "WITH _base AS (\n SELECT\n orders.status AS `orders___status`\n FROM orders AS orders\n GROUP BY\n orders.status\n), _base_2 AS (\n SELECT\n regions.name AS `customers___regions___name`\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n GROUP BY\n regions.name\n), _cm_customers__orders__amount_sum_partition_by_regions_name AS (\n SELECT\n customers__regions.name AS `orders___customers___regions___name`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_customers_regions_name`\n FROM orders AS orders\n LEFT JOIN customers AS customers\n ON orders.customer_id = customers.id\n LEFT JOIN regions AS customers__regions\n ON customers.region_id = customers__regions.id\n GROUP BY\n customers__regions.name\n), base_2 AS (\n SELECT\n _base_2.`customers___regions___name`,\n _cm_customers__orders__amount_sum_partition_by_regions_name.`orders___amount_sum_partition_by_customers_regions_name` AS `orders___amount_sum_partition_by_customers_regions_name`\n FROM _base_2\n LEFT JOIN _cm_customers__orders__amount_sum_partition_by_regions_name\n ON _base_2.`customers___regions___name` IS NOT DISTINCT FROM _cm_customers__orders__amount_sum_partition_by_regions_name.`orders___customers___regions___name`\n), step1 AS (\n SELECT\n `customers___regions___name`,\n `orders___amount_sum_partition_by_customers_regions_name`,\n RANK() OVER (ORDER BY `orders___amount_sum_partition_by_customers_regions_name` DESC) AS `customers___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`customers___regions___name` AS `regions__name`,\n _stage_inner.`customers___rank` AS `rank`\n FROM (\n SELECT\n `customers___regions___name`,\n `customers___rank`\n FROM (\n SELECT\n `customers___regions___name`,\n `orders___amount_sum_partition_by_customers_regions_name`,\n `customers___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_orders__customers__spend_weighted_avg_weight_rank_sum_amount_by_customers_regions_name AS (\n SELECT\n CAST(SUM(customers.spend * _cm_rank.`rank`) / NULLIF(SUM(_cm_rank.`rank`), 0) AS FLOAT64) AS `customers___spend_weighted_avg_weight_rank_sum_orders_amount_by_regions_name`\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n LEFT JOIN _cm_rank\n ON regions.name IS NOT DISTINCT FROM _cm_rank.`regions__name`\n)\nSELECT\n _base.`orders___status`,\n _cm_orders__customers__spend_weighted_avg_weight_rank_sum_amount_by_customers_regions_name.`customers___spend_weighted_avg_weight_rank_sum_orders_amount_by_regions_name` AS `orders___w`\nFROM _base\nCROSS JOIN _cm_orders__customers__spend_weighted_avg_weight_rank_sum_amount_by_customers_regions_name", - "param/ranked_transform::duckdb": "WITH _base AS (\n SELECT\n orders.status AS \"orders.status\"\n FROM orders AS orders\n GROUP BY\n orders.status\n), _base_2 AS (\n SELECT\n regions.name AS \"customers.regions.name\"\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n GROUP BY\n regions.name\n), _cm_customers__orders__amount_sum_partition_by_regions_name AS (\n SELECT\n customers__regions.name AS \"orders.customers.regions.name\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_customers_regions_name\"\n FROM orders AS orders\n LEFT JOIN customers AS customers\n ON orders.customer_id = customers.id\n LEFT JOIN regions AS customers__regions\n ON customers.region_id = customers__regions.id\n GROUP BY\n customers__regions.name\n), base_2 AS (\n SELECT\n _base_2.\"customers.regions.name\",\n _cm_customers__orders__amount_sum_partition_by_regions_name.\"orders.amount_sum_partition_by_customers_regions_name\" AS \"orders.amount_sum_partition_by_customers_regions_name\"\n FROM _base_2\n LEFT JOIN _cm_customers__orders__amount_sum_partition_by_regions_name\n ON _base_2.\"customers.regions.name\" IS NOT DISTINCT FROM _cm_customers__orders__amount_sum_partition_by_regions_name.\"orders.customers.regions.name\"\n), step1 AS (\n SELECT\n \"customers.regions.name\",\n \"orders.amount_sum_partition_by_customers_regions_name\",\n RANK() OVER (ORDER BY \"orders.amount_sum_partition_by_customers_regions_name\" DESC) AS \"customers.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"customers.regions.name\" AS \"regions__name\",\n _stage_inner.\"customers.rank\" AS \"rank\"\n FROM (\n SELECT\n \"customers.regions.name\",\n \"customers.rank\"\n FROM (\n SELECT\n \"customers.regions.name\",\n \"orders.amount_sum_partition_by_customers_regions_name\",\n \"customers.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_orders__customers__spend_weighted_avg_weight_rank_sum_amount_by_customers_regions_name AS (\n SELECT\n CAST(SUM(customers.spend * _cm_rank.\"rank\") / NULLIF(SUM(_cm_rank.\"rank\"), 0) AS DOUBLE) AS \"customers.spend_weighted_avg_weight_rank_sum_orders_amount_by_regions_name\"\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n LEFT JOIN _cm_rank\n ON regions.name IS NOT DISTINCT FROM _cm_rank.\"regions__name\"\n)\nSELECT\n _base.\"orders.status\",\n _cm_orders__customers__spend_weighted_avg_weight_rank_sum_amount_by_customers_regions_name.\"customers.spend_weighted_avg_weight_rank_sum_orders_amount_by_regions_name\" AS \"orders.w\"\nFROM _base\nCROSS JOIN _cm_orders__customers__spend_weighted_avg_weight_rank_sum_amount_by_customers_regions_name", - "param/ranked_transform::mysql": "WITH _base AS (\n SELECT\n orders.status AS `orders.status`\n FROM orders AS orders\n GROUP BY\n orders.status\n), _base_2 AS (\n SELECT\n regions.name AS `customers.regions.name`\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n GROUP BY\n regions.name\n), _cm_customers__orders__amount_sum_partition_by_regions_name AS (\n SELECT\n customers__regions.name AS `orders.customers.regions.name`,\n CAST(SUM(orders.amount) AS DOUBLE) AS `orders.amount_sum_partition_by_customers_regions_name`\n FROM orders AS orders\n LEFT JOIN customers AS customers\n ON orders.customer_id = customers.id\n LEFT JOIN regions AS customers__regions\n ON customers.region_id = customers__regions.id\n GROUP BY\n customers__regions.name\n), base_2 AS (\n SELECT\n _base_2.`customers.regions.name`,\n _cm_customers__orders__amount_sum_partition_by_regions_name.`orders.amount_sum_partition_by_customers_regions_name` AS `orders.amount_sum_partition_by_customers_regions_name`\n FROM _base_2\n LEFT JOIN _cm_customers__orders__amount_sum_partition_by_regions_name\n ON _base_2.`customers.regions.name` <=> _cm_customers__orders__amount_sum_partition_by_regions_name.`orders.customers.regions.name`\n), step1 AS (\n SELECT\n `customers.regions.name`,\n `orders.amount_sum_partition_by_customers_regions_name`,\n RANK() OVER (ORDER BY `orders.amount_sum_partition_by_customers_regions_name` DESC) AS `customers.rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`customers.regions.name` AS `regions__name`,\n _stage_inner.`customers.rank` AS `rank`\n FROM (\n SELECT\n `customers.regions.name`,\n `customers.rank`\n FROM (\n SELECT\n `customers.regions.name`,\n `orders.amount_sum_partition_by_customers_regions_name`,\n `customers.rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_orders__customers__spen_8fc01fb0_t_by_customers_regions_name AS (\n SELECT\n CAST(SUM(customers.spend * _cm_rank.`rank`) / NULLIF(SUM(_cm_rank.`rank`), 0) AS DOUBLE) AS `customers.spend_weighted_av_6ae319d7_ders_amount_by_regions_name`\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n LEFT JOIN _cm_rank\n ON regions.name <=> _cm_rank.`regions__name`\n)\nSELECT\n _base.`orders.status`,\n _cm_orders__customers__spen_8fc01fb0_t_by_customers_regions_name.`customers.spend_weighted_av_6ae319d7_ders_amount_by_regions_name` AS `orders.w`\nFROM _base\nCROSS JOIN _cm_orders__customers__spen_8fc01fb0_t_by_customers_regions_name", - "param/ranked_transform::postgres": "WITH _base AS (\n SELECT\n orders.status AS \"orders.status\"\n FROM orders AS orders\n GROUP BY\n orders.status\n), _base_2 AS (\n SELECT\n regions.name AS \"customers.regions.name\"\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n GROUP BY\n regions.name\n), _cm_customers__orders__amount_sum_partition_by_regions_name AS (\n SELECT\n customers__regions.name AS \"orders.customers.regions.name\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_customers_regions_name\"\n FROM orders AS orders\n LEFT JOIN customers AS customers\n ON orders.customer_id = customers.id\n LEFT JOIN regions AS customers__regions\n ON customers.region_id = customers__regions.id\n GROUP BY\n customers__regions.name\n), base_2 AS (\n SELECT\n _base_2.\"customers.regions.name\",\n _cm_customers__orders__amount_sum_partition_by_regions_name.\"orders.amount_sum_partition_by_customers_regions_name\" AS \"orders.amount_sum_partition_by_customers_regions_name\"\n FROM _base_2\n LEFT JOIN _cm_customers__orders__amount_sum_partition_by_regions_name\n ON _base_2.\"customers.regions.name\" IS NOT DISTINCT FROM _cm_customers__orders__amount_sum_partition_by_regions_name.\"orders.customers.regions.name\"\n), step1 AS (\n SELECT\n \"customers.regions.name\",\n \"orders.amount_sum_partition_by_customers_regions_name\",\n RANK() OVER (ORDER BY \"orders.amount_sum_partition_by_customers_regions_name\" DESC NULLS LAST) AS \"customers.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"customers.regions.name\" AS \"regions__name\",\n _stage_inner.\"customers.rank\" AS \"rank\"\n FROM (\n SELECT\n \"customers.regions.name\",\n \"customers.rank\"\n FROM (\n SELECT\n \"customers.regions.name\",\n \"orders.amount_sum_partition_by_customers_regions_name\",\n \"customers.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_orders__customers__spen_8fc01fb0_by_customers_regions_name AS (\n SELECT\n CAST(SUM(customers.spend * _cm_rank.\"rank\") / NULLIF(SUM(_cm_rank.\"rank\"), 0) AS DOUBLE PRECISION) AS \"customers.spend_weighted_av_6ae319d7_ers_amount_by_regions_name\"\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n LEFT JOIN _cm_rank\n ON regions.name IS NOT DISTINCT FROM _cm_rank.\"regions__name\"\n)\nSELECT\n _base.\"orders.status\",\n _cm_orders__customers__spen_8fc01fb0_by_customers_regions_name.\"customers.spend_weighted_av_6ae319d7_ers_amount_by_regions_name\" AS \"orders.w\"\nFROM _base\nCROSS JOIN _cm_orders__customers__spen_8fc01fb0_by_customers_regions_name", - "param/ranked_transform::snowflake": "WITH _base AS (\n SELECT\n orders.status AS \"orders.status\"\n FROM orders AS orders\n GROUP BY\n orders.status\n), _base_2 AS (\n SELECT\n regions.name AS \"customers.regions.name\"\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n GROUP BY\n regions.name\n), _cm_customers__orders__amount_sum_partition_by_regions_name AS (\n SELECT\n customers__regions.name AS \"orders.customers.regions.name\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_customers_regions_name\"\n FROM orders AS orders\n LEFT JOIN customers AS customers\n ON orders.customer_id = customers.id\n LEFT JOIN regions AS customers__regions\n ON customers.region_id = customers__regions.id\n GROUP BY\n customers__regions.name\n), base_2 AS (\n SELECT\n _base_2.\"customers.regions.name\",\n _cm_customers__orders__amount_sum_partition_by_regions_name.\"orders.amount_sum_partition_by_customers_regions_name\" AS \"orders.amount_sum_partition_by_customers_regions_name\"\n FROM _base_2\n LEFT JOIN _cm_customers__orders__amount_sum_partition_by_regions_name\n ON _base_2.\"customers.regions.name\" IS NOT DISTINCT FROM _cm_customers__orders__amount_sum_partition_by_regions_name.\"orders.customers.regions.name\"\n), step1 AS (\n SELECT\n \"customers.regions.name\",\n \"orders.amount_sum_partition_by_customers_regions_name\",\n RANK() OVER (ORDER BY \"orders.amount_sum_partition_by_customers_regions_name\" DESC NULLS LAST) AS \"customers.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"customers.regions.name\" AS \"regions__name\",\n _stage_inner.\"customers.rank\" AS \"rank\"\n FROM (\n SELECT\n \"customers.regions.name\",\n \"customers.rank\"\n FROM (\n SELECT\n \"customers.regions.name\",\n \"orders.amount_sum_partition_by_customers_regions_name\",\n \"customers.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_orders__customers__spend_weighted_avg_weight_rank_sum_amount_by_customers_regions_name AS (\n SELECT\n CAST(SUM(customers.spend * _cm_rank.\"rank\") / NULLIF(SUM(_cm_rank.\"rank\"), 0) AS DOUBLE) AS \"customers.spend_weighted_avg_weight_rank_sum_orders_amount_by_regions_name\"\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n LEFT JOIN _cm_rank\n ON regions.name IS NOT DISTINCT FROM _cm_rank.\"regions__name\"\n)\nSELECT\n _base.\"orders.status\",\n _cm_orders__customers__spend_weighted_avg_weight_rank_sum_amount_by_customers_regions_name.\"customers.spend_weighted_avg_weight_rank_sum_orders_amount_by_regions_name\" AS \"orders.w\"\nFROM _base\nCROSS JOIN _cm_orders__customers__spend_weighted_avg_weight_rank_sum_amount_by_customers_regions_name", - "param/ranked_transform::sqlite": "WITH _base AS (\n SELECT\n orders.status AS \"orders.status\"\n FROM orders AS orders\n GROUP BY\n orders.status\n), _base_2 AS (\n SELECT\n regions.name AS \"customers.regions.name\"\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n GROUP BY\n regions.name\n), _cm_customers__orders__amount_sum_partition_by_regions_name AS (\n SELECT\n customers__regions.name AS \"orders.customers.regions.name\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_customers_regions_name\"\n FROM orders AS orders\n LEFT JOIN customers AS customers\n ON orders.customer_id = customers.id\n LEFT JOIN regions AS customers__regions\n ON customers.region_id = customers__regions.id\n GROUP BY\n customers__regions.name\n), base_2 AS (\n SELECT\n _base_2.\"customers.regions.name\",\n _cm_customers__orders__amount_sum_partition_by_regions_name.\"orders.amount_sum_partition_by_customers_regions_name\" AS \"orders.amount_sum_partition_by_customers_regions_name\"\n FROM _base_2\n LEFT JOIN _cm_customers__orders__amount_sum_partition_by_regions_name\n ON _base_2.\"customers.regions.name\" IS _cm_customers__orders__amount_sum_partition_by_regions_name.\"orders.customers.regions.name\"\n), step1 AS (\n SELECT\n \"customers.regions.name\",\n \"orders.amount_sum_partition_by_customers_regions_name\",\n RANK() OVER (ORDER BY \"orders.amount_sum_partition_by_customers_regions_name\" DESC) AS \"customers.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"customers.regions.name\" AS \"regions__name\",\n _stage_inner.\"customers.rank\" AS \"rank\"\n FROM (\n SELECT\n \"customers.regions.name\",\n \"customers.rank\"\n FROM (\n SELECT\n \"customers.regions.name\",\n \"orders.amount_sum_partition_by_customers_regions_name\",\n \"customers.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_orders__customers__spend_weighted_avg_weight_rank_sum_amount_by_customers_regions_name AS (\n SELECT\n CAST(SUM(customers.spend * _cm_rank.\"rank\") / NULLIF(SUM(_cm_rank.\"rank\"), 0) AS REAL) AS \"customers.spend_weighted_avg_weight_rank_sum_orders_amount_by_regions_name\"\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n LEFT JOIN _cm_rank\n ON regions.name IS _cm_rank.\"regions__name\"\n)\nSELECT\n _base.\"orders.status\",\n _cm_orders__customers__spend_weighted_avg_weight_rank_sum_amount_by_customers_regions_name.\"customers.spend_weighted_avg_weight_rank_sum_orders_amount_by_regions_name\" AS \"orders.w\"\nFROM _base\nCROSS JOIN _cm_orders__customers__spend_weighted_avg_weight_rank_sum_amount_by_customers_regions_name", - "param/ranked_transform::tsql": "WITH _base AS (\n SELECT\n orders.status AS [orders___status]\n FROM orders AS orders\n GROUP BY\n orders.status\n), _base_2 AS (\n SELECT\n regions.name AS [customers___regions___name]\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n GROUP BY\n regions.name\n), _cm_customers__orders__amount_sum_partition_by_regions_name AS (\n SELECT\n customers__regions.name AS [orders___customers___regions___name],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_customers_regions_name]\n FROM orders AS orders\n LEFT JOIN customers AS customers\n ON orders.customer_id = customers.id\n LEFT JOIN regions AS customers__regions\n ON customers.region_id = customers__regions.id\n GROUP BY\n customers__regions.name\n), base_2 AS (\n SELECT\n _base_2.[customers___regions___name] AS [customers___regions___name],\n _cm_customers__orders__amount_sum_partition_by_regions_name.[orders___amount_sum_partition_by_customers_regions_name] AS [orders___amount_sum_partition_by_customers_regions_name]\n FROM _base_2\n LEFT JOIN _cm_customers__orders__amount_sum_partition_by_regions_name\n ON (\n _base_2.[customers___regions___name] = _cm_customers__orders__amount_sum_partition_by_regions_name.[orders___customers___regions___name]\n OR (\n _base_2.[customers___regions___name] IS NULL\n AND _cm_customers__orders__amount_sum_partition_by_regions_name.[orders___customers___regions___name] IS NULL\n )\n )\n), step1 AS (\n SELECT\n [customers___regions___name] AS [customers___regions___name],\n [orders___amount_sum_partition_by_customers_regions_name] AS [orders___amount_sum_partition_by_customers_regions_name],\n RANK() OVER (ORDER BY [orders___amount_sum_partition_by_customers_regions_name] DESC) AS [customers___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[customers___regions___name] AS [regions__name],\n _stage_inner.[customers___rank] AS [rank]\n FROM (\n SELECT\n [customers___regions___name] AS [customers___regions___name],\n [customers___rank] AS [customers___rank]\n FROM (\n SELECT\n [customers___regions___name],\n [orders___amount_sum_partition_by_customers_regions_name],\n [customers___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_orders__customers__spend_weighted_avg_weight_rank_sum_amount_by_customers_regions_name AS (\n SELECT\n CAST(SUM(customers.spend * _cm_rank.[rank]) / NULLIF(SUM(_cm_rank.[rank]), 0) AS FLOAT) AS [customers___spend_weighted_avg_weight_rank_sum_orders_amount_by_regions_name]\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n LEFT JOIN _cm_rank\n ON (\n regions.name = _cm_rank.[regions__name]\n OR (\n regions.name IS NULL AND _cm_rank.[regions__name] IS NULL\n )\n )\n)\nSELECT\n _base.[orders___status],\n _cm_orders__customers__spend_weighted_avg_weight_rank_sum_amount_by_customers_regions_name.[customers___spend_weighted_avg_weight_rank_sum_orders_amount_by_regions_name] AS [orders___w]\nFROM _base\nCROSS JOIN _cm_orders__customers__spend_weighted_avg_weight_rank_sum_amount_by_customers_regions_name", + "param/ranked_transform::bigquery": "WITH _base AS (\n SELECT\n orders.status AS `orders___status`\n FROM orders AS orders\n GROUP BY\n orders.status\n), _base_2 AS (\n SELECT\n regions.name AS `customers___regions___name`\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n GROUP BY\n regions.name\n), _cm_customers__orders__amount_sum_partition_by_regions_name AS (\n SELECT\n customers__regions.name AS `orders___customers___regions___name`,\n CAST(SUM(orders.amount) AS FLOAT64) AS `orders___amount_sum_partition_by_customers_regions_name`\n FROM orders AS orders\n LEFT JOIN customers AS customers\n ON orders.customer_id = customers.id\n LEFT JOIN regions AS customers__regions\n ON customers.region_id = customers__regions.id\n GROUP BY\n customers__regions.name\n), base_2 AS (\n SELECT\n _base_2.`customers___regions___name`,\n _cm_customers__orders__amount_sum_partition_by_regions_name.`orders___amount_sum_partition_by_customers_regions_name` AS `orders___amount_sum_partition_by_customers_regions_name`\n FROM _base_2\n LEFT JOIN _cm_customers__orders__amount_sum_partition_by_regions_name\n ON _base_2.`customers___regions___name` IS NOT DISTINCT FROM _cm_customers__orders__amount_sum_partition_by_regions_name.`orders___customers___regions___name`\n), step1 AS (\n SELECT\n `customers___regions___name`,\n `orders___amount_sum_partition_by_customers_regions_name`,\n CASE\n WHEN `orders___amount_sum_partition_by_customers_regions_name` IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN `orders___amount_sum_partition_by_customers_regions_name` IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY `orders___amount_sum_partition_by_customers_regions_name` DESC\n )\n END AS `customers___rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`customers___regions___name` AS `regions__name`,\n _stage_inner.`customers___rank` AS `rank`\n FROM (\n SELECT\n `customers___regions___name`,\n `customers___rank`\n FROM (\n SELECT\n `customers___regions___name`,\n `orders___amount_sum_partition_by_customers_regions_name`,\n `customers___rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_orders__customers__spend_weighted_avg_weight_rank_direction_desc_sum_amount_by_customers_regions_name AS (\n SELECT\n CAST(SUM(customers.spend * _cm_rank.`rank`) / NULLIF(SUM(_cm_rank.`rank`), 0) AS FLOAT64) AS `customers___spend_weighted_avg_weight_rank_direction_desc_sum_orders_amount_by_regions_name`\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n LEFT JOIN _cm_rank\n ON regions.name IS NOT DISTINCT FROM _cm_rank.`regions__name`\n)\nSELECT\n _base.`orders___status`,\n _cm_orders__customers__spend_weighted_avg_weight_rank_direction_desc_sum_amount_by_customers_regions_name.`customers___spend_weighted_avg_weight_rank_direction_desc_sum_orders_amount_by_regions_name` AS `orders___w`\nFROM _base\nCROSS JOIN _cm_orders__customers__spend_weighted_avg_weight_rank_direction_desc_sum_amount_by_customers_regions_name", + "param/ranked_transform::duckdb": "WITH _base AS (\n SELECT\n orders.status AS \"orders.status\"\n FROM orders AS orders\n GROUP BY\n orders.status\n), _base_2 AS (\n SELECT\n regions.name AS \"customers.regions.name\"\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n GROUP BY\n regions.name\n), _cm_customers__orders__amount_sum_partition_by_regions_name AS (\n SELECT\n customers__regions.name AS \"orders.customers.regions.name\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_customers_regions_name\"\n FROM orders AS orders\n LEFT JOIN customers AS customers\n ON orders.customer_id = customers.id\n LEFT JOIN regions AS customers__regions\n ON customers.region_id = customers__regions.id\n GROUP BY\n customers__regions.name\n), base_2 AS (\n SELECT\n _base_2.\"customers.regions.name\",\n _cm_customers__orders__amount_sum_partition_by_regions_name.\"orders.amount_sum_partition_by_customers_regions_name\" AS \"orders.amount_sum_partition_by_customers_regions_name\"\n FROM _base_2\n LEFT JOIN _cm_customers__orders__amount_sum_partition_by_regions_name\n ON _base_2.\"customers.regions.name\" IS NOT DISTINCT FROM _cm_customers__orders__amount_sum_partition_by_regions_name.\"orders.customers.regions.name\"\n), step1 AS (\n SELECT\n \"customers.regions.name\",\n \"orders.amount_sum_partition_by_customers_regions_name\",\n CASE\n WHEN \"orders.amount_sum_partition_by_customers_regions_name\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_sum_partition_by_customers_regions_name\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_partition_by_customers_regions_name\" DESC\n )\n END AS \"customers.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"customers.regions.name\" AS \"regions__name\",\n _stage_inner.\"customers.rank\" AS \"rank\"\n FROM (\n SELECT\n \"customers.regions.name\",\n \"customers.rank\"\n FROM (\n SELECT\n \"customers.regions.name\",\n \"orders.amount_sum_partition_by_customers_regions_name\",\n \"customers.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_orders__customers__spend_weighted_avg_weight_rank_direction_desc_sum_amount_by_customers_regions_name AS (\n SELECT\n CAST(SUM(customers.spend * _cm_rank.\"rank\") / NULLIF(SUM(_cm_rank.\"rank\"), 0) AS DOUBLE) AS \"customers.spend_weighted_avg_weight_rank_direction_desc_sum_orders_amount_by_regions_name\"\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n LEFT JOIN _cm_rank\n ON regions.name IS NOT DISTINCT FROM _cm_rank.\"regions__name\"\n)\nSELECT\n _base.\"orders.status\",\n _cm_orders__customers__spend_weighted_avg_weight_rank_direction_desc_sum_amount_by_customers_regions_name.\"customers.spend_weighted_avg_weight_rank_direction_desc_sum_orders_amount_by_regions_name\" AS \"orders.w\"\nFROM _base\nCROSS JOIN _cm_orders__customers__spend_weighted_avg_weight_rank_direction_desc_sum_amount_by_customers_regions_name", + "param/ranked_transform::mysql": "WITH _base AS (\n SELECT\n orders.status AS `orders.status`\n FROM orders AS orders\n GROUP BY\n orders.status\n), _base_2 AS (\n SELECT\n regions.name AS `customers.regions.name`\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n GROUP BY\n regions.name\n), _cm_customers__orders__amount_sum_partition_by_regions_name AS (\n SELECT\n customers__regions.name AS `orders.customers.regions.name`,\n CAST(SUM(orders.amount) AS DOUBLE) AS `orders.amount_sum_partition_by_customers_regions_name`\n FROM orders AS orders\n LEFT JOIN customers AS customers\n ON orders.customer_id = customers.id\n LEFT JOIN regions AS customers__regions\n ON customers.region_id = customers__regions.id\n GROUP BY\n customers__regions.name\n), base_2 AS (\n SELECT\n _base_2.`customers.regions.name`,\n _cm_customers__orders__amount_sum_partition_by_regions_name.`orders.amount_sum_partition_by_customers_regions_name` AS `orders.amount_sum_partition_by_customers_regions_name`\n FROM _base_2\n LEFT JOIN _cm_customers__orders__amount_sum_partition_by_regions_name\n ON _base_2.`customers.regions.name` <=> _cm_customers__orders__amount_sum_partition_by_regions_name.`orders.customers.regions.name`\n), step1 AS (\n SELECT\n `customers.regions.name`,\n `orders.amount_sum_partition_by_customers_regions_name`,\n CASE\n WHEN `orders.amount_sum_partition_by_customers_regions_name` IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN `orders.amount_sum_partition_by_customers_regions_name` IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY `orders.amount_sum_partition_by_customers_regions_name` DESC\n )\n END AS `customers.rank`\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.`customers.regions.name` AS `regions__name`,\n _stage_inner.`customers.rank` AS `rank`\n FROM (\n SELECT\n `customers.regions.name`,\n `customers.rank`\n FROM (\n SELECT\n `customers.regions.name`,\n `orders.amount_sum_partition_by_customers_regions_name`,\n `customers.rank`\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_orders__customers__spen_646bf81c_t_by_customers_regions_name AS (\n SELECT\n CAST(SUM(customers.spend * _cm_rank.`rank`) / NULLIF(SUM(_cm_rank.`rank`), 0) AS DOUBLE) AS `customers.spend_weighted_av_e745c4aa_ders_amount_by_regions_name`\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n LEFT JOIN _cm_rank\n ON regions.name <=> _cm_rank.`regions__name`\n)\nSELECT\n _base.`orders.status`,\n _cm_orders__customers__spen_646bf81c_t_by_customers_regions_name.`customers.spend_weighted_av_e745c4aa_ders_amount_by_regions_name` AS `orders.w`\nFROM _base\nCROSS JOIN _cm_orders__customers__spen_646bf81c_t_by_customers_regions_name", + "param/ranked_transform::postgres": "WITH _base AS (\n SELECT\n orders.status AS \"orders.status\"\n FROM orders AS orders\n GROUP BY\n orders.status\n), _base_2 AS (\n SELECT\n regions.name AS \"customers.regions.name\"\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n GROUP BY\n regions.name\n), _cm_customers__orders__amount_sum_partition_by_regions_name AS (\n SELECT\n customers__regions.name AS \"orders.customers.regions.name\",\n CAST(SUM(orders.amount) AS DOUBLE PRECISION) AS \"orders.amount_sum_partition_by_customers_regions_name\"\n FROM orders AS orders\n LEFT JOIN customers AS customers\n ON orders.customer_id = customers.id\n LEFT JOIN regions AS customers__regions\n ON customers.region_id = customers__regions.id\n GROUP BY\n customers__regions.name\n), base_2 AS (\n SELECT\n _base_2.\"customers.regions.name\",\n _cm_customers__orders__amount_sum_partition_by_regions_name.\"orders.amount_sum_partition_by_customers_regions_name\" AS \"orders.amount_sum_partition_by_customers_regions_name\"\n FROM _base_2\n LEFT JOIN _cm_customers__orders__amount_sum_partition_by_regions_name\n ON _base_2.\"customers.regions.name\" IS NOT DISTINCT FROM _cm_customers__orders__amount_sum_partition_by_regions_name.\"orders.customers.regions.name\"\n), step1 AS (\n SELECT\n \"customers.regions.name\",\n \"orders.amount_sum_partition_by_customers_regions_name\",\n CASE\n WHEN \"orders.amount_sum_partition_by_customers_regions_name\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_sum_partition_by_customers_regions_name\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_partition_by_customers_regions_name\" DESC\n )\n END AS \"customers.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"customers.regions.name\" AS \"regions__name\",\n _stage_inner.\"customers.rank\" AS \"rank\"\n FROM (\n SELECT\n \"customers.regions.name\",\n \"customers.rank\"\n FROM (\n SELECT\n \"customers.regions.name\",\n \"orders.amount_sum_partition_by_customers_regions_name\",\n \"customers.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_orders__customers__spen_646bf81c_by_customers_regions_name AS (\n SELECT\n CAST(SUM(customers.spend * _cm_rank.\"rank\") / NULLIF(SUM(_cm_rank.\"rank\"), 0) AS DOUBLE PRECISION) AS \"customers.spend_weighted_av_e745c4aa_ers_amount_by_regions_name\"\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n LEFT JOIN _cm_rank\n ON regions.name IS NOT DISTINCT FROM _cm_rank.\"regions__name\"\n)\nSELECT\n _base.\"orders.status\",\n _cm_orders__customers__spen_646bf81c_by_customers_regions_name.\"customers.spend_weighted_av_e745c4aa_ers_amount_by_regions_name\" AS \"orders.w\"\nFROM _base\nCROSS JOIN _cm_orders__customers__spen_646bf81c_by_customers_regions_name", + "param/ranked_transform::snowflake": "WITH _base AS (\n SELECT\n orders.status AS \"orders.status\"\n FROM orders AS orders\n GROUP BY\n orders.status\n), _base_2 AS (\n SELECT\n regions.name AS \"customers.regions.name\"\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n GROUP BY\n regions.name\n), _cm_customers__orders__amount_sum_partition_by_regions_name AS (\n SELECT\n customers__regions.name AS \"orders.customers.regions.name\",\n CAST(SUM(orders.amount) AS DOUBLE) AS \"orders.amount_sum_partition_by_customers_regions_name\"\n FROM orders AS orders\n LEFT JOIN customers AS customers\n ON orders.customer_id = customers.id\n LEFT JOIN regions AS customers__regions\n ON customers.region_id = customers__regions.id\n GROUP BY\n customers__regions.name\n), base_2 AS (\n SELECT\n _base_2.\"customers.regions.name\",\n _cm_customers__orders__amount_sum_partition_by_regions_name.\"orders.amount_sum_partition_by_customers_regions_name\" AS \"orders.amount_sum_partition_by_customers_regions_name\"\n FROM _base_2\n LEFT JOIN _cm_customers__orders__amount_sum_partition_by_regions_name\n ON _base_2.\"customers.regions.name\" IS NOT DISTINCT FROM _cm_customers__orders__amount_sum_partition_by_regions_name.\"orders.customers.regions.name\"\n), step1 AS (\n SELECT\n \"customers.regions.name\",\n \"orders.amount_sum_partition_by_customers_regions_name\",\n CASE\n WHEN \"orders.amount_sum_partition_by_customers_regions_name\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_sum_partition_by_customers_regions_name\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_partition_by_customers_regions_name\" DESC\n )\n END AS \"customers.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"customers.regions.name\" AS \"regions__name\",\n _stage_inner.\"customers.rank\" AS \"rank\"\n FROM (\n SELECT\n \"customers.regions.name\",\n \"customers.rank\"\n FROM (\n SELECT\n \"customers.regions.name\",\n \"orders.amount_sum_partition_by_customers_regions_name\",\n \"customers.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_orders__customers__spend_weighted_avg_weight_rank_direction_desc_sum_amount_by_customers_regions_name AS (\n SELECT\n CAST(SUM(customers.spend * _cm_rank.\"rank\") / NULLIF(SUM(_cm_rank.\"rank\"), 0) AS DOUBLE) AS \"customers.spend_weighted_avg_weight_rank_direction_desc_sum_orders_amount_by_regions_name\"\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n LEFT JOIN _cm_rank\n ON regions.name IS NOT DISTINCT FROM _cm_rank.\"regions__name\"\n)\nSELECT\n _base.\"orders.status\",\n _cm_orders__customers__spend_weighted_avg_weight_rank_direction_desc_sum_amount_by_customers_regions_name.\"customers.spend_weighted_avg_weight_rank_direction_desc_sum_orders_amount_by_regions_name\" AS \"orders.w\"\nFROM _base\nCROSS JOIN _cm_orders__customers__spend_weighted_avg_weight_rank_direction_desc_sum_amount_by_customers_regions_name", + "param/ranked_transform::sqlite": "WITH _base AS (\n SELECT\n orders.status AS \"orders.status\"\n FROM orders AS orders\n GROUP BY\n orders.status\n), _base_2 AS (\n SELECT\n regions.name AS \"customers.regions.name\"\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n GROUP BY\n regions.name\n), _cm_customers__orders__amount_sum_partition_by_regions_name AS (\n SELECT\n customers__regions.name AS \"orders.customers.regions.name\",\n CAST(SUM(orders.amount) AS REAL) AS \"orders.amount_sum_partition_by_customers_regions_name\"\n FROM orders AS orders\n LEFT JOIN customers AS customers\n ON orders.customer_id = customers.id\n LEFT JOIN regions AS customers__regions\n ON customers.region_id = customers__regions.id\n GROUP BY\n customers__regions.name\n), base_2 AS (\n SELECT\n _base_2.\"customers.regions.name\",\n _cm_customers__orders__amount_sum_partition_by_regions_name.\"orders.amount_sum_partition_by_customers_regions_name\" AS \"orders.amount_sum_partition_by_customers_regions_name\"\n FROM _base_2\n LEFT JOIN _cm_customers__orders__amount_sum_partition_by_regions_name\n ON _base_2.\"customers.regions.name\" IS _cm_customers__orders__amount_sum_partition_by_regions_name.\"orders.customers.regions.name\"\n), step1 AS (\n SELECT\n \"customers.regions.name\",\n \"orders.amount_sum_partition_by_customers_regions_name\",\n CASE\n WHEN \"orders.amount_sum_partition_by_customers_regions_name\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN \"orders.amount_sum_partition_by_customers_regions_name\" IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY \"orders.amount_sum_partition_by_customers_regions_name\" DESC\n )\n END AS \"customers.rank\"\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.\"customers.regions.name\" AS \"regions__name\",\n _stage_inner.\"customers.rank\" AS \"rank\"\n FROM (\n SELECT\n \"customers.regions.name\",\n \"customers.rank\"\n FROM (\n SELECT\n \"customers.regions.name\",\n \"orders.amount_sum_partition_by_customers_regions_name\",\n \"customers.rank\"\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_orders__customers__spend_weighted_avg_weight_rank_direction_desc_sum_amount_by_customers_regions_name AS (\n SELECT\n CAST(SUM(customers.spend * _cm_rank.\"rank\") / NULLIF(SUM(_cm_rank.\"rank\"), 0) AS REAL) AS \"customers.spend_weighted_avg_weight_rank_direction_desc_sum_orders_amount_by_regions_name\"\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n LEFT JOIN _cm_rank\n ON regions.name IS _cm_rank.\"regions__name\"\n)\nSELECT\n _base.\"orders.status\",\n _cm_orders__customers__spend_weighted_avg_weight_rank_direction_desc_sum_amount_by_customers_regions_name.\"customers.spend_weighted_avg_weight_rank_direction_desc_sum_orders_amount_by_regions_name\" AS \"orders.w\"\nFROM _base\nCROSS JOIN _cm_orders__customers__spend_weighted_avg_weight_rank_direction_desc_sum_amount_by_customers_regions_name", + "param/ranked_transform::tsql": "WITH _base AS (\n SELECT\n orders.status AS [orders___status]\n FROM orders AS orders\n GROUP BY\n orders.status\n), _base_2 AS (\n SELECT\n regions.name AS [customers___regions___name]\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n GROUP BY\n regions.name\n), _cm_customers__orders__amount_sum_partition_by_regions_name AS (\n SELECT\n customers__regions.name AS [orders___customers___regions___name],\n CAST(SUM(orders.amount) AS FLOAT) AS [orders___amount_sum_partition_by_customers_regions_name]\n FROM orders AS orders\n LEFT JOIN customers AS customers\n ON orders.customer_id = customers.id\n LEFT JOIN regions AS customers__regions\n ON customers.region_id = customers__regions.id\n GROUP BY\n customers__regions.name\n), base_2 AS (\n SELECT\n _base_2.[customers___regions___name] AS [customers___regions___name],\n _cm_customers__orders__amount_sum_partition_by_regions_name.[orders___amount_sum_partition_by_customers_regions_name] AS [orders___amount_sum_partition_by_customers_regions_name]\n FROM _base_2\n LEFT JOIN _cm_customers__orders__amount_sum_partition_by_regions_name\n ON (\n _base_2.[customers___regions___name] = _cm_customers__orders__amount_sum_partition_by_regions_name.[orders___customers___regions___name]\n OR (\n _base_2.[customers___regions___name] IS NULL\n AND _cm_customers__orders__amount_sum_partition_by_regions_name.[orders___customers___regions___name] IS NULL\n )\n )\n), step1 AS (\n SELECT\n [customers___regions___name] AS [customers___regions___name],\n [orders___amount_sum_partition_by_customers_regions_name] AS [orders___amount_sum_partition_by_customers_regions_name],\n CASE\n WHEN [orders___amount_sum_partition_by_customers_regions_name] IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE\n WHEN [orders___amount_sum_partition_by_customers_regions_name] IS NULL\n THEN 1\n ELSE 0\n END\n ORDER BY [orders___amount_sum_partition_by_customers_regions_name] DESC\n )\n END AS [customers___rank]\n FROM base_2\n), _cm_rank AS (\n SELECT\n _stage_inner.[customers___regions___name] AS [regions__name],\n _stage_inner.[customers___rank] AS [rank]\n FROM (\n SELECT\n [customers___regions___name] AS [customers___regions___name],\n [customers___rank] AS [customers___rank]\n FROM (\n SELECT\n [customers___regions___name],\n [orders___amount_sum_partition_by_customers_regions_name],\n [customers___rank]\n FROM step1\n ) AS _outer\n ) AS _stage_inner\n), _cm_orders__customers__spend_weighted_avg_weight_rank_direction_desc_sum_amount_by_customers_regions_name AS (\n SELECT\n CAST(SUM(customers.spend * _cm_rank.[rank]) / NULLIF(SUM(_cm_rank.[rank]), 0) AS FLOAT) AS [customers___spend_weighted_avg_weight_rank_direction_desc_sum_orders_amount_by_regions_name]\n FROM customers AS customers\n LEFT JOIN regions AS regions\n ON customers.region_id = regions.id\n LEFT JOIN _cm_rank\n ON (\n regions.name = _cm_rank.[regions__name]\n OR (\n regions.name IS NULL AND _cm_rank.[regions__name] IS NULL\n )\n )\n)\nSELECT\n _base.[orders___status],\n _cm_orders__customers__spend_weighted_avg_weight_rank_direction_desc_sum_amount_by_customers_regions_name.[customers___spend_weighted_avg_weight_rank_direction_desc_sum_orders_amount_by_regions_name] AS [orders___w]\nFROM _base\nCROSS JOIN _cm_orders__customers__spend_weighted_avg_weight_rank_direction_desc_sum_amount_by_customers_regions_name", "param/ungrained_reagg::bigquery": "WITH _base AS (\n SELECT\n sales.region AS `sales___region`\n FROM sales AS sales\n GROUP BY\n sales.region\n), _base_2 AS (\n SELECT\n sales.city AS `sales___city`,\n sales.region AS `sales___region`,\n CAST(SUM(sales.amount) AS FLOAT64) AS `sales___amount_sum_partition_by_city_region`\n FROM sales AS sales\n GROUP BY\n sales.city,\n sales.region\n), _cm_sales__id_count_partition_by_region AS (\n SELECT\n sales.region AS `sales___region`,\n COUNT(sales.id) AS `sales___id_count_partition_by_region`\n FROM sales AS sales\n GROUP BY\n sales.region\n), _cm_amount_sum_partition_by_city_region AS (\n SELECT\n _stage_inner.`sales___city` AS `city`,\n _stage_inner.`sales___region` AS `region`,\n _stage_inner.`sales___amount_sum_partition_by_city_region` AS `amount_sum_partition_by_city_region`,\n _stage_inner.`sales___id_count_partition_by_region` AS `id_count_partition_by_region`\n FROM (\n SELECT\n _base_2.`sales___city`,\n _base_2.`sales___region`,\n _base_2.`sales___amount_sum_partition_by_city_region`,\n COALESCE(_cm_sales__id_count_partition_by_region.`sales___id_count_partition_by_region`, 0) AS `sales___id_count_partition_by_region`\n FROM _base_2\n LEFT JOIN _cm_sales__id_count_partition_by_region\n ON _base_2.`sales___region` IS NOT DISTINCT FROM _cm_sales__id_count_partition_by_region.`sales___region`\n ) AS _stage_inner\n), _cm_sales___wsum_weight_count_id AS (\n SELECT\n _base.`sales___region` AS `sales___region`,\n SUM(_base._v * _base._p0) AS `sales___w`\n FROM (\n SELECT\n sales.region AS `sales___region`,\n sales.city AS _ek0,\n sales.region AS _ek1,\n MAX(_cm_amount_sum_partition_by_city_region.`amount_sum_partition_by_city_region`) AS _v,\n MAX(\n COALESCE(_cm_amount_sum_partition_by_city_region.`id_count_partition_by_region`, 0)\n ) AS _p0\n FROM sales AS sales\n LEFT JOIN _cm_amount_sum_partition_by_city_region\n ON sales.city IS NOT DISTINCT FROM _cm_amount_sum_partition_by_city_region.`city`\n AND sales.region IS NOT DISTINCT FROM _cm_amount_sum_partition_by_city_region.`region`\n GROUP BY\n sales.region,\n sales.city,\n sales.region\n ) AS _base\n GROUP BY\n _base.`sales___region`\n)\nSELECT\n _base.`sales___region`,\n _cm_sales___wsum_weight_count_id.`sales___w`\nFROM _base\nLEFT JOIN _cm_sales___wsum_weight_count_id\n ON _base.`sales___region` IS NOT DISTINCT FROM _cm_sales___wsum_weight_count_id.`sales___region`", "param/ungrained_reagg::duckdb": "WITH _base AS (\n SELECT\n sales.region AS \"sales.region\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), _base_2 AS (\n SELECT\n sales.city AS \"sales.city\",\n sales.region AS \"sales.region\",\n CAST(SUM(sales.amount) AS DOUBLE) AS \"sales.amount_sum_partition_by_city_region\"\n FROM sales AS sales\n GROUP BY\n sales.city,\n sales.region\n), _cm_sales__id_count_partition_by_region AS (\n SELECT\n sales.region AS \"sales.region\",\n COUNT(sales.id) AS \"sales.id_count_partition_by_region\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), _cm_amount_sum_partition_by_city_region AS (\n SELECT\n _stage_inner.\"sales.city\" AS \"city\",\n _stage_inner.\"sales.region\" AS \"region\",\n _stage_inner.\"sales.amount_sum_partition_by_city_region\" AS \"amount_sum_partition_by_city_region\",\n _stage_inner.\"sales.id_count_partition_by_region\" AS \"id_count_partition_by_region\"\n FROM (\n SELECT\n _base_2.\"sales.city\",\n _base_2.\"sales.region\",\n _base_2.\"sales.amount_sum_partition_by_city_region\",\n COALESCE(_cm_sales__id_count_partition_by_region.\"sales.id_count_partition_by_region\", 0) AS \"sales.id_count_partition_by_region\"\n FROM _base_2\n LEFT JOIN _cm_sales__id_count_partition_by_region\n ON _base_2.\"sales.region\" IS NOT DISTINCT FROM _cm_sales__id_count_partition_by_region.\"sales.region\"\n ) AS _stage_inner\n), _cm_sales___wsum_weight_count_id AS (\n SELECT\n _base.\"sales.region\" AS \"sales.region\",\n SUM(_base._v * _base._p0) AS \"sales.w\"\n FROM (\n SELECT\n sales.region AS \"sales.region\",\n sales.city AS _ek0,\n sales.region AS _ek1,\n MAX(_cm_amount_sum_partition_by_city_region.\"amount_sum_partition_by_city_region\") AS _v,\n MAX(\n COALESCE(_cm_amount_sum_partition_by_city_region.\"id_count_partition_by_region\", 0)\n ) AS _p0\n FROM sales AS sales\n LEFT JOIN _cm_amount_sum_partition_by_city_region\n ON sales.city IS NOT DISTINCT FROM _cm_amount_sum_partition_by_city_region.\"city\"\n AND sales.region IS NOT DISTINCT FROM _cm_amount_sum_partition_by_city_region.\"region\"\n GROUP BY\n sales.region,\n sales.city,\n sales.region\n ) AS _base\n GROUP BY\n _base.\"sales.region\"\n)\nSELECT\n _base.\"sales.region\",\n _cm_sales___wsum_weight_count_id.\"sales.w\"\nFROM _base\nLEFT JOIN _cm_sales___wsum_weight_count_id\n ON _base.\"sales.region\" IS NOT DISTINCT FROM _cm_sales___wsum_weight_count_id.\"sales.region\"", "param/ungrained_reagg::mysql": "WITH _base AS (\n SELECT\n sales.region AS `sales.region`\n FROM sales AS sales\n GROUP BY\n sales.region\n), _base_2 AS (\n SELECT\n sales.city AS `sales.city`,\n sales.region AS `sales.region`,\n CAST(SUM(sales.amount) AS DOUBLE) AS `sales.amount_sum_partition_by_city_region`\n FROM sales AS sales\n GROUP BY\n sales.city,\n sales.region\n), _cm_sales__id_count_partition_by_region AS (\n SELECT\n sales.region AS `sales.region`,\n COUNT(sales.id) AS `sales.id_count_partition_by_region`\n FROM sales AS sales\n GROUP BY\n sales.region\n), _cm_amount_sum_partition_by_city_region AS (\n SELECT\n _stage_inner.`sales.city` AS `city`,\n _stage_inner.`sales.region` AS `region`,\n _stage_inner.`sales.amount_sum_partition_by_city_region` AS `amount_sum_partition_by_city_region`,\n _stage_inner.`sales.id_count_partition_by_region` AS `id_count_partition_by_region`\n FROM (\n SELECT\n _base_2.`sales.city`,\n _base_2.`sales.region`,\n _base_2.`sales.amount_sum_partition_by_city_region`,\n COALESCE(_cm_sales__id_count_partition_by_region.`sales.id_count_partition_by_region`, 0) AS `sales.id_count_partition_by_region`\n FROM _base_2\n LEFT JOIN _cm_sales__id_count_partition_by_region\n ON _base_2.`sales.region` <=> _cm_sales__id_count_partition_by_region.`sales.region`\n ) AS _stage_inner\n), _cm_sales___wsum_weight_count_id AS (\n SELECT\n _base.`sales.region` AS `sales.region`,\n SUM(_base._v * _base._p0) AS `sales.w`\n FROM (\n SELECT\n sales.region AS `sales.region`,\n sales.city AS _ek0,\n sales.region AS _ek1,\n MAX(_cm_amount_sum_partition_by_city_region.`amount_sum_partition_by_city_region`) AS _v,\n MAX(\n COALESCE(_cm_amount_sum_partition_by_city_region.`id_count_partition_by_region`, 0)\n ) AS _p0\n FROM sales AS sales\n LEFT JOIN _cm_amount_sum_partition_by_city_region\n ON sales.city <=> _cm_amount_sum_partition_by_city_region.`city`\n AND sales.region <=> _cm_amount_sum_partition_by_city_region.`region`\n GROUP BY\n sales.region,\n sales.city,\n sales.region\n ) AS _base\n GROUP BY\n _base.`sales.region`\n)\nSELECT\n _base.`sales.region`,\n _cm_sales___wsum_weight_count_id.`sales.w`\nFROM _base\nLEFT JOIN _cm_sales___wsum_weight_count_id\n ON _base.`sales.region` <=> _cm_sales___wsum_weight_count_id.`sales.region`", diff --git a/tests/golden/rank_direction_sql_baseline.json b/tests/golden/rank_direction_sql_baseline.json new file mode 100644 index 00000000..cb1acb3a --- /dev/null +++ b/tests/golden/rank_direction_sql_baseline.json @@ -0,0 +1,42 @@ +{ + "dense_rank/asc::bigquery": "WITH base AS (\n SELECT\n sales.region AS `sales___region`,\n SUM(sales.amount) AS `sales___amount_sum`\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n `sales___region`,\n `sales___amount_sum`,\n CASE\n WHEN `sales___amount_sum` IS NULL\n THEN NULL\n ELSE DENSE_RANK() OVER (\n PARTITION BY CASE WHEN `sales___amount_sum` IS NULL THEN 1 ELSE 0 END\n ORDER BY `sales___amount_sum`\n )\n END AS `sales___r`\n FROM base\n)\nSELECT\n `sales___region`,\n `sales___r`\nFROM (\n SELECT\n `sales___region`,\n `sales___amount_sum`,\n `sales___r`\n FROM step1\n) AS _outer", + "dense_rank/asc::duckdb": "WITH base AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n CASE\n WHEN \"sales.amount_sum\" IS NULL\n THEN NULL\n ELSE DENSE_RANK() OVER (\n PARTITION BY CASE WHEN \"sales.amount_sum\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum\"\n )\n END AS \"sales.r\"\n FROM base\n)\nSELECT\n \"sales.region\",\n \"sales.r\"\nFROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n \"sales.r\"\n FROM step1\n) AS _outer", + "dense_rank/asc::postgres": "WITH base AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n CASE\n WHEN \"sales.amount_sum\" IS NULL\n THEN NULL\n ELSE DENSE_RANK() OVER (\n PARTITION BY CASE WHEN \"sales.amount_sum\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum\"\n )\n END AS \"sales.r\"\n FROM base\n)\nSELECT\n \"sales.region\",\n \"sales.r\"\nFROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n \"sales.r\"\n FROM step1\n) AS _outer", + "dense_rank/asc::sqlite": "WITH base AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n CASE\n WHEN \"sales.amount_sum\" IS NULL\n THEN NULL\n ELSE DENSE_RANK() OVER (\n PARTITION BY CASE WHEN \"sales.amount_sum\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum\"\n )\n END AS \"sales.r\"\n FROM base\n)\nSELECT\n \"sales.region\",\n \"sales.r\"\nFROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n \"sales.r\"\n FROM step1\n) AS _outer", + "dense_rank/asc::tsql": "WITH base AS (\n SELECT\n sales.region AS [sales___region],\n SUM(sales.amount) AS [sales___amount_sum]\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n [sales___region] AS [sales___region],\n [sales___amount_sum] AS [sales___amount_sum],\n CASE\n WHEN [sales___amount_sum] IS NULL\n THEN NULL\n ELSE DENSE_RANK() OVER (\n PARTITION BY CASE WHEN [sales___amount_sum] IS NULL THEN 1 ELSE 0 END\n ORDER BY [sales___amount_sum]\n )\n END AS [sales___r]\n FROM base\n)\nSELECT\n [sales___region],\n [sales___r]\nFROM (\n SELECT\n [sales___region],\n [sales___amount_sum],\n [sales___r]\n FROM step1\n) AS _outer", + "dense_rank/desc::bigquery": "WITH base AS (\n SELECT\n sales.region AS `sales___region`,\n SUM(sales.amount) AS `sales___amount_sum`\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n `sales___region`,\n `sales___amount_sum`,\n CASE\n WHEN `sales___amount_sum` IS NULL\n THEN NULL\n ELSE DENSE_RANK() OVER (\n PARTITION BY CASE WHEN `sales___amount_sum` IS NULL THEN 1 ELSE 0 END\n ORDER BY `sales___amount_sum` DESC\n )\n END AS `sales___r`\n FROM base\n)\nSELECT\n `sales___region`,\n `sales___r`\nFROM (\n SELECT\n `sales___region`,\n `sales___amount_sum`,\n `sales___r`\n FROM step1\n) AS _outer", + "dense_rank/desc::duckdb": "WITH base AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n CASE\n WHEN \"sales.amount_sum\" IS NULL\n THEN NULL\n ELSE DENSE_RANK() OVER (\n PARTITION BY CASE WHEN \"sales.amount_sum\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum\" DESC\n )\n END AS \"sales.r\"\n FROM base\n)\nSELECT\n \"sales.region\",\n \"sales.r\"\nFROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n \"sales.r\"\n FROM step1\n) AS _outer", + "dense_rank/desc::postgres": "WITH base AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n CASE\n WHEN \"sales.amount_sum\" IS NULL\n THEN NULL\n ELSE DENSE_RANK() OVER (\n PARTITION BY CASE WHEN \"sales.amount_sum\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum\" DESC\n )\n END AS \"sales.r\"\n FROM base\n)\nSELECT\n \"sales.region\",\n \"sales.r\"\nFROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n \"sales.r\"\n FROM step1\n) AS _outer", + "dense_rank/desc::sqlite": "WITH base AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n CASE\n WHEN \"sales.amount_sum\" IS NULL\n THEN NULL\n ELSE DENSE_RANK() OVER (\n PARTITION BY CASE WHEN \"sales.amount_sum\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum\" DESC\n )\n END AS \"sales.r\"\n FROM base\n)\nSELECT\n \"sales.region\",\n \"sales.r\"\nFROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n \"sales.r\"\n FROM step1\n) AS _outer", + "dense_rank/desc::tsql": "WITH base AS (\n SELECT\n sales.region AS [sales___region],\n SUM(sales.amount) AS [sales___amount_sum]\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n [sales___region] AS [sales___region],\n [sales___amount_sum] AS [sales___amount_sum],\n CASE\n WHEN [sales___amount_sum] IS NULL\n THEN NULL\n ELSE DENSE_RANK() OVER (\n PARTITION BY CASE WHEN [sales___amount_sum] IS NULL THEN 1 ELSE 0 END\n ORDER BY [sales___amount_sum] DESC\n )\n END AS [sales___r]\n FROM base\n)\nSELECT\n [sales___region],\n [sales___r]\nFROM (\n SELECT\n [sales___region],\n [sales___amount_sum],\n [sales___r]\n FROM step1\n) AS _outer", + "ntile/n4::bigquery": "WITH base AS (\n SELECT\n sales.region AS `sales___region`,\n SUM(sales.amount) AS `sales___amount_sum`\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n `sales___region`,\n `sales___amount_sum`,\n CASE\n WHEN `sales___amount_sum` IS NULL\n THEN NULL\n ELSE NTILE(4) OVER (\n PARTITION BY CASE WHEN `sales___amount_sum` IS NULL THEN 1 ELSE 0 END\n ORDER BY `sales___amount_sum`\n )\n END AS `sales___r`\n FROM base\n)\nSELECT\n `sales___region`,\n `sales___r`\nFROM (\n SELECT\n `sales___region`,\n `sales___amount_sum`,\n `sales___r`\n FROM step1\n) AS _outer", + "ntile/n4::duckdb": "WITH base AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n CASE\n WHEN \"sales.amount_sum\" IS NULL\n THEN NULL\n ELSE NTILE(4) OVER (\n PARTITION BY CASE WHEN \"sales.amount_sum\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum\"\n )\n END AS \"sales.r\"\n FROM base\n)\nSELECT\n \"sales.region\",\n \"sales.r\"\nFROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n \"sales.r\"\n FROM step1\n) AS _outer", + "ntile/n4::postgres": "WITH base AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n CASE\n WHEN \"sales.amount_sum\" IS NULL\n THEN NULL\n ELSE NTILE(4) OVER (\n PARTITION BY CASE WHEN \"sales.amount_sum\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum\"\n )\n END AS \"sales.r\"\n FROM base\n)\nSELECT\n \"sales.region\",\n \"sales.r\"\nFROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n \"sales.r\"\n FROM step1\n) AS _outer", + "ntile/n4::sqlite": "WITH base AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n CASE\n WHEN \"sales.amount_sum\" IS NULL\n THEN NULL\n ELSE NTILE(4) OVER (\n PARTITION BY CASE WHEN \"sales.amount_sum\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum\"\n )\n END AS \"sales.r\"\n FROM base\n)\nSELECT\n \"sales.region\",\n \"sales.r\"\nFROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n \"sales.r\"\n FROM step1\n) AS _outer", + "ntile/n4::tsql": "WITH base AS (\n SELECT\n sales.region AS [sales___region],\n SUM(sales.amount) AS [sales___amount_sum]\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n [sales___region] AS [sales___region],\n [sales___amount_sum] AS [sales___amount_sum],\n CASE\n WHEN [sales___amount_sum] IS NULL\n THEN NULL\n ELSE NTILE(4) OVER (\n PARTITION BY CASE WHEN [sales___amount_sum] IS NULL THEN 1 ELSE 0 END\n ORDER BY [sales___amount_sum]\n )\n END AS [sales___r]\n FROM base\n)\nSELECT\n [sales___region],\n [sales___r]\nFROM (\n SELECT\n [sales___region],\n [sales___amount_sum],\n [sales___r]\n FROM step1\n) AS _outer", + "percent_rank/plain::bigquery": "WITH base AS (\n SELECT\n sales.region AS `sales___region`,\n SUM(sales.amount) AS `sales___amount_sum`\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n `sales___region`,\n `sales___amount_sum`,\n CASE\n WHEN `sales___amount_sum` IS NULL\n THEN NULL\n ELSE PERCENT_RANK() OVER (\n PARTITION BY CASE WHEN `sales___amount_sum` IS NULL THEN 1 ELSE 0 END\n ORDER BY `sales___amount_sum`\n )\n END AS `sales___r`\n FROM base\n)\nSELECT\n `sales___region`,\n `sales___r`\nFROM (\n SELECT\n `sales___region`,\n `sales___amount_sum`,\n `sales___r`\n FROM step1\n) AS _outer", + "percent_rank/plain::duckdb": "WITH base AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n CASE\n WHEN \"sales.amount_sum\" IS NULL\n THEN NULL\n ELSE PERCENT_RANK() OVER (\n PARTITION BY CASE WHEN \"sales.amount_sum\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum\"\n )\n END AS \"sales.r\"\n FROM base\n)\nSELECT\n \"sales.region\",\n \"sales.r\"\nFROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n \"sales.r\"\n FROM step1\n) AS _outer", + "percent_rank/plain::postgres": "WITH base AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n CASE\n WHEN \"sales.amount_sum\" IS NULL\n THEN NULL\n ELSE PERCENT_RANK() OVER (\n PARTITION BY CASE WHEN \"sales.amount_sum\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum\"\n )\n END AS \"sales.r\"\n FROM base\n)\nSELECT\n \"sales.region\",\n \"sales.r\"\nFROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n \"sales.r\"\n FROM step1\n) AS _outer", + "percent_rank/plain::sqlite": "WITH base AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n CASE\n WHEN \"sales.amount_sum\" IS NULL\n THEN NULL\n ELSE PERCENT_RANK() OVER (\n PARTITION BY CASE WHEN \"sales.amount_sum\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum\"\n )\n END AS \"sales.r\"\n FROM base\n)\nSELECT\n \"sales.region\",\n \"sales.r\"\nFROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n \"sales.r\"\n FROM step1\n) AS _outer", + "percent_rank/plain::tsql": "WITH base AS (\n SELECT\n sales.region AS [sales___region],\n SUM(sales.amount) AS [sales___amount_sum]\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n [sales___region] AS [sales___region],\n [sales___amount_sum] AS [sales___amount_sum],\n CASE\n WHEN [sales___amount_sum] IS NULL\n THEN NULL\n ELSE PERCENT_RANK() OVER (\n PARTITION BY CASE WHEN [sales___amount_sum] IS NULL THEN 1 ELSE 0 END\n ORDER BY [sales___amount_sum]\n )\n END AS [sales___r]\n FROM base\n)\nSELECT\n [sales___region],\n [sales___r]\nFROM (\n SELECT\n [sales___region],\n [sales___amount_sum],\n [sales___r]\n FROM step1\n) AS _outer", + "rank/asc::bigquery": "WITH base AS (\n SELECT\n sales.region AS `sales___region`,\n SUM(sales.amount) AS `sales___amount_sum`\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n `sales___region`,\n `sales___amount_sum`,\n CASE\n WHEN `sales___amount_sum` IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN `sales___amount_sum` IS NULL THEN 1 ELSE 0 END\n ORDER BY `sales___amount_sum`\n )\n END AS `sales___r`\n FROM base\n)\nSELECT\n `sales___region`,\n `sales___r`\nFROM (\n SELECT\n `sales___region`,\n `sales___amount_sum`,\n `sales___r`\n FROM step1\n) AS _outer", + "rank/asc::duckdb": "WITH base AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n CASE\n WHEN \"sales.amount_sum\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN \"sales.amount_sum\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum\"\n )\n END AS \"sales.r\"\n FROM base\n)\nSELECT\n \"sales.region\",\n \"sales.r\"\nFROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n \"sales.r\"\n FROM step1\n) AS _outer", + "rank/asc::postgres": "WITH base AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n CASE\n WHEN \"sales.amount_sum\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN \"sales.amount_sum\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum\"\n )\n END AS \"sales.r\"\n FROM base\n)\nSELECT\n \"sales.region\",\n \"sales.r\"\nFROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n \"sales.r\"\n FROM step1\n) AS _outer", + "rank/asc::sqlite": "WITH base AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n CASE\n WHEN \"sales.amount_sum\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN \"sales.amount_sum\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum\"\n )\n END AS \"sales.r\"\n FROM base\n)\nSELECT\n \"sales.region\",\n \"sales.r\"\nFROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n \"sales.r\"\n FROM step1\n) AS _outer", + "rank/asc::tsql": "WITH base AS (\n SELECT\n sales.region AS [sales___region],\n SUM(sales.amount) AS [sales___amount_sum]\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n [sales___region] AS [sales___region],\n [sales___amount_sum] AS [sales___amount_sum],\n CASE\n WHEN [sales___amount_sum] IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN [sales___amount_sum] IS NULL THEN 1 ELSE 0 END\n ORDER BY [sales___amount_sum]\n )\n END AS [sales___r]\n FROM base\n)\nSELECT\n [sales___region],\n [sales___r]\nFROM (\n SELECT\n [sales___region],\n [sales___amount_sum],\n [sales___r]\n FROM step1\n) AS _outer", + "rank/asc_partitioned::bigquery": "WITH base AS (\n SELECT\n sales.region AS `sales___region`,\n sales.city AS `sales___city`,\n SUM(sales.amount) AS `sales___amount_sum`\n FROM sales AS sales\n GROUP BY\n sales.region,\n sales.city\n), step1 AS (\n SELECT\n `sales___region`,\n `sales___city`,\n `sales___amount_sum`,\n CASE\n WHEN `sales___amount_sum` IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY `sales___region`, CASE WHEN `sales___amount_sum` IS NULL THEN 1 ELSE 0 END\n ORDER BY `sales___amount_sum`\n )\n END AS `sales___r`\n FROM base\n)\nSELECT\n `sales___region`,\n `sales___city`,\n `sales___r`\nFROM (\n SELECT\n `sales___region`,\n `sales___city`,\n `sales___amount_sum`,\n `sales___r`\n FROM step1\n) AS _outer", + "rank/asc_partitioned::duckdb": "WITH base AS (\n SELECT\n sales.region AS \"sales.region\",\n sales.city AS \"sales.city\",\n SUM(sales.amount) AS \"sales.amount_sum\"\n FROM sales AS sales\n GROUP BY\n sales.region,\n sales.city\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.city\",\n \"sales.amount_sum\",\n CASE\n WHEN \"sales.amount_sum\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY \"sales.region\", CASE WHEN \"sales.amount_sum\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum\"\n )\n END AS \"sales.r\"\n FROM base\n)\nSELECT\n \"sales.region\",\n \"sales.city\",\n \"sales.r\"\nFROM (\n SELECT\n \"sales.region\",\n \"sales.city\",\n \"sales.amount_sum\",\n \"sales.r\"\n FROM step1\n) AS _outer", + "rank/asc_partitioned::postgres": "WITH base AS (\n SELECT\n sales.region AS \"sales.region\",\n sales.city AS \"sales.city\",\n SUM(sales.amount) AS \"sales.amount_sum\"\n FROM sales AS sales\n GROUP BY\n sales.region,\n sales.city\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.city\",\n \"sales.amount_sum\",\n CASE\n WHEN \"sales.amount_sum\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY \"sales.region\", CASE WHEN \"sales.amount_sum\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum\"\n )\n END AS \"sales.r\"\n FROM base\n)\nSELECT\n \"sales.region\",\n \"sales.city\",\n \"sales.r\"\nFROM (\n SELECT\n \"sales.region\",\n \"sales.city\",\n \"sales.amount_sum\",\n \"sales.r\"\n FROM step1\n) AS _outer", + "rank/asc_partitioned::sqlite": "WITH base AS (\n SELECT\n sales.region AS \"sales.region\",\n sales.city AS \"sales.city\",\n SUM(sales.amount) AS \"sales.amount_sum\"\n FROM sales AS sales\n GROUP BY\n sales.region,\n sales.city\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.city\",\n \"sales.amount_sum\",\n CASE\n WHEN \"sales.amount_sum\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY \"sales.region\", CASE WHEN \"sales.amount_sum\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum\"\n )\n END AS \"sales.r\"\n FROM base\n)\nSELECT\n \"sales.region\",\n \"sales.city\",\n \"sales.r\"\nFROM (\n SELECT\n \"sales.region\",\n \"sales.city\",\n \"sales.amount_sum\",\n \"sales.r\"\n FROM step1\n) AS _outer", + "rank/asc_partitioned::tsql": "WITH base AS (\n SELECT\n sales.region AS [sales___region],\n sales.city AS [sales___city],\n SUM(sales.amount) AS [sales___amount_sum]\n FROM sales AS sales\n GROUP BY\n sales.region,\n sales.city\n), step1 AS (\n SELECT\n [sales___region] AS [sales___region],\n [sales___city] AS [sales___city],\n [sales___amount_sum] AS [sales___amount_sum],\n CASE\n WHEN [sales___amount_sum] IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY [sales___region], CASE WHEN [sales___amount_sum] IS NULL THEN 1 ELSE 0 END\n ORDER BY [sales___amount_sum]\n )\n END AS [sales___r]\n FROM base\n)\nSELECT\n [sales___region],\n [sales___city],\n [sales___r]\nFROM (\n SELECT\n [sales___region],\n [sales___city],\n [sales___amount_sum],\n [sales___r]\n FROM step1\n) AS _outer", + "rank/desc::bigquery": "WITH base AS (\n SELECT\n sales.region AS `sales___region`,\n SUM(sales.amount) AS `sales___amount_sum`\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n `sales___region`,\n `sales___amount_sum`,\n CASE\n WHEN `sales___amount_sum` IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN `sales___amount_sum` IS NULL THEN 1 ELSE 0 END\n ORDER BY `sales___amount_sum` DESC\n )\n END AS `sales___r`\n FROM base\n)\nSELECT\n `sales___region`,\n `sales___r`\nFROM (\n SELECT\n `sales___region`,\n `sales___amount_sum`,\n `sales___r`\n FROM step1\n) AS _outer", + "rank/desc::duckdb": "WITH base AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n CASE\n WHEN \"sales.amount_sum\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN \"sales.amount_sum\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum\" DESC\n )\n END AS \"sales.r\"\n FROM base\n)\nSELECT\n \"sales.region\",\n \"sales.r\"\nFROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n \"sales.r\"\n FROM step1\n) AS _outer", + "rank/desc::postgres": "WITH base AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n CASE\n WHEN \"sales.amount_sum\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN \"sales.amount_sum\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum\" DESC\n )\n END AS \"sales.r\"\n FROM base\n)\nSELECT\n \"sales.region\",\n \"sales.r\"\nFROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n \"sales.r\"\n FROM step1\n) AS _outer", + "rank/desc::sqlite": "WITH base AS (\n SELECT\n sales.region AS \"sales.region\",\n SUM(sales.amount) AS \"sales.amount_sum\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n CASE\n WHEN \"sales.amount_sum\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN \"sales.amount_sum\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.amount_sum\" DESC\n )\n END AS \"sales.r\"\n FROM base\n)\nSELECT\n \"sales.region\",\n \"sales.r\"\nFROM (\n SELECT\n \"sales.region\",\n \"sales.amount_sum\",\n \"sales.r\"\n FROM step1\n) AS _outer", + "rank/desc::tsql": "WITH base AS (\n SELECT\n sales.region AS [sales___region],\n SUM(sales.amount) AS [sales___amount_sum]\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n [sales___region] AS [sales___region],\n [sales___amount_sum] AS [sales___amount_sum],\n CASE\n WHEN [sales___amount_sum] IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN [sales___amount_sum] IS NULL THEN 1 ELSE 0 END\n ORDER BY [sales___amount_sum] DESC\n )\n END AS [sales___r]\n FROM base\n)\nSELECT\n [sales___region],\n [sales___r]\nFROM (\n SELECT\n [sales___region],\n [sales___amount_sum],\n [sales___r]\n FROM step1\n) AS _outer", + "rank/non_numeric::bigquery": "WITH base AS (\n SELECT\n sales.region AS `sales___region`,\n MIN(sales.city) AS `sales___city_min`\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n `sales___region`,\n `sales___city_min`,\n CASE\n WHEN `sales___city_min` IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN `sales___city_min` IS NULL THEN 1 ELSE 0 END\n ORDER BY `sales___city_min`\n )\n END AS `sales___r`\n FROM base\n)\nSELECT\n `sales___region`,\n `sales___r`\nFROM (\n SELECT\n `sales___region`,\n `sales___city_min`,\n `sales___r`\n FROM step1\n) AS _outer", + "rank/non_numeric::duckdb": "WITH base AS (\n SELECT\n sales.region AS \"sales.region\",\n MIN(sales.city) AS \"sales.city_min\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.city_min\",\n CASE\n WHEN \"sales.city_min\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN \"sales.city_min\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.city_min\"\n )\n END AS \"sales.r\"\n FROM base\n)\nSELECT\n \"sales.region\",\n \"sales.r\"\nFROM (\n SELECT\n \"sales.region\",\n \"sales.city_min\",\n \"sales.r\"\n FROM step1\n) AS _outer", + "rank/non_numeric::postgres": "WITH base AS (\n SELECT\n sales.region AS \"sales.region\",\n MIN(sales.city) AS \"sales.city_min\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.city_min\",\n CASE\n WHEN \"sales.city_min\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN \"sales.city_min\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.city_min\"\n )\n END AS \"sales.r\"\n FROM base\n)\nSELECT\n \"sales.region\",\n \"sales.r\"\nFROM (\n SELECT\n \"sales.region\",\n \"sales.city_min\",\n \"sales.r\"\n FROM step1\n) AS _outer", + "rank/non_numeric::sqlite": "WITH base AS (\n SELECT\n sales.region AS \"sales.region\",\n MIN(sales.city) AS \"sales.city_min\"\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n \"sales.region\",\n \"sales.city_min\",\n CASE\n WHEN \"sales.city_min\" IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN \"sales.city_min\" IS NULL THEN 1 ELSE 0 END\n ORDER BY \"sales.city_min\"\n )\n END AS \"sales.r\"\n FROM base\n)\nSELECT\n \"sales.region\",\n \"sales.r\"\nFROM (\n SELECT\n \"sales.region\",\n \"sales.city_min\",\n \"sales.r\"\n FROM step1\n) AS _outer", + "rank/non_numeric::tsql": "WITH base AS (\n SELECT\n sales.region AS [sales___region],\n MIN(sales.city) AS [sales___city_min]\n FROM sales AS sales\n GROUP BY\n sales.region\n), step1 AS (\n SELECT\n [sales___region] AS [sales___region],\n [sales___city_min] AS [sales___city_min],\n CASE\n WHEN [sales___city_min] IS NULL\n THEN NULL\n ELSE RANK() OVER (\n PARTITION BY CASE WHEN [sales___city_min] IS NULL THEN 1 ELSE 0 END\n ORDER BY [sales___city_min]\n )\n END AS [sales___r]\n FROM base\n)\nSELECT\n [sales___region],\n [sales___r]\nFROM (\n SELECT\n [sales___region],\n [sales___city_min],\n [sales___r]\n FROM step1\n) AS _outer" +} diff --git a/tests/test_dev1934_save_time_formula.py b/tests/test_dev1934_save_time_formula.py index 754d7ace..11c0a3c4 100644 --- a/tests/test_dev1934_save_time_formula.py +++ b/tests/test_dev1934_save_time_formula.py @@ -389,7 +389,7 @@ def test_blank_builtin_formula_and_param_defaults_become_absent(self) -> None: (agg,) = model.aggregations assert agg.formula is None assert [p.name for p in agg.params] == ["w2"] - assert model.version == 12 + assert model.version == 13 assert raw["aggregations"][0]["formula"] == " " assert len(raw["aggregations"][0]["params"]) == 2 diff --git a/tests/test_memories_storage.py b/tests/test_memories_storage.py index cf2b63be..c0b72921 100644 --- a/tests/test_memories_storage.py +++ b/tests/test_memories_storage.py @@ -69,7 +69,7 @@ async def test_save_returns_memory_with_str_id( assert memory.learning.startswith("orders.is_returned") assert memory.entities == ["mydb.orders.is_returned"] assert memory.query is None - assert memory.version == 2 + assert memory.version == 3 assert memory.created_at is not None async def test_save_with_query_persists_query( diff --git a/tests/test_memory_string_ids.py b/tests/test_memory_string_ids.py index c341bfc7..11c63059 100644 --- a/tests/test_memory_string_ids.py +++ b/tests/test_memory_string_ids.py @@ -244,7 +244,7 @@ def test_v1_int_id_stringified(self) -> None: } m = Memory.model_validate(v1) assert m.id == "42" - assert m.version == 2 + assert m.version == 3 def test_v1_no_version_assumed_v1(self) -> None: # No version field → treated as v1; migrator stringifies. @@ -255,7 +255,7 @@ def test_v1_no_version_assumed_v1(self) -> None: } m = Memory.model_validate(legacy) assert m.id == "7" - assert m.version == 2 + assert m.version == 3 async def test_v2_save_round_trip( self, storage: StorageBackend, @@ -266,7 +266,7 @@ async def test_v2_save_round_trip( ) loaded = await storage.get_memory(m.id) assert loaded.id == "kb.policy" - assert loaded.version == 2 + assert loaded.version == 3 def test_duplicate_int_string_rows_same_content_normalized(self) -> None: """The v2 migrator deduplicates rows that exist under both int and diff --git a/tests/test_rank_direction_migration.py b/tests/test_rank_direction_migration.py index b0b9d72d..205ff38c 100644 --- a/tests/test_rank_direction_migration.py +++ b/tests/test_rank_direction_migration.py @@ -247,6 +247,13 @@ def test_fresh_or_current_model_untouched(self, version): model = SlayerModel.model_validate(_model_dict(measures=[{"name": "x", "formula": BARE}], version=v)) assert model.measures[0].formula == BARE + def test_fresh_query_backed_model_untouched(self): + data = _model_dict(measures=[], version=None, source_queries=[_query_dict(version=None)]) + data.pop("sql_table", None) + data.pop("columns", None) + [q] = SlayerModel.model_validate(data).source_queries or [] + assert (q.measures or [])[0].formula == "rank(sum(amount)) + 1" + def test_fresh_memory_untouched(self): mem = Memory.model_validate({"id": "m1", "learning": "x", "query": _query_dict(version=None)}) assert mem.query is not None diff --git a/tests/test_sqlite_memories_pk_migration.py b/tests/test_sqlite_memories_pk_migration.py index f6685f17..c036c96d 100644 --- a/tests/test_sqlite_memories_pk_migration.py +++ b/tests/test_sqlite_memories_pk_migration.py @@ -180,7 +180,7 @@ async def test_legacy_int_rows_round_trip_through_v2_load( assert rows[0].id == "1" assert rows[0].learning == "legacy" assert rows[0].entities == ["mydb.orders"] - assert rows[0].version == 2 + assert rows[0].version == 3 @pytest.fixture(autouse=True) From 9ec5a46f950dac872f2947bc2fc50a8c9ed0153c Mon Sep 17 00:00:00 2001 From: Egor Kraev Date: Sat, 3 Oct 2026 16:14:01 +0200 Subject: [PATCH 04/11] Rank direction: agent-facing text, docs, examples and notebooks; typed test call sites --- .basedpyright/baseline.json | 120 ------------------ docs/concepts/formulas.md | 40 +++--- docs/concepts/models.md | 2 +- docs/concepts/queries.md | 12 +- docs/concepts/references.md | 4 +- .../models/jaffle_shop/customers.yaml | 2 +- .../models/jaffle_shop/items.yaml | 2 +- .../models/jaffle_shop/orders.yaml | 2 +- .../models/jaffle_shop/products.yaml | 2 +- .../models/jaffle_shop/stores.yaml | 2 +- .../models/jaffle_shop/supplies.yaml | 2 +- .../models/jaffle_shop/tweets.yaml | 2 +- docs/examples/15_duckdb/duckdb.md | 2 +- docs/examples/15_duckdb/duckdb_cli_nb.ipynb | 115 ++++++++++------- .../examples/15_duckdb/duckdb_python_nb.ipynb | 75 ++++++----- examples/comparisons/matrix.json | 2 +- examples/comparisons/matrix.yaml | 2 +- examples/comparisons/probes.yaml | 22 ++-- examples/embedded/run.py | 2 +- examples/embedded/verify.py | 2 +- .../tasks.md | 10 +- slayer/core/formula.py | 2 +- slayer/core/keys.py | 4 +- slayer/mcp/server.py | 9 +- slayer/sql/generator.py | 2 +- slayer/sql/window_detect.py | 2 +- ..._dev1733_order_only_transform_composite.py | 22 ++-- tests/test_dev1832_transform_source.py | 6 +- tests/test_dev1953_partition_alias.py | 2 +- tests/test_memory_string_ids.py | 5 +- tests/test_query_backed_typed_expansion.py | 2 +- tests/test_rank_direction.py | 22 +++- tests/test_rank_direction_migration.py | 12 +- tests/test_sql_generator.py | 3 +- tests/test_transforms_planner.py | 4 +- 35 files changed, 231 insertions(+), 290 deletions(-) diff --git a/.basedpyright/baseline.json b/.basedpyright/baseline.json index c60287b1..819ff8ac 100644 --- a/.basedpyright/baseline.json +++ b/.basedpyright/baseline.json @@ -14287,14 +14287,6 @@ "lineCount": 1 } }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 36, - "endColumn": 54, - "lineCount": 1 - } - }, { "code": "reportArgumentType", "range": { @@ -14311,30 +14303,6 @@ "lineCount": 1 } }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 36, - "endColumn": 54, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 36, - "endColumn": 54, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 36, - "endColumn": 54, - "lineCount": 1 - } - }, { "code": "reportArgumentType", "range": { @@ -14431,22 +14399,6 @@ "lineCount": 1 } }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 33, - "endColumn": 51, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 33, - "endColumn": 48, - "lineCount": 1 - } - }, { "code": "reportArgumentType", "range": { @@ -14631,14 +14583,6 @@ "lineCount": 1 } }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 36, - "endColumn": 54, - "lineCount": 1 - } - }, { "code": "reportArgumentType", "range": { @@ -14647,14 +14591,6 @@ "lineCount": 1 } }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 36, - "endColumn": 54, - "lineCount": 1 - } - }, { "code": "reportArgumentType", "range": { @@ -14703,14 +14639,6 @@ "lineCount": 1 } }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 36, - "endColumn": 54, - "lineCount": 1 - } - }, { "code": "reportArgumentType", "range": { @@ -14727,14 +14655,6 @@ "lineCount": 1 } }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 36, - "endColumn": 51, - "lineCount": 1 - } - }, { "code": "reportArgumentType", "range": { @@ -14766,14 +14686,6 @@ "endColumn": 48, "lineCount": 1 } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 36, - "endColumn": 54, - "lineCount": 1 - } } ], "./tests/test_dev1739_guards.py": [ @@ -30015,14 +29927,6 @@ "lineCount": 1 } }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 25, - "endColumn": 81, - "lineCount": 1 - } - }, { "code": "reportArgumentType", "range": { @@ -33153,22 +33057,6 @@ "lineCount": 1 } }, - { - "code": "reportPrivateImportUsage", - "range": { - "startColumn": 28, - "endColumn": 31, - "lineCount": 1 - } - }, - { - "code": "reportPrivateImportUsage", - "range": { - "startColumn": 46, - "endColumn": 49, - "lineCount": 1 - } - }, { "code": "reportArgumentType", "range": { @@ -37685,14 +37573,6 @@ "lineCount": 1 } }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 21, - "endColumn": 54, - "lineCount": 1 - } - }, { "code": "reportArgumentType", "range": { diff --git a/docs/concepts/formulas.md b/docs/concepts/formulas.md index fc496aba..2b10c94c 100644 --- a/docs/concepts/formulas.md +++ b/docs/concepts/formulas.md @@ -55,7 +55,7 @@ same key). Very long expressions fold to a stable-hash key. An explicit keys collide (`sum(amount - cost)` and `sum(amount + cost)`) fail with a duplicate-key error asking for a rename. -Expression sources also accept joined-model refs (`sum(amount - customers.discount)`, homed at the deepest dataset that determines every operand — see [cross-model measures](queries.md#cross-model-measures)), operands whose column carries a `filter` (which masks that operand's value), and nested transforms (`sum(cumsum(sum(amount, partition_by=[region, ordered_at])) - 1)`, aggregated over the transform's own cells). A re-aggregation constituent mixed with a row-level column (`sum(amount * last(X))`) executes at its own grain and broadcasts per partition onto the rows, and one query may select the same re-aggregation both on its own and inside a mixed source; a windowed inner under a transform constituent (`sum(rank(sum(revenue, window='90d', partition_by=region)))`) is a second-order aggregation over the operand cells (COMBINED phase, not a row broadcast); a target-homed inner whose `partition_by=` names the host's time axis stays a typed error. +Expression sources also accept joined-model refs (`sum(amount - customers.discount)`, homed at the deepest dataset that determines every operand — see [cross-model measures](queries.md#cross-model-measures)), operands whose column carries a `filter` (which masks that operand's value), and nested transforms (`sum(cumsum(sum(amount, partition_by=[region, ordered_at])) - 1)`, aggregated over the transform's own cells). A re-aggregation constituent mixed with a row-level column (`sum(amount * last(X))`) executes at its own grain and broadcasts per partition onto the rows, and one query may select the same re-aggregation both on its own and inside a mixed source; a windowed inner under a transform constituent (`sum(rank(sum(revenue, window='90d', partition_by=region), direction='desc'))`) is a second-order aggregation over the operand cells (COMBINED phase, not a row broadcast); a target-homed inner whose `partition_by=` names the host's time axis stays a typed error. A source mixing row-level columns with attached values (`sum(quantity * avg(price, partition_by=product))`) is a row-grain aggregation @@ -63,7 +63,7 @@ A source mixing row-level columns with attached values fully-attached source is a [re-aggregation](#re-aggregation-aggregate-over-an-attached-value). An attached value — an aggregate or a grained transform -(`weight=rank(sum(amount, partition_by=region))`) — may also arrive as a +(`weight=rank(sum(amount, partition_by=region), direction='desc')`) — may also arrive as a *parameter* of a row-level aggregation (`weighted_avg(amount, weight=sum(amount, partition_by=region))`): it is attached into the input relation, so each row is weighted by its cell's value. @@ -310,10 +310,10 @@ Functions apply window operations to measures: | `change(x)` | Period-over-period difference (partition-safe, resets per group) | Desugars to `x - time_shift(x, -1)` | | `change_pct(x)` | Period-over-period % change, e.g. month-over-month growth (partition-safe, resets per group; NULL when the prior period's value is 0 or missing) | Desugars to `CASE WHEN ts != 0 THEN (x - ts) / ts END` where `ts = time_shift(x, -1)` | | `consecutive_periods(predicate)` | Current trailing run length where predicate is true | Staged window CTEs with reset groups | -| `rank(x[, partition_by=...])` | Ranking by value (descending) | `RANK() OVER ([PARTITION BY ...] ORDER BY x DESC)` | -| `percent_rank(x[, partition_by=...])` | Relative rank in [0, 1] (descending) | `PERCENT_RANK() OVER ([PARTITION BY ...] ORDER BY x DESC)` | -| `dense_rank(x[, partition_by=...])` | Ranking with no gaps after ties (descending) | `DENSE_RANK() OVER ([PARTITION BY ...] ORDER BY x DESC)` | -| `ntile(x, n=N[, partition_by=...])` | Bucket the rows into N equal groups (descending) | `NTILE(N) OVER ([PARTITION BY ...] ORDER BY x DESC)` | +| `rank(x, direction='asc'\|'desc'[, partition_by=...])` | Ranking by value in the required direction | `RANK() OVER ([PARTITION BY ...] ORDER BY x ASC\|DESC)` | +| `percent_rank(x[, partition_by=...])` | Relative rank in [0, 1] (ascending) | `PERCENT_RANK() OVER ([PARTITION BY ...] ORDER BY x ASC)` | +| `dense_rank(x, direction='asc'\|'desc'[, partition_by=...])` | Ranking with no gaps after ties, in the required direction | `DENSE_RANK() OVER ([PARTITION BY ...] ORDER BY x ASC\|DESC)` | +| `ntile(x, n=N[, partition_by=...])` | Bucket the rows into N equal groups (ascending) | `NTILE(N) OVER ([PARTITION BY ...] ORDER BY x ASC)` | | `first(x)` | Earliest time bucket's value | `FIRST_VALUE(x) OVER (ORDER BY time ASC ...)` | | `last(x)` | Most recent time bucket's value | `FIRST_VALUE(x) OVER (ORDER BY time DESC ...)` | @@ -392,7 +392,9 @@ Use `show_sql=True` on the query to see what SQL is generated for complex formul ### Rank-family transforms -The rank family — `rank`, `percent_rank`, `dense_rank`, `ntile` — are timeless window-function transforms that order rows by the inner measure descending and emit a per-row rank value. They do not need a time dimension and, unlike the time-ordered transforms (`cumsum`, `lag`, `lead`, `first`, `last`, …), they default to **no `PARTITION BY`** — every row in the result set is ranked against every other row. +The rank family — `rank`, `percent_rank`, `dense_rank`, `ntile` — are timeless window-function transforms that order rows by the inner value and emit a per-row rank value. They do not need a time dimension and, unlike the time-ordered transforms (`cumsum`, `lag`, `lead`, `first`, `last`, …), they default to **no `PARTITION BY`** — every row in the result set is ranked against every other row. + +`rank` and `dense_rank` take a **required** `direction=` keyword: `direction='desc'` ranks the highest value 1, `direction='asc'` the lowest (`ascending` / `descending` and any case are accepted too). Leaving it out is an error that shows both spellings. The inner value may be any orderable type, so `rank(min(created_at), direction='asc')` ranks the earliest first. `ntile` and `percent_rank` take no `direction=` and always order ascending: bucket `1` holds the lowest values and the lowest value has `percent_rank` `0`. ```json { @@ -400,24 +402,30 @@ The rank family — `rank`, `percent_rank`, `dense_rank`, `ntile` — are timele "dimensions": ["customer_name"], "measures": [ "sum(revenue)", - {"formula": "rank(sum(revenue))", "name": "rnk"} + {"formula": "rank(sum(revenue), direction='desc')", "name": "rnk"} ], "order": [{"column": "sum(revenue)", "direction": "desc"}] } ``` -Combine with a filter to get "top N": +Combine with a filter to get "top N" (or `direction='asc'` for "bottom N"): ```json -{"filters": ["rank(sum(revenue)) <= 10"]} +{"filters": ["rank(sum(revenue), direction='desc') <= 10"]} ``` +A row whose inner value is NULL gets a NULL result from all four, takes no rank position or `ntile` bucket, and does not count in `percent_rank`'s denominator; the other rows rank as if it were absent, on every database. A `rank(...) <= N` filter therefore drops NULL-valued rows. + +An unnamed rank measure's result key spells its direction as a bare value: `rank(sum(revenue), direction='desc')` is returned as `orders.rank_revenue_sum_desc`. + **Choosing between the four:** -- `rank(x)` — ties share a rank, then the next rank is skipped (`1, 1, 3, 4`). Use for top-N rows. -- `dense_rank(x)` — ties share a rank, no gaps after (`1, 1, 2, 3`). Use for "top N distinct values" / tier counting. -- `percent_rank(x)` — relative position in `[0, 1]` (`(rank - 1) / (count - 1)`). Use for normalized rankings comparable across queries with different result-set sizes. -- `ntile(x, n=N)` — bucket every row into one of `N` equal-sized groups (`1` is the top bucket; required `n=` kwarg is a positive integer). Use for quartiles / deciles. +- `rank(x, direction=...)` — ties share a rank, then the next rank is skipped (`1, 1, 3, 4`). Use for top-N or bottom-N rows. +- `dense_rank(x, direction=...)` — ties share a rank, no gaps after (`1, 1, 2, 3`). Use for "top N distinct values" / tier counting. +- `percent_rank(x)` — relative position in `[0, 1]` (`(rank - 1) / (count - 1)`), lowest value `0`. Use for normalized rankings comparable across queries with different result-set sizes. +- `ntile(x, n=N)` — bucket every row into one of `N` equal-sized groups (`1` is the lowest bucket; required `n=` kwarg is a positive integer). Use for quartiles / deciles. + +Models and queries saved before `direction=` existed load with `direction='desc'` filled in on every `rank` / `dense_rank` call, keeping their meaning; their stored `ntile` / `percent_rank` results now order ascending. **Ranking within a partition (`partition_by=`):** @@ -429,7 +437,7 @@ To rank within groups instead of across the whole result set, pass `partition_by "dimensions": ["region", "customer_name"], "measures": [ "sum(revenue)", - {"formula": "dense_rank(sum(revenue), partition_by=region)", "name": "rev_rank_within_region"}, + {"formula": "dense_rank(sum(revenue), partition_by=region, direction='desc')", "name": "rev_rank_within_region"}, {"formula": "ntile(sum(revenue), n=4, partition_by=region)", "name": "rev_quartile_within_region"} ] } @@ -437,7 +445,7 @@ To rank within groups instead of across the whole result set, pass `partition_by Multiple partition columns: `partition_by=[region, channel]`. Cross-model dotted paths work too: `partition_by=customers.region`. -The rank family's `partition_by=` may name a computed dimension, as an aggregation's can (one that itself wraps an aggregation is for now accepted only in filters and order). Each key must be a member of the transform's operand grain — the union of its inner aggregates' grains (an ungrained inner contributes the query's dimensions) — so `rank(sum(revenue, partition_by=[city, region]), partition_by=product)` is an error naming the remedy. +The rank family's `partition_by=` may name a computed dimension, as an aggregation's can (one that itself wraps an aggregation is for now accepted only in filters and order). Each key must be a member of the transform's operand grain — the union of its inner aggregates' grains (an ungrained inner contributes the query's dimensions) — so `rank(sum(revenue, partition_by=[city, region]), partition_by=product, direction='desc')` is an error naming the remedy. > **Note:** SLayer's formula parser is Python-AST-based and rejects raw `OVER (...)` SQL in `ModelMeasure.formula` and filter strings. Use the rank-family transforms (`rank`, `percent_rank`, `dense_rank`, `ntile`) for ranking instead of `row_number() over (...) <= N`. If you need a non-standard window expression, define it on a `Column.sql` (e.g., `{"name": "rn", "sql": "row_number() over (order by mass desc)", "type": "NUMBER"}`) and filter on the column — SLayer auto-promotes the predicate to a post-aggregation outer `WHERE`. diff --git a/docs/concepts/models.md b/docs/concepts/models.md index c995f229..af7b5de9 100644 --- a/docs/concepts/models.md +++ b/docs/concepts/models.md @@ -172,7 +172,7 @@ Cycles in the reference graph (e.g., `c1.sql = "c2 + 1"` and `c2.sql = "c1 - 1"` A column's `sql` may contain a window function (`row_number() over (...)`, `dense_rank() over (...)`, etc.). The column behaves like any other column when used in `dimensions` / SELECT. -> **Filtering on a windowed column is rejected.** A query filter naming a `Column` whose `sql` contains a window function (e.g. `{"filters": ["rn <= 3"]}` against a column whose `sql` is `row_number() over (...)`) raises with a clear message. Use `{"filters": ["rank() <= 3"]}` (see [formulas.md](formulas.md#rank-family-transforms)) — the rank-family transforms cover the top-N case in pure DSL — or factor the column into a multi-stage `source_queries` model. +> **Filtering on a windowed column is rejected.** A query filter naming a `Column` whose `sql` contains a window function (e.g. `{"filters": ["rn <= 3"]}` against a column whose `sql` is `row_number() over (...)`) raises with a clear message. Use `{"filters": ["rank(, direction='desc') <= 3"]}` (see [formulas.md](formulas.md#rank-family-transforms)) — the rank-family transforms cover the top-N case in pure DSL — or factor the column into a multi-stage `source_queries` model. ### SQL expression conventions diff --git a/docs/concepts/queries.md b/docs/concepts/queries.md index 20639487..1c375876 100644 --- a/docs/concepts/queries.md +++ b/docs/concepts/queries.md @@ -95,13 +95,13 @@ the computed dimension name (`band == 1`) applies after regrouping. A dimension expression may band a windowed partitioned aggregate (`sum(amount, window='90d', partition_by=region)`), a `first` / `last`, or a -transform over a grained aggregate — `rank(sum(revenue, partition_by=region))` as +transform over a grained aggregate — `rank(sum(revenue, partition_by=region), direction='desc')` as a DIMENSION ranks the partitions (it evaluates at the producer grain), whereas the same expression as a MEASURE ranks the result rows (query grain). An aggregation-derived dimension combines with transform measures: alongside `band` you can declare `time_shift(sum(amount), -1)`, `change` / `change_pct`, -`cumsum`, `lag` / `lead`, `consecutive_periods(...)`, or `rank(sum(amount))`, +`cumsum`, `lag` / `lead`, `consecutive_periods(...)`, or `rank(sum(amount), direction='desc')`, with or without plain and `partition_by=` measures in the same query. Every transform treats the computed dimension as an ordinary grouping dimension (a running total accumulates within each `(region, band)` group; a time shift @@ -109,7 +109,7 @@ compares each group only against itself). A transform over aggregates at **different** partition grains unions the grains and broadcasts each aggregate to the union — -`rank(sum(amount, partition_by=region) - sum(amount, partition_by=city))` ranks +`rank(sum(amount, partition_by=region) - sum(amount, partition_by=city), direction='desc')` ranks the `(region, city)` rows, each region total broadcast across its cities and each city total against its region. The same holds as a bare measure (`sum(a, partition_by=region) - sum(b, partition_by=city)` @@ -162,7 +162,7 @@ Emits roughly `SELECT orders.status, orders.amount FROM orders WHERE orders.amou - `measures` must be empty — `DistinctDimensionValuesError` otherwise. - At least one of `dimensions` / `time_dimensions` must be non-empty (nothing to project otherwise). -- Filters / order items must not reference any measure — neither an aggregation (`sum(amount) > 100`, `count(*) > 0`), transform calls (`rank(sum(amount)) <= 5`), nor a bare saved-`ModelMeasure` name. +- Filters / order items must not reference any measure — neither an aggregation (`sum(amount) > 100`, `count(*) > 0`), transform calls (`rank(sum(amount), direction='desc') <= 5`), nor a bare saved-`ModelMeasure` name. **Time dimensions** are allowed: each one emits its `DATE_TRUNC` truncation as a projected column without aggregating. For raw column values (no truncation), put the time column in `dimensions` instead. @@ -217,7 +217,7 @@ What each shape of an *undeclared* order target does: | Order target | Behavior | | --- | --- | | An aggregate (`sum(amount)`, `sum(customers.revenue)`) | Computed hidden, sorted on, stripped from the result. Always allowed. | -| An inline **transform** (`rank(sum(amount))`, `cumsum(...)`, `change(...)`, `lag`/`lead`/`ntile`) | Computed hidden, sorted on, stripped. | +| An inline **transform** (`rank(sum(amount), direction='desc')`, `cumsum(...)`, `change(...)`, `lag`/`lead`/`ntile`) | Computed hidden, sorted on, stripped. | | An inline **composite** (`sum(revenue) / sum(cnt)`, `abs(sum(amount))`, `change(sum(amount)) / 2`) | Computed hidden, sorted on, stripped. | | A **windowed** aggregate (`sum(amount, window='90d')`), alone or inside a composite | Computed hidden in its own rolling-window CTE, sorted on, stripped. | | A raw row column, in a **raw-rows** query (`distinct_dimension_values: false`, no measures) | Sorted on directly (`ORDER BY orders.created_at`). Applies to a **joined** column (`customers.regions.name`) and to a derived column whose `sql` reaches through a join — the join is pulled in for the sort. | @@ -438,7 +438,7 @@ Window functions (`OVER (...)`) are not allowed inside the inner WHERE on SQLite Use one of: -* `rank() <= N` (or `dense_rank` / `percent_rank` / `ntile(, n=N)`) for ranking — simpler and dialect-portable. Pass `partition_by=` to rank within groups. +* `rank(, direction='desc') <= N` (or `dense_rank` / `percent_rank` / `ntile(, n=N)`) for ranking — simpler and dialect-portable. Pass `partition_by=` to rank within groups. * `first(x)` / `last(x)` / `lag(x, n)` / `lead(x, n)` for time-based window transforms. * A multi-stage `source_queries` model where the window computation lives in an earlier stage. diff --git a/docs/concepts/references.md b/docs/concepts/references.md index f901bf93..0da76ceb 100644 --- a/docs/concepts/references.md +++ b/docs/concepts/references.md @@ -7,7 +7,7 @@ SLayer has two distinct expression layers and the rules for what each one accept | Mode | Fields | Parser | Accepts | Rejects | |---|---|---|---|---| | **A — SQL** | `Column.sql`, `Column.filter`, each entry of `SlayerModel.filters` | sqlglot | Any valid SQL expression for the underlying dialect — function calls (`json_extract`, `coalesce`, `nullif`, `lower`, `length`, …), arithmetic, `CASE WHEN`, string literals, comparison and boolean operators in SQL spelling (`=`, `<>`, `IS NULL`, `AND`, `OR`, `NOT`, `IN`, `LIKE`). Bare names and dotted join paths (`customers.regions.name`). | Aggregations (`sum(revenue)`); SLayer transform calls (`cumsum`, `change`, `rank`, …); references to `ModelMeasure` formulas; raw `OVER (...)` window functions inside `Column.filter` / `SlayerModel.filters` (allowed only in `Column.sql`); the legacy `__`-delimited split-alias qualifier (`customers__regions.name`) — now a hard error (`LegacyDunderAliasError`). | -| **B — DSL** | `ModelMeasure.formula`, `SlayerQuery.measures`, `SlayerQuery.filters`, `SlayerQuery.dimensions`, `SlayerQuery.time_dimensions`, `SlayerQuery.order`, `SlayerQuery.main_time_dimension` | Python AST formula parser | Bare names that resolve to a `Column` or `ModelMeasure` on the model; single-dot dotted paths through joins (`customers.regions.name`, `sum(customers.revenue)`); aggregations (`sum(revenue)`, `count(*)`, `percentile(price, p=0.9)`, parametric forms), including same-model expression sources (`sum(amount - cost)`); transform calls (`cumsum(sum(revenue))`, `rank(sum(revenue), partition_by=region)`); arithmetic / boolean / comparison operators; the SQL `\|\|` concat operator (folded into `concat(...)`); pattern matching via the `like(value, pattern)` scalar (emits the SQL `LIKE` operator — wrap in `not (...)` for `NOT LIKE`); a closed allowlist of scalar functions (matched case-insensitively) — null handling (`nullif`, `coalesce`, `ifnull`), math (`ln`, `log10`, `log2`, `log`, `exp`, `sqrt`, `pow`, `power`, `abs`, `floor`, `ceil`, `ceiling`, `round`, `sign`, `trunc`, `mod`), scalar min/max (`greatest`, `least`), string hygiene (`lower`, `upper`, `trim`, `ltrim`, `rtrim`, `replace`, `substr`, `substring`, `instr`, `length`, `concat`), `like`, and [date functions](#date-and-time-functions) (`date_part`, `date_diff`, `date_add`, `current_date`, `now`), each with a declared argument count that is validated (`coalesce`, `concat`, `greatest` and `least` are variadic; `trunc` takes exactly one argument); `{variable}` placeholders (filters only). | identifiers using the reserved `__slayer_` prefix; raw SQL function calls outside that allowlist (`json_extract`, `date_trunc`, …), and any allowlisted call with the wrong number of arguments; raw `OVER (...)`; bare names that don't resolve to a Column / ModelMeasure / custom aggregation / query alias; `NULL` inside an `in` / `not in` list (use `is null` / `is not null` instead — see below). | +| **B — DSL** | `ModelMeasure.formula`, `SlayerQuery.measures`, `SlayerQuery.filters`, `SlayerQuery.dimensions`, `SlayerQuery.time_dimensions`, `SlayerQuery.order`, `SlayerQuery.main_time_dimension` | Python AST formula parser | Bare names that resolve to a `Column` or `ModelMeasure` on the model; single-dot dotted paths through joins (`customers.regions.name`, `sum(customers.revenue)`); aggregations (`sum(revenue)`, `count(*)`, `percentile(price, p=0.9)`, parametric forms), including same-model expression sources (`sum(amount - cost)`); transform calls (`cumsum(sum(revenue))`, `rank(sum(revenue), partition_by=region, direction='desc')`); arithmetic / boolean / comparison operators; the SQL `\|\|` concat operator (folded into `concat(...)`); pattern matching via the `like(value, pattern)` scalar (emits the SQL `LIKE` operator — wrap in `not (...)` for `NOT LIKE`); a closed allowlist of scalar functions (matched case-insensitively) — null handling (`nullif`, `coalesce`, `ifnull`), math (`ln`, `log10`, `log2`, `log`, `exp`, `sqrt`, `pow`, `power`, `abs`, `floor`, `ceil`, `ceiling`, `round`, `sign`, `trunc`, `mod`), scalar min/max (`greatest`, `least`), string hygiene (`lower`, `upper`, `trim`, `ltrim`, `rtrim`, `replace`, `substr`, `substring`, `instr`, `length`, `concat`), `like`, and [date functions](#date-and-time-functions) (`date_part`, `date_diff`, `date_add`, `current_date`, `now`), each with a declared argument count that is validated (`coalesce`, `concat`, `greatest` and `least` are variadic; `trunc` takes exactly one argument); `{variable}` placeholders (filters only). | identifiers using the reserved `__slayer_` prefix; raw SQL function calls outside that allowlist (`json_extract`, `date_trunc`, …), and any allowlisted call with the wrong number of arguments; raw `OVER (...)`; bare names that don't resolve to a Column / ModelMeasure / custom aggregation / query alias; `NULL` inside an `in` / `not in` list (use `is null` / `is not null` instead — see below). | ## Identifier resolution @@ -29,7 +29,7 @@ SLayer has two distinct expression layers and the rules for what each one accept * A single-dot dotted path walks the join graph: `customers.regions.name` traverses `model → customers → regions` and resolves `name` on the regions model. Multi-hop is supported. Each segment resolves as an incident **edge name first, then a neighbour model name**, in **either direction** ([bidirectional traversal](models.md#bidirectional-traversal)); an ambiguous hop (parallel edges, no edge-name segment) fails closed naming the candidates. * A **short form** naming only the target model and column (`regions.name`) auto-routes to its full path when the target is reached by exactly one route — or, among several, exactly one fan-out-free route — surfacing under that full routed path; an ambiguous or unreachable target is rejected (`UnresolvableDimensionJoinError`) with a suggested path, and a broken explicit chain is never silently repaired. * Aggregations: `()` (e.g. `sum(revenue)`), `count(*)`, `(, )` (e.g. `weighted_avg(price, weight=quantity)`), and `()` for cross-model aggregations — see [Aggregation syntax](#aggregation-syntax). -* Transform calls wrap aggregated refs: `cumsum(sum(revenue))`, `rank(sum(revenue), partition_by=region)`, `change(sum(customers.revenue))`, etc. +* Transform calls wrap aggregated refs: `cumsum(sum(revenue))`, `rank(sum(revenue), partition_by=region, direction='desc')`, `change(sum(customers.revenue))`, etc. * A `__` token in a name is matched by **exact name**, not split into a join walk — write a single-dot DSL path (`customers.region`) for a join. Only the reserved `__slayer_` prefix is rejected. ## Aggregation syntax diff --git a/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/customers.yaml b/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/customers.yaml index 6d347cd9..3383c3ef 100644 --- a/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/customers.yaml +++ b/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/customers.yaml @@ -1,4 +1,4 @@ -version: 12 +version: 13 name: customers sql_table: customers source_kind: table diff --git a/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/items.yaml b/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/items.yaml index 3667b6fc..ee3e4347 100644 --- a/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/items.yaml +++ b/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/items.yaml @@ -1,4 +1,4 @@ -version: 12 +version: 13 name: items sql_table: items source_kind: table diff --git a/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/orders.yaml b/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/orders.yaml index 5324343e..37d301f6 100644 --- a/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/orders.yaml +++ b/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/orders.yaml @@ -1,4 +1,4 @@ -version: 12 +version: 13 name: orders sql_table: orders source_kind: table diff --git a/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/products.yaml b/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/products.yaml index 054e6d23..3d40dfba 100644 --- a/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/products.yaml +++ b/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/products.yaml @@ -1,4 +1,4 @@ -version: 12 +version: 13 name: products sql_table: products source_kind: table diff --git a/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/stores.yaml b/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/stores.yaml index 8020da53..b0de08aa 100644 --- a/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/stores.yaml +++ b/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/stores.yaml @@ -1,4 +1,4 @@ -version: 12 +version: 13 name: stores sql_table: stores source_kind: table diff --git a/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/supplies.yaml b/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/supplies.yaml index 0149d408..0db1d41f 100644 --- a/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/supplies.yaml +++ b/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/supplies.yaml @@ -1,4 +1,4 @@ -version: 12 +version: 13 name: supplies sql_table: supplies source_kind: table diff --git a/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/tweets.yaml b/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/tweets.yaml index fbc2b68d..05b57e78 100644 --- a/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/tweets.yaml +++ b/docs/examples/09_lightning_talk/slayer_models/models/jaffle_shop/tweets.yaml @@ -1,4 +1,4 @@ -version: 12 +version: 13 name: tweets sql_table: tweets source_kind: table diff --git a/docs/examples/15_duckdb/duckdb.md b/docs/examples/15_duckdb/duckdb.md index 5296e9d4..d55689bd 100644 --- a/docs/examples/15_duckdb/duckdb.md +++ b/docs/examples/15_duckdb/duckdb.md @@ -16,7 +16,7 @@ Each notebook shows, from scratch, how to define a view over a remote file in Du *cool* by its average high temperature, a `CASE WHEN avg(temp_max, partition_by=date) …` band used as a grouping dimension; and -- a **ranking transform in a measure** — `rank(sum(precipitation))` to order the +- a **ranking transform in a measure** — `rank(sum(precipitation), direction='desc')` to order the months by rainfall, one of SLayer's query-time [transforms](../../concepts/formulas.md). diff --git a/docs/examples/15_duckdb/duckdb_cli_nb.ipynb b/docs/examples/15_duckdb/duckdb_cli_nb.ipynb index 4d563830..accf8491 100644 --- a/docs/examples/15_duckdb/duckdb_cli_nb.ipynb +++ b/docs/examples/15_duckdb/duckdb_cli_nb.ipynb @@ -32,10 +32,10 @@ "id": "a1581e98", "metadata": { "execution": { - "iopub.execute_input": "2026-09-22T07:27:53.628652Z", - "iopub.status.busy": "2026-09-22T07:27:53.628538Z", - "iopub.status.idle": "2026-09-22T07:27:55.437893Z", - "shell.execute_reply": "2026-09-22T07:27:55.437364Z" + "iopub.execute_input": "2026-10-03T14:05:32.294378Z", + "iopub.status.busy": "2026-10-03T14:05:32.294129Z", + "iopub.status.idle": "2026-10-03T14:05:34.307338Z", + "shell.execute_reply": "2026-10-03T14:05:34.307034Z" } }, "outputs": [ @@ -51,7 +51,29 @@ "name": "stdout", "output_type": "stream", "text": [ - "weather\n" + "weather\n", + "time_spine — Built-in virtual model (not stored; every datasource has one): every instant" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + ". Group by time_spine.timestamp at a granularity to get every bucket in range, empty ones included. " + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "For the models wired to it and its rules: inspect(reference='weather_db.time_spine', entity_type='mo" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "del').\n" ] } ], @@ -94,10 +116,10 @@ "id": "23141eb2", "metadata": { "execution": { - "iopub.execute_input": "2026-09-22T07:27:55.439087Z", - "iopub.status.busy": "2026-09-22T07:27:55.438954Z", - "iopub.status.idle": "2026-09-22T07:27:56.335840Z", - "shell.execute_reply": "2026-09-22T07:27:56.335347Z" + "iopub.execute_input": "2026-10-03T14:05:34.309111Z", + "iopub.status.busy": "2026-10-03T14:05:34.309029Z", + "iopub.status.idle": "2026-10-03T14:05:35.245785Z", + "shell.execute_reply": "2026-10-03T14:05:35.245474Z" } }, "outputs": [], @@ -113,10 +135,10 @@ "id": "8846f8cd", "metadata": { "execution": { - "iopub.execute_input": "2026-09-22T07:27:56.337210Z", - "iopub.status.busy": "2026-09-22T07:27:56.337132Z", - "iopub.status.idle": "2026-09-22T07:27:56.540150Z", - "shell.execute_reply": "2026-09-22T07:27:56.539661Z" + "iopub.execute_input": "2026-10-03T14:05:35.246925Z", + "iopub.status.busy": "2026-10-03T14:05:35.246828Z", + "iopub.status.idle": "2026-10-03T14:05:35.410676Z", + "shell.execute_reply": "2026-10-03T14:05:35.410452Z" } }, "outputs": [ @@ -208,7 +230,7 @@ "One query, single stage, two query-time features at the month grain:\n", "\n", "- **A dimension computed from an aggregate.** `season` is `CASE WHEN avg(temp_max, partition_by=date) >= 18 THEN 'warm' ELSE 'cool' END` — each month is grouped *warm* or *cool* by its own average high, a value that only exists after aggregating. SLayer computes it in a synthesized stage and regroups on it.\n", - "- **A ranking transform in a measure.** `rank(sum(precipitation))` orders the months by rainfall, 1 = wettest.\n", + "- **A ranking transform in a measure.** `rank(sum(precipitation), direction='desc')` orders the months by rainfall, 1 = wettest.\n", "\n", "Ordered wettest-first, the answer tells a story: Seattle's rainiest months are all *cool*-season.\n", "\n", @@ -221,10 +243,10 @@ "id": "55bc2e93", "metadata": { "execution": { - "iopub.execute_input": "2026-09-22T07:27:56.541414Z", - "iopub.status.busy": "2026-09-22T07:27:56.541330Z", - "iopub.status.idle": "2026-09-22T07:27:57.586140Z", - "shell.execute_reply": "2026-09-22T07:27:57.585731Z" + "iopub.execute_input": "2026-10-03T14:05:35.411751Z", + "iopub.status.busy": "2026-10-03T14:05:35.411665Z", + "iopub.status.idle": "2026-10-03T14:05:36.513900Z", + "shell.execute_reply": "2026-10-03T14:05:36.513582Z" } }, "outputs": [], @@ -241,7 +263,7 @@ " ],\n", " \"measures\": [\n", " {\"formula\": \"sum(precipitation)\", \"name\": \"total_rain\"},\n", - " {\"formula\": \"rank(sum(precipitation))\", \"name\": \"rain_rank\"}\n", + " {\"formula\": \"rank(sum(precipitation), direction='desc')\", \"name\": \"rain_rank\"}\n", " ],\n", " \"order\": [{\"column\": \"rain_rank\", \"direction\": \"asc\"}]\n", "}\n", @@ -255,10 +277,10 @@ "id": "c59d5e13", "metadata": { "execution": { - "iopub.execute_input": "2026-09-22T07:27:57.587641Z", - "iopub.status.busy": "2026-09-22T07:27:57.587556Z", - "iopub.status.idle": "2026-09-22T07:27:57.593517Z", - "shell.execute_reply": "2026-09-22T07:27:57.593213Z" + "iopub.execute_input": "2026-10-03T14:05:36.519116Z", + "iopub.status.busy": "2026-10-03T14:05:36.518902Z", + "iopub.status.idle": "2026-10-03T14:05:36.526337Z", + "shell.execute_reply": "2026-10-03T14:05:36.526057Z" } }, "outputs": [ @@ -615,14 +637,14 @@ " \n", " 46\n", " warm\n", - " 2012-08-01 00:00:00\n", + " 2013-07-01 00:00:00\n", " 0.0\n", " 47\n", " \n", " \n", " 47\n", " warm\n", - " 2013-07-01 00:00:00\n", + " 2012-08-01 00:00:00\n", " 0.0\n", " 47\n", " \n", @@ -678,8 +700,8 @@ "43 warm 2015-06-01 00:00:00 5.9 44\n", "44 warm 2015-07-01 00:00:00 2.3 45\n", "45 warm 2012-09-01 00:00:00 0.9 46\n", - "46 warm 2012-08-01 00:00:00 0.0 47\n", - "47 warm 2013-07-01 00:00:00 0.0 47" + "46 warm 2013-07-01 00:00:00 0.0 47\n", + "47 warm 2012-08-01 00:00:00 0.0 47" ] }, "execution_count": 5, @@ -709,10 +731,10 @@ "id": "250e2828", "metadata": { "execution": { - "iopub.execute_input": "2026-09-22T07:27:57.594471Z", - "iopub.status.busy": "2026-09-22T07:27:57.594380Z", - "iopub.status.idle": "2026-09-22T07:27:58.260910Z", - "shell.execute_reply": "2026-09-22T07:27:58.260452Z" + "iopub.execute_input": "2026-10-03T14:05:36.527581Z", + "iopub.status.busy": "2026-10-03T14:05:36.527438Z", + "iopub.status.idle": "2026-10-03T14:05:37.197408Z", + "shell.execute_reply": "2026-10-03T14:05:37.197002Z" } }, "outputs": [], @@ -728,10 +750,10 @@ "id": "5ccc4772", "metadata": { "execution": { - "iopub.execute_input": "2026-09-22T07:27:58.262347Z", - "iopub.status.busy": "2026-09-22T07:27:58.262263Z", - "iopub.status.idle": "2026-09-22T07:27:58.264092Z", - "shell.execute_reply": "2026-09-22T07:27:58.263854Z" + "iopub.execute_input": "2026-10-03T14:05:37.198996Z", + "iopub.status.busy": "2026-10-03T14:05:37.198902Z", + "iopub.status.idle": "2026-10-03T14:05:37.200828Z", + "shell.execute_reply": "2026-10-03T14:05:37.200547Z" } }, "outputs": [ @@ -739,12 +761,6 @@ "data": { "text/markdown": [ "```sql\n", - "SELECT\n", - " \"weather.season\",\n", - " \"weather.date\",\n", - " \"weather.total_rain\",\n", - " \"weather.rain_rank\"\n", - "FROM (\n", "WITH _cm_temp_max_avg_partition_by_date AS (\n", " SELECT\n", " _stage_inner.\"weather.date_month\" AS \"date_month\",\n", @@ -781,7 +797,14 @@ " \"weather.season\",\n", " \"weather.date\",\n", " \"weather.total_rain\",\n", - " RANK() OVER (ORDER BY \"weather.total_rain\" DESC) AS \"weather.rain_rank\"\n", + " CASE\n", + " WHEN \"weather.total_rain\" IS NULL\n", + " THEN NULL\n", + " ELSE RANK() OVER (\n", + " PARTITION BY CASE WHEN \"weather.total_rain\" IS NULL THEN 1 ELSE 0 END\n", + " ORDER BY \"weather.total_rain\" DESC\n", + " )\n", + " END AS \"weather.rain_rank\"\n", " FROM base\n", ")\n", "SELECT\n", @@ -789,7 +812,13 @@ " \"weather.date\",\n", " \"weather.total_rain\",\n", " \"weather.rain_rank\"\n", - "FROM step1\n", + "FROM (\n", + " SELECT\n", + " \"weather.season\",\n", + " \"weather.date\",\n", + " \"weather.total_rain\",\n", + " \"weather.rain_rank\"\n", + " FROM step1\n", ") AS _outer\n", "ORDER BY\n", " \"weather.rain_rank\" ASC\n", diff --git a/docs/examples/15_duckdb/duckdb_python_nb.ipynb b/docs/examples/15_duckdb/duckdb_python_nb.ipynb index 8c33e27b..e215764a 100644 --- a/docs/examples/15_duckdb/duckdb_python_nb.ipynb +++ b/docs/examples/15_duckdb/duckdb_python_nb.ipynb @@ -28,10 +28,10 @@ "id": "001e5c0f", "metadata": { "execution": { - "iopub.execute_input": "2026-09-22T07:28:00.676438Z", - "iopub.status.busy": "2026-09-22T07:28:00.676352Z", - "iopub.status.idle": "2026-09-22T07:28:02.211151Z", - "shell.execute_reply": "2026-09-22T07:28:02.210593Z" + "iopub.execute_input": "2026-10-03T14:05:38.066946Z", + "iopub.status.busy": "2026-10-03T14:05:38.066584Z", + "iopub.status.idle": "2026-10-03T14:05:39.770003Z", + "shell.execute_reply": "2026-10-03T14:05:39.769631Z" } }, "outputs": [ @@ -187,10 +187,10 @@ "id": "83eee736", "metadata": { "execution": { - "iopub.execute_input": "2026-09-22T07:28:02.212472Z", - "iopub.status.busy": "2026-09-22T07:28:02.212380Z", - "iopub.status.idle": "2026-09-22T07:28:04.359898Z", - "shell.execute_reply": "2026-09-22T07:28:04.358166Z" + "iopub.execute_input": "2026-10-03T14:05:39.771214Z", + "iopub.status.busy": "2026-10-03T14:05:39.771122Z", + "iopub.status.idle": "2026-10-03T14:05:42.281479Z", + "shell.execute_reply": "2026-10-03T14:05:42.280885Z" } }, "outputs": [ @@ -299,10 +299,10 @@ "id": "90fe7d74", "metadata": { "execution": { - "iopub.execute_input": "2026-09-22T07:28:04.364227Z", - "iopub.status.busy": "2026-09-22T07:28:04.363836Z", - "iopub.status.idle": "2026-09-22T07:28:04.581577Z", - "shell.execute_reply": "2026-09-22T07:28:04.581191Z" + "iopub.execute_input": "2026-10-03T14:05:42.282808Z", + "iopub.status.busy": "2026-10-03T14:05:42.282706Z", + "iopub.status.idle": "2026-10-03T14:05:42.508004Z", + "shell.execute_reply": "2026-10-03T14:05:42.507681Z" } }, "outputs": [ @@ -408,7 +408,7 @@ "Two query-time features at the month grain, in **one** single-stage query:\n", "\n", "- **A dimension computed from an aggregate.** `season` — `CASE WHEN avg(temp_max, partition_by=date) >= 18 THEN 'warm' ELSE 'cool' END` — groups each month *warm* or *cool* by its own average high. That value only exists after aggregating, so SLayer computes it in a synthesized stage and regroups on it (the `partition_by=date` grain follows the monthly time dimension).\n", - "- **A ranking transform in a measure.** `rank(sum(precipitation))` orders the months by rainfall, with 1 the wettest.\n", + "- **A ranking transform in a measure.** `rank(sum(precipitation), direction='desc')` orders the months by rainfall, with 1 the wettest.\n", "\n", "Ordered wettest-first, the result tells a story: every one of Seattle's rainiest months is *cool*-season." ] @@ -419,10 +419,10 @@ "id": "887e7f19", "metadata": { "execution": { - "iopub.execute_input": "2026-09-22T07:28:04.582949Z", - "iopub.status.busy": "2026-09-22T07:28:04.582857Z", - "iopub.status.idle": "2026-09-22T07:28:04.932475Z", - "shell.execute_reply": "2026-09-22T07:28:04.932116Z" + "iopub.execute_input": "2026-10-03T14:05:42.509317Z", + "iopub.status.busy": "2026-10-03T14:05:42.509242Z", + "iopub.status.idle": "2026-10-03T14:05:42.903072Z", + "shell.execute_reply": "2026-10-03T14:05:42.902658Z" } }, "outputs": [ @@ -779,14 +779,14 @@ " \n", " 46\n", " warm\n", - " 2012-08-01\n", + " 2013-07-01\n", " 0.0\n", " 47\n", " \n", " \n", " 47\n", " warm\n", - " 2013-07-01\n", + " 2012-08-01\n", " 0.0\n", " 47\n", " \n", @@ -842,8 +842,8 @@ "43 warm 2015-06-01 5.9 44\n", "44 warm 2015-07-01 2.3 45\n", "45 warm 2012-09-01 0.9 46\n", - "46 warm 2012-08-01 0.0 47\n", - "47 warm 2013-07-01 0.0 47" + "46 warm 2013-07-01 0.0 47\n", + "47 warm 2012-08-01 0.0 47" ] }, "execution_count": 4, @@ -863,7 +863,7 @@ " ],\n", " \"measures\": [\n", " {\"formula\": \"sum(precipitation)\", \"name\": \"total_rain\"},\n", - " {\"formula\": \"rank(sum(precipitation))\", \"name\": \"rain_rank\"},\n", + " {\"formula\": \"rank(sum(precipitation), direction='desc')\", \"name\": \"rain_rank\"},\n", " ],\n", " \"order\": [{\"column\": \"rain_rank\", \"direction\": \"asc\"}],\n", "}\n", @@ -893,10 +893,10 @@ "id": "8b5f7a20", "metadata": { "execution": { - "iopub.execute_input": "2026-09-22T07:28:04.933692Z", - "iopub.status.busy": "2026-09-22T07:28:04.933620Z", - "iopub.status.idle": "2026-09-22T07:28:04.935223Z", - "shell.execute_reply": "2026-09-22T07:28:04.934959Z" + "iopub.execute_input": "2026-10-03T14:05:42.905679Z", + "iopub.status.busy": "2026-10-03T14:05:42.905553Z", + "iopub.status.idle": "2026-10-03T14:05:42.908095Z", + "shell.execute_reply": "2026-10-03T14:05:42.907530Z" } }, "outputs": [ @@ -904,12 +904,6 @@ "name": "stdout", "output_type": "stream", "text": [ - "SELECT\n", - " \"weather.season\",\n", - " \"weather.date\",\n", - " \"weather.total_rain\",\n", - " \"weather.rain_rank\"\n", - "FROM (\n", "WITH _cm_temp_max_avg_partition_by_date AS (\n", " SELECT\n", " _stage_inner.\"weather.date_month\" AS \"date_month\",\n", @@ -946,7 +940,14 @@ " \"weather.season\",\n", " \"weather.date\",\n", " \"weather.total_rain\",\n", - " RANK() OVER (ORDER BY \"weather.total_rain\" DESC) AS \"weather.rain_rank\"\n", + " CASE\n", + " WHEN \"weather.total_rain\" IS NULL\n", + " THEN NULL\n", + " ELSE RANK() OVER (\n", + " PARTITION BY CASE WHEN \"weather.total_rain\" IS NULL THEN 1 ELSE 0 END\n", + " ORDER BY \"weather.total_rain\" DESC\n", + " )\n", + " END AS \"weather.rain_rank\"\n", " FROM base\n", ")\n", "SELECT\n", @@ -954,7 +955,13 @@ " \"weather.date\",\n", " \"weather.total_rain\",\n", " \"weather.rain_rank\"\n", - "FROM step1\n", + "FROM (\n", + " SELECT\n", + " \"weather.season\",\n", + " \"weather.date\",\n", + " \"weather.total_rain\",\n", + " \"weather.rain_rank\"\n", + " FROM step1\n", ") AS _outer\n", "ORDER BY\n", " \"weather.rain_rank\" ASC\n" diff --git a/examples/comparisons/matrix.json b/examples/comparisons/matrix.json index b73d2f59..af4de8f5 100644 --- a/examples/comparisons/matrix.json +++ b/examples/comparisons/matrix.json @@ -2004,7 +2004,7 @@ "cells": { "slayer": { "verdict": "yes", - "how": "rank(sum(revenue)) <= 10 · consecutive_periods(sum(revenue) > 0) >= 3", + "how": "rank(sum(revenue), direction='desc') <= 10 · consecutive_periods(sum(revenue) > 0) >= 3", "caveat": "Streaks count calendar periods, so an empty month breaks them (verified)." }, "malloy": { diff --git a/examples/comparisons/matrix.yaml b/examples/comparisons/matrix.yaml index 2f414429..cffc957e 100644 --- a/examples/comparisons/matrix.yaml +++ b/examples/comparisons/matrix.yaml @@ -290,7 +290,7 @@ rows: origin: SLayer slayer: verdict: 'yes' - how: "`rank(sum(revenue)) <= 10` · `consecutive_periods(sum(revenue) > 0) >= 3`" + how: "`rank(sum(revenue), direction='desc') <= 10` · `consecutive_periods(sum(revenue) > 0) >= 3`" caveat: "Streaks count calendar periods, so an empty month breaks them (verified)." malloy: verdict: partial diff --git a/examples/comparisons/probes.yaml b/examples/comparisons/probes.yaml index 3ebf1a68..44035708 100644 --- a/examples/comparisons/probes.yaml +++ b/examples/comparisons/probes.yaml @@ -257,7 +257,7 @@ probes: compare: {keys: [region, city], values: [rk]} slayer: query: {source_model: orders_flat, dimensions: [region, city], - measures: [{formula: "rank(sum(amount, partition_by=region) - sum(amount, partition_by=city))", name: rk}]} + measures: [{formula: "rank(sum(amount, partition_by=region) - sum(amount, partition_by=city), direction='desc')", name: rk}]} - id: F4 row: Q2 @@ -972,13 +972,13 @@ probes: - id: Q5b row: Q5 - title: filter rank(sum(amount)) <= 2 (top two customers) + title: filter rank(sum(amount), direction='desc') <= 2 (top two customers) truth_sql: | select c.name, sum(o.amount) from orders o left join customers c on o.customer_id = c.id group by 1 order by 2 desc limit 2 compare: {keys: [customers.name], values: [v]} slayer: - query: {source_model: orders, dimensions: [customers.name], filters: ["rank(sum(amount)) <= 2"], + query: {source_model: orders, dimensions: [customers.name], filters: ["rank(sum(amount), direction='desc') <= 2"], measures: [{formula: "sum(amount)", name: v}]} metricflow: note: order by the selected metric plus a global limit @@ -1471,7 +1471,7 @@ probes: compare: {keys: [region], values: [v]} slayer: query: {source_model: orders_flat, dimensions: [region], - measures: [{formula: "max(rank(sum(amount, partition_by=[region, customer_id]), partition_by=region))", name: v}]} + measures: [{formula: "max(rank(sum(amount, partition_by=[region, customer_id]), partition_by=region, direction='desc'))", name: v}]} - id: Q9c-3 row: Q7 @@ -2722,14 +2722,14 @@ probes: - id: Q13b row: Q13 - title: order by an undeclared transform rank(sum(amount)) asc + title: order by an undeclared transform rank(sum(amount), direction='desc') asc truth_sql: | select c.name, count(*) from orders o left join customers c on o.customer_id = c.id group by 1 order by sum(o.amount) desc limit 3 compare: {keys: [customers.name], values: [order_count]} slayer: query: {source_model: orders, dimensions: [customers.name], limit: 3, - order: [{column: "rank(sum(amount))", direction: asc}], measures: [{formula: "count(*)", name: order_count}]} + order: [{column: "rank(sum(amount), direction='desc')", direction: asc}], measures: [{formula: "count(*)", name: order_count}]} - id: Q13c row: Q13 @@ -2773,7 +2773,7 @@ probes: compare: {keys: [region, name], values: [revenue]} slayer: query: {source_model: orders, dimensions: [customers.regions.name, customers.name], - filters: ["rank(sum(amount), partition_by=customers.regions.name) <= 1"], + filters: ["rank(sum(amount), partition_by=customers.regions.name, direction='desc') <= 1"], measures: [{formula: "sum(amount)", name: revenue}]} keys: [customers.regions.name, customers.name] malloy: @@ -2805,7 +2805,7 @@ probes: compare: {keys: [region, city], values: [v]} slayer: query: {source_model: orders_flat, dimensions: [region, city], - filters: ["dense_rank(sum(amount), partition_by=region) <= 1"], measures: [{formula: "sum(amount)", name: v}]} + filters: ["dense_rank(sum(amount), partition_by=region, direction='desc') <= 1"], measures: [{formula: "sum(amount)", name: v}]} - id: W2 row: Q14 @@ -2818,7 +2818,7 @@ probes: compare: {keys: [order_date], values: [v]} slayer: query: {source_model: orders, time_dimensions: [{dimension: order_date, granularity: month}], - measures: [{formula: "rank(sum(amount, window='90d'))", name: v}]} + measures: [{formula: "rank(sum(amount, window='90d'), direction='desc')", name: v}]} - id: Q14c row: Q14 @@ -3387,7 +3387,7 @@ probes: compare: {keys: [region, by_customer.name], values: [revenue, by_customer.revenue]} slayer: query: {source_model: orders, dimensions: [customers.regions.name, customers.name], - filters: ["rank(sum(amount), partition_by=customers.regions.name) <= 2"], + filters: ["rank(sum(amount), partition_by=customers.regions.name, direction='desc') <= 2"], measures: [{formula: "sum(amount, partition_by=customers.regions.name)", name: region_rev}, {formula: "sum(amount)", name: cust_rev}]} keys: [customers.regions.name, customers.name] @@ -3411,7 +3411,7 @@ probes: compare: {keys: [customers.regions.name, customers.name], values: [n]} slayer: query: {source_model: orders, dimensions: [customers.regions.name, customers.name], - filters: ["rank(count(*), partition_by=customers.regions.name) <= 2"], + filters: ["rank(count(*), partition_by=customers.regions.name, direction='desc') <= 2"], measures: [{formula: "count(*)", name: n}]} - id: Q24-sibling-customers diff --git a/examples/embedded/run.py b/examples/embedded/run.py index ac13bb84..1e363dbe 100644 --- a/examples/embedded/run.py +++ b/examples/embedded/run.py @@ -158,7 +158,7 @@ def main(): result = engine.execute_sync(query=SlayerQuery( source_model="orders", dimensions=["customers.name"], - measures=[COUNT_MEASURE, {"formula": "rank(count(*))", "name": "rk"}], + measures=[COUNT_MEASURE, {"formula": "rank(count(*), direction='desc')", "name": "rk"}], order=[{"column": "count", "direction": "desc"}], )) for row in result.data: diff --git a/examples/embedded/verify.py b/examples/embedded/verify.py index 1bee64e6..12da5581 100644 --- a/examples/embedded/verify.py +++ b/examples/embedded/verify.py @@ -210,7 +210,7 @@ def check(name, condition): query=SlayerQuery( source_model="orders", dimensions=["customers.name"], - measures=[COUNT_MEASURE, {"formula": "rank(count(*))", "name": "rnk"}], + measures=[COUNT_MEASURE, {"formula": "rank(count(*), direction='desc')", "name": "rnk"}], order=[{"column": "count", "direction": "desc"}], ) ) diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/tasks.md b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/tasks.md index d7539960..55a9d6e7 100644 --- a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/tasks.md +++ b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/tasks.md @@ -83,24 +83,24 @@ old `810 / 43` and gives `810 / 33` under NULL→NULL. ## 5. Agent-facing text, docs, examples -- [ ] 5.1 Update the rank-family line of the `query` tool description in +- [x] 5.1 Update the rank-family line of the `query` tool description in `slayer/mcp/server.py`, the suggestion strings in `slayer/sql/window_detect.py` and `slayer/core/errors.py`, and `slayer/memories/help_content` if it spells a rank call. Verify: grep finds no bare `rank(` / `dense_rank(` in `slayer/` outside migrations and tests. -- [ ] 5.2 Update the docs: `docs/concepts/formulas.md` (function table and rank section: +- [x] 5.2 Update the docs: `docs/concepts/formulas.md` (function table and rank section: direction, ascending `ntile` / `percent_rank`, NULL→NULL, `_asc` / `_desc` keys), `queries.md`, `models.md`, `references.md`, `docs/database-support.md`, `docs/dbt/dbt_import.md`, `docs/osi/osi_import.md`, and the `01_dynamic`, `05_joined_measures`, `07_aggregations` and `15_duckdb` example pages. Verify: grep finds no bare rank call in `docs/`. -- [ ] 5.3 Update `examples/` (`embedded`, `clickhouse`, `snowflake`, `verify_common.py`, +- [x] 5.3 Update `examples/` (`embedded`, `clickhouse`, `snowflake`, `verify_common.py`, `comparisons/matrix.yaml` + `probes.yaml`). Verify: grep is clean and the matrix / probe checks in the unit suite pass. -- [ ] 5.4 Re-execute every edited notebook in place +- [x] 5.4 Re-execute every edited notebook in place (`jupyter nbconvert --to notebook --execute --inplace`). Verify: committed outputs are fresh and the notebook suite passes. -- [ ] 5.5 Write the release-notes entry in an untracked `RELEASE_NOTES_*.md`: required +- [x] 5.5 Write the release-notes entry in an untracked `RELEASE_NOTES_*.md`: required direction, the `ntile` / `percent_rank` flip, NULL→NULL, renamed unnamed rank keys, lazy migration. Never stage it. diff --git a/slayer/core/formula.py b/slayer/core/formula.py index 3a2c8bd8..53a26a91 100644 --- a/slayer/core/formula.py +++ b/slayer/core/formula.py @@ -659,7 +659,7 @@ def _parse_node( # Remaining positional args are transform parameters (offset, granularity, etc.) # The rank family is keyword-only after the measure; reject extra positionals - # so calls like `rank(revenue:sum, 2)` or `ntile(revenue:sum, 4, n=2)` fail + # so calls like `rank(revenue:sum, 'desc')` or `ntile(revenue:sum, 4, n=2)` fail # fast instead of silently dropping the extra arg downstream. if func_name in RANK_FAMILY_TRANSFORMS and len(node.args) > 1: raise ValueError( diff --git a/slayer/core/keys.py b/slayer/core/keys.py index e355ab03..19a6ad17 100644 --- a/slayer/core/keys.py +++ b/slayer/core/keys.py @@ -404,7 +404,7 @@ def __eq__(self, other: object) -> bool: # Positional and kwarg arg values share one union: `last(created_at)` binds an # identifier column, `weighted_avg(weight=qty)` a column, # `weighted_avg(weight=count(id, partition_by=…))` an aggregate, and -# `weighted_avg(weight=rank(sum(amount, partition_by=…)))` a grained transform — +# `weighted_avg(weight=rank(sum(amount, partition_by=…), direction='desc'))` a grained transform — # all via `_bind_agg_arg`. _AggregateArgValue = Union[ ColumnKey, ColumnSqlKey, SqlFragmentKey, "AggregateKey", "TransformKey", @@ -972,7 +972,7 @@ def effective_root_grain( windowed = window_kwarg_of(agg) is not None if getattr(agg, "partition_keys", None) is not None: grain = regroup_root_grain(agg) - # A transform with no grained inner (e.g. rank(region)) is the degenerate + # A transform with no grained inner (e.g. rank(region, direction='desc')) is the degenerate # query-grain identity (Axiom 11.1); its operand cells are the query grain. if isinstance(agg, TransformKey) and grain.is_empty: return Grain.of([*projected_dim_keys, *projected_td_keys]), False diff --git a/slayer/mcp/server.py b/slayer/mcp/server.py index c763d89b..dbfa3c04 100644 --- a/slayer/mcp/server.py +++ b/slayer/mcp/server.py @@ -510,9 +510,12 @@ async def query( for growth); time_shift(x, -1[, 'year']) (the shifted value itself, for custom arithmetic); lag(x, n) / lead(x, n) (row-position shift, NULL at edges); first(x) / last(x) (broadcast the earliest/latest bucket's value); consecutive_periods(predicate) - (trailing run length; the predicate may be row-level, e.g. status = 'paid'); rank(x), - dense_rank(x), percent_rank(x), ntile(x, n=N) (rank family — optional partition_by=, no - time dimension needed). All other transforms require a time_dimensions entry. + (trailing run length; the predicate may be row-level, e.g. status = 'paid'); + rank(x, direction='desc') / dense_rank(x, direction='asc') (direction is required: + 'desc' ranks the highest value 1, 'asc' the lowest), percent_rank(x), ntile(x, n=N) + (always ascending: bucket 1 / 0.0 is the lowest) (rank family — optional partition_by=, + no time dimension needed; a NULL value ranks NULL). All other transforms require a + time_dimensions entry. Transforms nest in either order (change(cumsum(x))). Not supported: a row-level column mixed into a composite or nested input of time_shift / change / change_pct, or mixed with another aggregation's value inside one aggregation source. diff --git a/slayer/sql/generator.py b/slayer/sql/generator.py index a5260e4f..f00f8cb1 100644 --- a/slayer/sql/generator.py +++ b/slayer/sql/generator.py @@ -4498,7 +4498,7 @@ def _joined_or_local_dim_expr( ) def _rank_family_window( - self, *, fn: Expression, measure: Expression, partition_by: List[Expression], descending: bool, + self, *, fn: Expression, measure: Expression, partition_by: Iterable[Expression], descending: bool, ) -> Expression: """``CASE WHEN v IS NULL THEN NULL ELSE fn OVER (PARTITION BY …, ORDER BY v) END``: NULL rows rank NULL, outside the others' window.""" null_flag = exp.Case( diff --git a/slayer/sql/window_detect.py b/slayer/sql/window_detect.py index 306867c3..78d0456d 100644 --- a/slayer/sql/window_detect.py +++ b/slayer/sql/window_detect.py @@ -30,7 +30,7 @@ def has_window_function(sql: str) -> bool: "contains a window function (OVER clause). Window functions are not " "allowed in WHERE on SQLite or most dialects. Either: (a) use a SLayer " "transform — rank(), first(), last(), lag(), lead() — e.g. " - "'rank() <= 3'; (b) define the window expression as a " + "`rank(, direction='desc') <= 3`; (b) define the window expression as a " "Column.sql on the model and filter on the column; or (c) compute it " "in an earlier stage of a multi-stage model." ) diff --git a/tests/test_dev1733_order_only_transform_composite.py b/tests/test_dev1733_order_only_transform_composite.py index 639dedae..5677dee3 100644 --- a/tests/test_dev1733_order_only_transform_composite.py +++ b/tests/test_dev1733_order_only_transform_composite.py @@ -303,7 +303,7 @@ async def test_rank_order_only_hidden_and_ordered(self, engine) -> None: source_model="orders", dimensions=[ColumnRef(name="status")], measures=[ModelMeasure(formula="amount:sum")], - order=[OrderItem(column="rank(amount:sum, direction='desc')", direction="desc")], + order=[OrderItem.model_validate({"column": "rank(amount:sum, direction='desc')", "direction": "desc"})], ) sql = await _sql(engine, query) assert _outer_select_columns(sql) == ["orders.status", "orders.amount_sum"], sql @@ -343,7 +343,7 @@ async def test_order_only_transform_alongside_declared_transform(self, engine) - source_model="orders", time_dimensions=_MONTH, measures=[ModelMeasure(formula="cumsum(amount:sum)", name="cs")], - order=[OrderItem(column="rank(amount:sum, direction='desc')", direction="desc")], + order=[OrderItem.model_validate({"column": "rank(amount:sum, direction='desc')", "direction": "desc"})], ) sql = await _sql(engine, query) assert _outer_select_columns(sql) == ["orders.created_at", "orders.cs"], sql @@ -354,7 +354,7 @@ async def test_order_only_transform_with_limit_and_offset(self, engine) -> None: source_model="orders", dimensions=[ColumnRef(name="status")], measures=[ModelMeasure(formula="amount:sum")], - order=[OrderItem(column="rank(amount:sum, direction='desc')", direction="desc")], + order=[OrderItem.model_validate({"column": "rank(amount:sum, direction='desc')", "direction": "desc"})], limit=2, offset=1, ) sql = await _sql(engine, query) @@ -370,7 +370,7 @@ async def test_order_only_transform_with_post_filter(self, engine) -> None: time_dimensions=_MONTH, measures=[ModelMeasure(formula="cumsum(amount:sum)", name="cs")], filters=["cs > 5"], - order=[OrderItem(column="rank(amount:sum, direction='desc')", direction="desc")], + order=[OrderItem.model_validate({"column": "rank(amount:sum, direction='desc')", "direction": "desc"})], ) sql = await _sql(engine, query) assert _outer_select_columns(sql) == ["orders.created_at", "orders.cs"], sql @@ -579,8 +579,8 @@ async def test_two_order_items_same_transform_op(self, engine) -> None: dimensions=[ColumnRef(name="status")], measures=[ModelMeasure(formula="*:count")], order=[ - OrderItem(column="rank(amount:sum, direction='desc')", direction="desc"), - OrderItem(column="rank(fee:sum, direction='desc')", direction="asc"), + OrderItem.model_validate({"column": "rank(amount:sum, direction='desc')", "direction": "desc"}), + OrderItem.model_validate({"column": "rank(fee:sum, direction='desc')", "direction": "asc"}), ], ) sql = await _sql(engine, query) @@ -1063,7 +1063,7 @@ async def test_order_only_transform_quotes_per_dialect( source_model="orders", dimensions=[ColumnRef(name="status")], measures=[ModelMeasure(formula="amount:sum")], - order=[OrderItem(column="rank(amount:sum, direction='desc')", direction="desc")], + order=[OrderItem.model_validate({"column": "rank(amount:sum, direction='desc')", "direction": "desc"})], ) resp = await eng.execute(query, dry_run=True) sql = resp.sql or "" @@ -1121,7 +1121,7 @@ async def test_top_n_by_order_only_transform(self, exec_engine) -> None: source_model="orders", dimensions=[ColumnRef(name="status")], measures=[ModelMeasure(formula="amount:sum")], - order=[OrderItem(column="rank(amount:sum, direction='desc')", direction="desc")], + order=[OrderItem.model_validate({"column": "rank(amount:sum, direction='desc')", "direction": "desc"})], ) resp = await exec_engine.execute(query) assert [r["orders.status"] for r in resp.data] == ["open", "paid"], resp.data @@ -1203,7 +1203,7 @@ async def test_hidden_transform_order_slot_stripped_from_response( source_model="orders", dimensions=[ColumnRef(name="status")], measures=[ModelMeasure(formula="amount:sum")], - order=[OrderItem(column="rank(amount:sum, direction='desc')", direction="desc")], + order=[OrderItem.model_validate({"column": "rank(amount:sum, direction='desc')", "direction": "desc"})], ) resp = await exec_engine.execute(query) assert resp.columns == ["orders.status", "orders.amount_sum"], resp.columns @@ -1256,7 +1256,7 @@ async def test_downstream_stage_order_only_transform(self, exec_engine) -> None: source_model="s1", dimensions=[ColumnRef(name="status")], measures=[ModelMeasure(formula="amt:sum", name="total")], - order=[OrderItem(column="rank(amt:sum, direction='desc')", direction="desc")], + order=[OrderItem.model_validate({"column": "rank(amt:sum, direction='desc')", "direction": "desc"})], ) resp = await exec_engine.execute(query=[inner, outer]) assert resp.columns == ["s1.status", "s1.total"], resp.columns @@ -1368,7 +1368,7 @@ async def test_order_only_transform_in_raw_rows_mode_rejected( source_model="orders", dimensions=[ColumnRef(name="status")], distinct_dimension_values=False, - order=[OrderItem(column="rank(amount:sum, direction='desc')", direction="desc")], + order=[OrderItem.model_validate({"column": "rank(amount:sum, direction='desc')", "direction": "desc"})], ) with pytest.raises(DistinctDimensionValuesError) as ei: await _sql(engine, query) diff --git a/tests/test_dev1832_transform_source.py b/tests/test_dev1832_transform_source.py index e8d99a48..0c28ddcf 100644 --- a/tests/test_dev1832_transform_source.py +++ b/tests/test_dev1832_transform_source.py @@ -640,8 +640,10 @@ async def test_rank_isolates_null_cells_on_postgres(self): measures=[ModelMeasure(formula=WINDOWED_INNER, name="m")], time_dimensions=month_td()), dialect="postgres") [window] = rank_windows(sql, dialect="postgres") - assert window.fn == "RANK" and window.descending, sql - assert window.null_flag and window.null_guarded, sql + assert window.fn == "RANK", sql + assert window.descending, sql + assert window.null_flag, sql + assert window.null_guarded, sql class TestCrossModelGrainedInnerBoundary: diff --git a/tests/test_dev1953_partition_alias.py b/tests/test_dev1953_partition_alias.py index 1abb1839..5fb3e3bd 100644 --- a/tests/test_dev1953_partition_alias.py +++ b/tests/test_dev1953_partition_alias.py @@ -71,7 +71,7 @@ def _ureg_city_ranks() -> Dict[Tuple, Optional[int]]: def _weighted_avg(group: Callable[[tuple], str], - weight: Callable[[tuple], int]) -> Dict[str, Optional[float]]: + weight: Callable[[tuple], Optional[int]]) -> Dict[str, Optional[float]]: """weighted_avg(amount, weight=…) per ``group(row)``.""" per: Dict[str, list] = defaultdict(list) for row in _SALES_ROWS_WIDE: diff --git a/tests/test_memory_string_ids.py b/tests/test_memory_string_ids.py index 11c63059..43cd1173 100644 --- a/tests/test_memory_string_ids.py +++ b/tests/test_memory_string_ids.py @@ -13,10 +13,12 @@ from collections.abc import Iterator import pytest +import yaml from slayer.core.errors import IdCollisionError from slayer.memories.models import Memory from slayer.storage.base import StorageBackend +from slayer.storage.migrations import migrate from slayer.storage.sqlite_storage import SQLiteStorage from slayer.storage.yaml_storage import YAMLStorage @@ -271,7 +273,6 @@ async def test_v2_save_round_trip( def test_duplicate_int_string_rows_same_content_normalized(self) -> None: """The v2 migrator deduplicates rows that exist under both int and str forms (``42`` and ``"42"``) when their content matches.""" - from slayer.storage.migrations import migrate int_row = { "version": 1, @@ -295,7 +296,6 @@ async def test_yaml_legacy_int_and_string_rows_dedupe_on_load(self) -> None: both ``id: 42`` (int) and ``id: "42"`` (str) for the same logical memory must collapse to a single row on load. When content matches, keep one; when content differs, raise loud.""" - import yaml with tempfile.TemporaryDirectory() as tmpdir: # Two rows with the SAME content — should dedupe silently. @@ -325,7 +325,6 @@ def test_yaml_legacy_int_and_string_rows_conflict_raises(self) -> None: """Same-id under int and str forms with DIFFERENT learning content is a data-loss risk; the migrator must fail loud rather than silently picking one.""" - import yaml with tempfile.TemporaryDirectory() as tmpdir: legacy_path = os.path.join(tmpdir, "memories.yaml") diff --git a/tests/test_query_backed_typed_expansion.py b/tests/test_query_backed_typed_expansion.py index 21d472b7..ef217beb 100644 --- a/tests/test_query_backed_typed_expansion.py +++ b/tests/test_query_backed_typed_expansion.py @@ -526,7 +526,7 @@ async def test_excludes_hidden_hoisted_slots(self) -> None: source_queries=[SlayerQuery( source_model="orders", dimensions=["status"], - measures=[{"formula": "rank(amount:sum, direction='desc')", "name": "rank_by_amt"}], + measures=[ModelMeasure(formula="rank(amount:sum, direction='desc')", name="rank_by_amt")], )], ) engine, tmp = await _engine() diff --git a/tests/test_rank_direction.py b/tests/test_rank_direction.py index be0cc1da..61a7be72 100644 --- a/tests/test_rank_direction.py +++ b/tests/test_rank_direction.py @@ -258,14 +258,16 @@ def test_error_is_a_query_type_error(self): class TestDirectionErrors: @pytest.mark.parametrize(("fields", "op"), _MISSING) async def test_missing_direction_fails_before_sql(self, engine, fields, op): + query = sales_q(**fields) for dry_run in (True, False): with pytest.raises(core_errors.TransformArgumentError) as ei: - await engine.execute(sales_q(**fields), dry_run=dry_run) + await engine.execute(query, dry_run=dry_run) _assert_missing_direction(str(ei.value), op=op) async def test_saved_measure_without_direction(self, saved_bare_engine): + query = sales_q(dimensions=["region"], measures=["saved_rank"]) with pytest.raises(core_errors.TransformArgumentError) as ei: - await saved_bare_engine.execute(sales_q(dimensions=["region"], measures=["saved_rank"])) + await saved_bare_engine.execute(query) _assert_missing_direction(str(ei.value)) @pytest.mark.parametrize("formula", [ @@ -275,8 +277,9 @@ async def test_saved_measure_without_direction(self, saved_bare_engine): "dense_rank(sum(amount), direction='')", ]) async def test_unrecognised_or_non_literal(self, engine, formula): + query = sales_q(dimensions=["region"], measures=[_m(formula)]) with pytest.raises(core_errors.TransformArgumentError) as ei: - await engine.execute(sales_q(dimensions=["region"], measures=[_m(formula)])) + await engine.execute(query) msg = str(ei.value) for word in ("asc", "desc", "ascending", "descending"): assert word in msg, msg @@ -286,11 +289,13 @@ async def test_unrecognised_or_non_literal(self, engine, formula): ("percent_rank(sum(amount), direction='asc')", "percent_rank"), ]) async def test_ntile_and_percent_rank_reject_direction(self, engine, formula, op): + query = sales_q(dimensions=["region"], measures=[_m(formula)]) with pytest.raises(core_errors.TransformArgumentError) as ei: - await engine.execute(sales_q(dimensions=["region"], measures=[_m(formula)])) + await engine.execute(query) msg = str(ei.value) assert op in msg, msg - assert "ascending" in msg and "direction" in msg, msg + assert "ascending" in msg, msg + assert "direction" in msg, msg @pytest.mark.parametrize("formula", [ "rank(amount:sum, foo=1, direction='desc')", @@ -410,8 +415,11 @@ async def test_partition_keys_precede_the_null_flag(self, dialect): dimensions=["region", "city"], measures=[_m("rank(sum(amount), partition_by=region, direction='asc')")]), dialect=dialect) [window] = rank_windows(sql, dialect=dialect) - assert len(window.partition_sql) == 1 and "region" in window.partition_sql[0], sql - assert window.null_flag and window.null_guarded and not window.descending + assert len(window.partition_sql) == 1, sql + assert "region" in window.partition_sql[0], sql + assert window.null_flag + assert window.null_guarded + assert not window.descending @pytest.mark.parametrize("formula", [p.values[0] for p in _EMISSION]) async def test_tsql_matches_postgres(self, formula): diff --git a/tests/test_rank_direction_migration.py b/tests/test_rank_direction_migration.py index 205ff38c..f6a3f914 100644 --- a/tests/test_rank_direction_migration.py +++ b/tests/test_rank_direction_migration.py @@ -393,9 +393,11 @@ async def test_fresh_or_current_query_with_bare_rank_fails(self, seed, version): payload: dict = {"source_model": "sales", "dimensions": ["region"], "measures": [BARE]} if version == "current": payload["version"] = mig.CURRENT_VERSIONS["SlayerQuery"] + query = SlayerQuery.model_validate(payload) with pytest.raises(core_errors.TransformArgumentError) as ei: - await _execute(storage, SlayerQuery.model_validate(payload)) - assert "direction='asc'" in str(ei.value) and "direction='desc'" in str(ei.value) + await _execute(storage, query) + assert "direction='asc'" in str(ei.value) + assert "direction='desc'" in str(ei.value) async def test_explicit_old_query_executes_descending(self, seed): storage = await seed(models=[_model_dict(measures=[])]) @@ -428,7 +430,8 @@ def test_rest_fresh_payload_fails(served_storage): resp = client.post("/query", json={"source_model": "sales", "dimensions": ["region"], "measures": [{"formula": BARE}]}) assert 400 <= resp.status_code < 500 - assert "direction='desc'" in resp.text and "direction='asc'" in resp.text + assert "direction='desc'" in resp.text + assert "direction='asc'" in resp.text async def test_mcp_fresh_payload_fails(served_storage): @@ -438,4 +441,5 @@ async def test_mcp_fresh_payload_fails(served_storage): "source_model": "sales", "dimensions": ["region"], "measures": [BARE]}})) except Exception as exc: # noqa: BLE001 — a ToolError carries the message too text = str(exc) - assert "direction='desc'" in text and "direction='asc'" in text + assert "direction='desc'" in text + assert "direction='asc'" in text diff --git a/tests/test_sql_generator.py b/tests/test_sql_generator.py index 31949dfc..58783639 100644 --- a/tests/test_sql_generator.py +++ b/tests/test_sql_generator.py @@ -1855,7 +1855,8 @@ def _assert_rank_window(sql: str, *, fn: str, partition: list[str], descending: [window] = rank_windows(sql, dialect="postgres") assert (window.fn, window.order_sql, window.partition_sql, window.descending) == ( fn, '"orders.revenue_sum"', partition, descending), sql - assert window.null_flag and window.null_guarded, sql + assert window.null_flag, sql + assert window.null_guarded, sql class TestRankFamilyTransforms: diff --git a/tests/test_transforms_planner.py b/tests/test_transforms_planner.py index c8d68e5d..9984f45e 100644 --- a/tests/test_transforms_planner.py +++ b/tests/test_transforms_planner.py @@ -44,7 +44,7 @@ TransformKey, ) from slayer.core.keys import Grain -from slayer.core.models import Column, SlayerModel +from slayer.core.models import Column, ModelMeasure, SlayerModel from slayer.core.query import ColumnRef, SlayerQuery, TimeDimension from slayer.core.scope import ModelScope from slayer.engine.binding import bind_expr @@ -399,7 +399,7 @@ def test_cumsum_emits_transform_layer(self) -> None: def test_rank_emits_transform_layer(self) -> None: q = SlayerQuery( source_model="orders", - measures=[{"formula": "rank(amount:sum, direction='desc')"}], + measures=[ModelMeasure(formula="rank(amount:sum, direction='desc')")], ) planned = plan_query(query=q, bundle=_bundle()) assert any( From 0092d3a7def16f08ec3ee28bab1582892135f527 Mon Sep 17 00:00:00 2001 From: Egor Kraev Date: Sat, 3 Oct 2026 16:14:22 +0200 Subject: [PATCH 05/11] Tick gate tasks for rank direction --- .../tasks.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/tasks.md b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/tasks.md index 55a9d6e7..f29bb43e 100644 --- a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/tasks.md +++ b/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/tasks.md @@ -106,11 +106,11 @@ old `810 / 43` and gives `810 / 33` under NULL→NULL. ## 6. Gates -- [ ] 6.1 `poetry run pytest -m "not integration"` is fully green. -- [ ] 6.2 The integration suite with the CI invocation from CLAUDE.md is green +- [x] 6.1 `poetry run pytest -m "not integration"` is fully green. +- [x] 6.2 The integration suite with the CI invocation from CLAUDE.md is green (Postgres locally). -- [ ] 6.3 `poetry run ruff check slayer/ tests/` and `poetry run basedpyright` (no new +- [x] 6.3 `poetry run ruff check slayer/ tests/` and `poetry run basedpyright` (no new errors vs the baseline) are clean. -- [ ] 6.4 `uvx --no-build --from living-architecture==0.2.1 la-arch-check` is clean. -- [ ] 6.5 `openspec validate dev-2040-rank-family-ordering-required-direction-on-rankdense-rank --strict` +- [x] 6.4 `uvx --no-build --from living-architecture==0.2.1 la-arch-check` is clean. +- [x] 6.5 `openspec validate dev-2040-rank-family-ordering-required-direction-on-rankdense-rank --strict` passes. From 71ed1d0b94509c32fd3a2842adb3db81d74a756f Mon Sep 17 00:00:00 2001 From: Egor Kraev Date: Sat, 3 Oct 2026 18:00:47 +0200 Subject: [PATCH 06/11] Waive import-not-top for the embedded examples' sys.path bootstrap --- examples/embedded/run.py | 16 ++++++++-------- examples/embedded/verify.py | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/examples/embedded/run.py b/examples/embedded/run.py index 1e363dbe..2351ea30 100644 --- a/examples/embedded/run.py +++ b/examples/embedded/run.py @@ -16,14 +16,14 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..")) sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) -from seed import seed - -from slayer.async_utils import run_sync -from slayer.core.models import DatasourceConfig -from slayer.core.query import SlayerQuery -from slayer.engine.ingestion import ingest_datasource -from slayer.engine.query_engine import SlayerQueryEngine -from slayer.storage.yaml_storage import YAMLStorage +from seed import seed # ALLOW(import-not-top): sys.path bootstrap for the shared examples/seed.py + +from slayer.async_utils import run_sync # ALLOW(import-not-top): sys.path bootstrap for the shared examples/seed.py +from slayer.core.models import DatasourceConfig # ALLOW(import-not-top): sys.path bootstrap for the shared examples/seed.py +from slayer.core.query import SlayerQuery # ALLOW(import-not-top): sys.path bootstrap for the shared examples/seed.py +from slayer.engine.ingestion import ingest_datasource # ALLOW(import-not-top): sys.path bootstrap for the shared examples/seed.py +from slayer.engine.query_engine import SlayerQueryEngine # ALLOW(import-not-top): sys.path bootstrap for the shared examples/seed.py +from slayer.storage.yaml_storage import YAMLStorage # ALLOW(import-not-top): sys.path bootstrap for the shared examples/seed.py # Repeated string literals hoisted to constants (Sonar python:S1192). COUNT_MEASURE = "count(*)" diff --git a/examples/embedded/verify.py b/examples/embedded/verify.py index 12da5581..fcc21aad 100644 --- a/examples/embedded/verify.py +++ b/examples/embedded/verify.py @@ -12,14 +12,14 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..")) sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) -from seed import seed, ORDERS - -from slayer.async_utils import run_sync -from slayer.core.models import DatasourceConfig -from slayer.core.query import SlayerQuery -from slayer.engine.ingestion import ingest_datasource -from slayer.engine.query_engine import SlayerQueryEngine -from slayer.storage.yaml_storage import YAMLStorage +from seed import seed, ORDERS # ALLOW(import-not-top): sys.path bootstrap for the shared examples/seed.py + +from slayer.async_utils import run_sync # ALLOW(import-not-top): sys.path bootstrap for the shared examples/seed.py +from slayer.core.models import DatasourceConfig # ALLOW(import-not-top): sys.path bootstrap for the shared examples/seed.py +from slayer.core.query import SlayerQuery # ALLOW(import-not-top): sys.path bootstrap for the shared examples/seed.py +from slayer.engine.ingestion import ingest_datasource # ALLOW(import-not-top): sys.path bootstrap for the shared examples/seed.py +from slayer.engine.query_engine import SlayerQueryEngine # ALLOW(import-not-top): sys.path bootstrap for the shared examples/seed.py +from slayer.storage.yaml_storage import YAMLStorage # ALLOW(import-not-top): sys.path bootstrap for the shared examples/seed.py # Repeated string literals hoisted to constants (Sonar python:S1192). COUNT_MEASURE = "count(*)" From 82b65bb52d8ac89371f6444a54541409efcc2fb8 Mon Sep 17 00:00:00 2001 From: Egor Kraev Date: Sat, 3 Oct 2026 18:16:24 +0200 Subject: [PATCH 07/11] List direction among accepted keywords in rank/dense_rank unknown-keyword errors --- slayer/core/formula.py | 5 +++-- slayer/engine/binding.py | 8 ++++++-- tests/test_rank_direction.py | 13 +++++++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/slayer/core/formula.py b/slayer/core/formula.py index 53a26a91..236c7794 100644 --- a/slayer/core/formula.py +++ b/slayer/core/formula.py @@ -23,7 +23,7 @@ from pydantic import BaseModel, Field -from slayer.core.direction import rank_direction +from slayer.core.direction import DIRECTED_RANK_TRANSFORMS, rank_direction from slayer.core.enums import ( BUILTIN_AGGREGATIONS, RANK_FAMILY_TRANSFORMS, @@ -894,9 +894,10 @@ def _parse_transform_kwargs( # NOSONAR S3776 — straight-line whitelist + per- f"Transform '{transform}' does not accept keyword arguments; " f"got '{kw.arg}=' in formula {original!r}" ) + advertised = allowed | ({"direction"} if transform in DIRECTED_RANK_TRANSFORMS else set()) raise ValueError( f"Transform '{transform}' does not accept keyword '{kw.arg}'. " - f"Accepted kwargs: {', '.join(sorted(allowed))}. " + f"Accepted kwargs: {', '.join(sorted(advertised))}. " f"Formula: {original!r}" ) diff --git a/slayer/engine/binding.py b/slayer/engine/binding.py index 692b9496..5e7fdfde 100644 --- a/slayer/engine/binding.py +++ b/slayer/engine/binding.py @@ -36,7 +36,7 @@ format_unknown_aggregation, normalize_aggregation_name, ) -from slayer.core.direction import rank_direction +from slayer.core.direction import DIRECTED_RANK_TRANSFORMS, rank_direction from slayer.core.enums import RANK_FAMILY_TRANSFORMS from slayer.core.granularity import CustomGranularity, Granularity, resolve_granularity from slayer.core.refs import EXPRESSION_SOURCE_KINDS, key_display @@ -1665,7 +1665,11 @@ def _bind_transform_params( direction_value = _fold_to_scalar(v) continue if k not in allowed_kwargs: - advertised = allowed_kwargs | ({"partition_by"} if rank_family else set()) + advertised = ( + allowed_kwargs + | ({"partition_by"} if rank_family else set()) + | ({"direction"} if op in DIRECTED_RANK_TRANSFORMS else set()) + ) raise TransformArgumentError( summary=f"Transform {op!r} does not accept keyword " f"argument {k!r}. Accepted: {sorted(advertised)}." diff --git a/tests/test_rank_direction.py b/tests/test_rank_direction.py index 61a7be72..06da1859 100644 --- a/tests/test_rank_direction.py +++ b/tests/test_rank_direction.py @@ -311,6 +311,19 @@ def test_other_transform_argument_errors_are_typed(self, formula): with pytest.raises(core_errors.TransformArgumentError): _bind(formula) + @pytest.mark.parametrize(("op", "advertised"), [ + ("rank", True), ("dense_rank", True), ("percent_rank", False), + ]) + def test_unknown_keyword_lists_direction_where_accepted(self, op, advertised): + formula = f"{op}(sum(amount), foo=1)" + with pytest.raises(core_errors.TransformArgumentError) as binder: + _bind(formula) + with pytest.raises(ValueError) as importer: + parse_formula(formula) + for msg in (str(binder.value), str(importer.value)): + assert "foo" in msg, msg + assert ("direction" in msg) is advertised, msg + class TestImporterParity: @pytest.mark.parametrize("formula", [ From cb6aaccdf263f98b9a9b1d838f74edfa7712bb8c Mon Sep 17 00:00:00 2001 From: Egor Kraev Date: Sat, 3 Oct 2026 18:39:42 +0200 Subject: [PATCH 08/11] Execute the rank-family NULL-input tests on real Postgres Subclasses TestNullInputs over a pytest-postgresql database. T-SQL and BigQuery stay emission-only here: no local ODBC driver / no credentials. --- .../test_rank_direction_postgres.py | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 tests/integration/test_rank_direction_postgres.py diff --git a/tests/integration/test_rank_direction_postgres.py b/tests/integration/test_rank_direction_postgres.py new file mode 100644 index 00000000..5f6256d2 --- /dev/null +++ b/tests/integration/test_rank_direction_postgres.py @@ -0,0 +1,61 @@ +"""Rank-family NULL inputs executed on a real Postgres.""" + +import pytest +from pytest_postgresql import factories + +import tests.test_rank_direction as base +from slayer.core.models import DatasourceConfig +from slayer.engine.query_engine import SlayerQueryEngine +from slayer.sql import engine_factory +from slayer.storage.yaml_storage import YAMLStorage +from tests._dev1847_fixtures import ( + _CORDERS_ROWS, + _CUSTOMERS_ROWS, + _REGIONS_ROWS, + _SALES_ROWS_WIDE, + dev1847_models, +) + +pytestmark = pytest.mark.integration + +postgresql_proc = factories.postgresql_proc(port=None) +postgresql = factories.postgresql("postgresql_proc") + +_DDL = ( + "CREATE TABLE sales (id INTEGER PRIMARY KEY, region TEXT, city TEXT, product TEXT, " + "amount DOUBLE PRECISION, quantity DOUBLE PRECISION, unit_price DOUBLE PRECISION)", + "CREATE TABLE regions (id INTEGER PRIMARY KEY, name TEXT)", + "CREATE TABLE customers (id INTEGER PRIMARY KEY, region_id INTEGER)", + "CREATE TABLE corders (id INTEGER PRIMARY KEY, customer_id INTEGER, amount DOUBLE PRECISION)", +) +_INSERTS = ( + ("INSERT INTO sales VALUES (%s, %s, %s, %s, %s, %s, %s)", _SALES_ROWS_WIDE), + ("INSERT INTO regions VALUES (%s, %s)", _REGIONS_ROWS), + ("INSERT INTO customers VALUES (%s, %s)", _CUSTOMERS_ROWS), + ("INSERT INTO corders VALUES (%s, %s, %s)", _CORDERS_ROWS), +) + + +class TestPostgresNullInputs(base.TestNullInputs): + @pytest.fixture + async def engine(self, postgresql, tmp_path): + cur = postgresql.cursor() + for stmt in _DDL: + cur.execute(stmt) + for stmt, rows in _INSERTS: + cur.executemany(stmt, rows) + postgresql.commit() + info = postgresql.info + ds = DatasourceConfig(name="test", type="postgres", host=info.host, port=info.port, + database=info.dbname, username=info.user, password="") + storage = YAMLStorage(base_dir=str(tmp_path)) + await storage.save_datasource(ds) + for model in dev1847_models(): + await storage.save_model(model) + engine = SlayerQueryEngine(storage=storage) + try: + yield engine + finally: + await engine.aclose() + engine.close() + engine_factory.invalidate_engine(ds) From bd24c46d21e845fb4c08083fc5cf7be480cf7cac Mon Sep 17 00:00:00 2001 From: Egor Kraev Date: Sat, 3 Oct 2026 19:00:08 +0200 Subject: [PATCH 09/11] Share the direction-keyword listing between binder and importer One helper builds the accepted-keywords list for both unknown-keyword errors; hoisting it out of the binder loop clears Sonar S3776 (18 -> 13). --- slayer/core/direction.py | 5 +++++ slayer/core/formula.py | 4 ++-- slayer/engine/binding.py | 10 ++++------ tests/test_rank_direction.py | 8 ++++++++ 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/slayer/core/direction.py b/slayer/core/direction.py index 65134760..24b62b73 100644 --- a/slayer/core/direction.py +++ b/slayer/core/direction.py @@ -26,6 +26,11 @@ def normalize_direction(value: Any) -> str | None: return DIRECTION_NORMALIZE.get(value.strip().lower()) +def with_direction_kwarg(*, op: str, accepted: frozenset[str]) -> frozenset[str]: + """``accepted`` plus ``direction`` when ``op`` takes one, for error listings.""" + return accepted | {"direction"} if op in DIRECTED_RANK_TRANSFORMS else accepted + + def rank_direction(*, op: str, given: bool, value: Any = None) -> str | None: """Validate a rank-family call's ``direction=``; ``value`` is the literal, or any non-``str`` when not a string literal.""" if op in ASCENDING_RANK_TRANSFORMS: diff --git a/slayer/core/formula.py b/slayer/core/formula.py index 236c7794..ce3c1913 100644 --- a/slayer/core/formula.py +++ b/slayer/core/formula.py @@ -23,7 +23,7 @@ from pydantic import BaseModel, Field -from slayer.core.direction import DIRECTED_RANK_TRANSFORMS, rank_direction +from slayer.core.direction import rank_direction, with_direction_kwarg from slayer.core.enums import ( BUILTIN_AGGREGATIONS, RANK_FAMILY_TRANSFORMS, @@ -894,7 +894,7 @@ def _parse_transform_kwargs( # NOSONAR S3776 — straight-line whitelist + per- f"Transform '{transform}' does not accept keyword arguments; " f"got '{kw.arg}=' in formula {original!r}" ) - advertised = allowed | ({"direction"} if transform in DIRECTED_RANK_TRANSFORMS else set()) + advertised = with_direction_kwarg(op=transform, accepted=allowed) raise ValueError( f"Transform '{transform}' does not accept keyword '{kw.arg}'. " f"Accepted kwargs: {', '.join(sorted(advertised))}. " diff --git a/slayer/engine/binding.py b/slayer/engine/binding.py index 5e7fdfde..fe319f37 100644 --- a/slayer/engine/binding.py +++ b/slayer/engine/binding.py @@ -36,7 +36,7 @@ format_unknown_aggregation, normalize_aggregation_name, ) -from slayer.core.direction import DIRECTED_RANK_TRANSFORMS, rank_direction +from slayer.core.direction import rank_direction, with_direction_kwarg from slayer.core.enums import RANK_FAMILY_TRANSFORMS from slayer.core.granularity import CustomGranularity, Granularity, resolve_granularity from slayer.core.refs import EXPRESSION_SOURCE_KINDS, key_display @@ -1652,6 +1652,9 @@ def _bind_transform_params( allowed_kwargs = _TRANSFORM_KWARG_RULES.get(op, frozenset()) seen_kwargs: set = set() rank_family = op in RANK_FAMILY_TRANSFORMS + advertised = with_direction_kwarg( + op=op, accepted=allowed_kwargs | {"partition_by"} if rank_family else allowed_kwargs, + ) direction_value: object = _NOT_SCALAR for k, v in [*positional_pairs, *kwargs]: if k == "partition_by" and rank_family: @@ -1665,11 +1668,6 @@ def _bind_transform_params( direction_value = _fold_to_scalar(v) continue if k not in allowed_kwargs: - advertised = ( - allowed_kwargs - | ({"partition_by"} if rank_family else set()) - | ({"direction"} if op in DIRECTED_RANK_TRANSFORMS else set()) - ) raise TransformArgumentError( summary=f"Transform {op!r} does not accept keyword " f"argument {k!r}. Accepted: {sorted(advertised)}." diff --git a/tests/test_rank_direction.py b/tests/test_rank_direction.py index 06da1859..cd33c1a4 100644 --- a/tests/test_rank_direction.py +++ b/tests/test_rank_direction.py @@ -324,6 +324,14 @@ def test_unknown_keyword_lists_direction_where_accepted(self, op, advertised): assert "foo" in msg, msg assert ("direction" in msg) is advertised, msg + def test_unknown_keyword_outside_rank_family_lists_no_rank_keywords(self): + with pytest.raises(core_errors.TransformArgumentError) as ei: + _bind("lag(amount:sum, foo=1)") + msg = str(ei.value) + assert "foo" in msg, msg + assert "partition_by" not in msg, msg + assert "direction" not in msg, msg + class TestImporterParity: @pytest.mark.parametrize("formula", [ From 5f91a13709748f4867d2a330c641c949ffc2c6ea Mon Sep 17 00:00:00 2001 From: Egor Kraev Date: Sat, 3 Oct 2026 20:54:29 +0200 Subject: [PATCH 10/11] Drop past-issue references from lines this PR changed; fix a stale rank comment --- tests/test_sql_generator.py | 2 +- tests/test_syntax.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_sql_generator.py b/tests/test_sql_generator.py index 58783639..9d600a5c 100644 --- a/tests/test_sql_generator.py +++ b/tests/test_sql_generator.py @@ -2029,7 +2029,7 @@ async def test_ntile_with_n_kwarg_in_filter( async def test_rank_with_partition_by_kwarg_in_filter( self, generator: SQLGenerator, orders_model: SlayerModel ) -> None: - """DEV-1492: ``rank(, partition_by=, direction='desc') <= 1`` end-to-end.""" + """``rank(, partition_by=, direction='desc') <= 1`` end-to-end.""" query = SlayerQuery( source_model="orders", dimensions=[ColumnRef(name="status"), ColumnRef(name="customer_id")], diff --git a/tests/test_syntax.py b/tests/test_syntax.py index 3d6ddf0b..2a0c711a 100644 --- a/tests/test_syntax.py +++ b/tests/test_syntax.py @@ -704,8 +704,7 @@ def test_transform_kwarg_preserved_in_filter(self): assert result.left.kwargs == (("n", Literal(value=Decimal(4))),) def test_rank_partition_by_kwarg_preserved_in_filter(self): - # DEV-1492: rank(revenue:sum, partition_by=region) <= 1 — kwarg - # survives the operator rewrite; binder turns partition_by into a + # Both kwargs survive the operator rewrite; binder turns partition_by into a # column ref (covered by SQL-gen tests). result = parse_filter_expr("rank(revenue:sum, partition_by=region, direction='desc') <= 1") assert isinstance(result, Cmp) From c4d8570d2f24b4e83ce8c5950fffcdd7f374e867 Mon Sep 17 00:00:00 2001 From: Egor Kraev Date: Sat, 3 Oct 2026 21:11:27 +0200 Subject: [PATCH 11/11] Archive OpenSpec change dev-2040-rank-family-ordering-required-direction-on-rankdense-rank --- .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../aggregations/functional-form/spec.md | 0 .../specs/queries/computed-dimensions/spec.md | 0 .../specs/queries/measure-naming/spec.md | 0 .../queries/partitioned-aggregates/spec.md | 0 .../specs/queries/semantics/spec.md | 0 .../specs/queries/transforms/spec.md | 0 .../tasks.md | 0 .../aggregations/functional-form/spec.md | 6 +- .../specs/queries/computed-dimensions/spec.md | 36 +-- openspec/specs/queries/measure-naming/spec.md | 13 +- .../queries/partitioned-aggregates/spec.md | 68 ++--- openspec/specs/queries/semantics/spec.md | 4 +- openspec/specs/queries/transforms/spec.md | 248 ++++++++++++++++-- 16 files changed, 302 insertions(+), 73 deletions(-) rename openspec/changes/{dev-2040-rank-family-ordering-required-direction-on-rankdense-rank => archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank}/.openspec.yaml (100%) rename openspec/changes/{dev-2040-rank-family-ordering-required-direction-on-rankdense-rank => archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank}/design.md (100%) rename openspec/changes/{dev-2040-rank-family-ordering-required-direction-on-rankdense-rank => archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank}/proposal.md (100%) rename openspec/changes/{dev-2040-rank-family-ordering-required-direction-on-rankdense-rank => archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank}/specs/aggregations/functional-form/spec.md (100%) rename openspec/changes/{dev-2040-rank-family-ordering-required-direction-on-rankdense-rank => archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank}/specs/queries/computed-dimensions/spec.md (100%) rename openspec/changes/{dev-2040-rank-family-ordering-required-direction-on-rankdense-rank => archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank}/specs/queries/measure-naming/spec.md (100%) rename openspec/changes/{dev-2040-rank-family-ordering-required-direction-on-rankdense-rank => archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank}/specs/queries/partitioned-aggregates/spec.md (100%) rename openspec/changes/{dev-2040-rank-family-ordering-required-direction-on-rankdense-rank => archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank}/specs/queries/semantics/spec.md (100%) rename openspec/changes/{dev-2040-rank-family-ordering-required-direction-on-rankdense-rank => archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank}/specs/queries/transforms/spec.md (100%) rename openspec/changes/{dev-2040-rank-family-ordering-required-direction-on-rankdense-rank => archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank}/tasks.md (100%) diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/.openspec.yaml b/openspec/changes/archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/.openspec.yaml similarity index 100% rename from openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/.openspec.yaml rename to openspec/changes/archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/.openspec.yaml diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/design.md b/openspec/changes/archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/design.md similarity index 100% rename from openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/design.md rename to openspec/changes/archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/design.md diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/proposal.md b/openspec/changes/archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/proposal.md similarity index 100% rename from openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/proposal.md rename to openspec/changes/archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/proposal.md diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/aggregations/functional-form/spec.md b/openspec/changes/archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/aggregations/functional-form/spec.md similarity index 100% rename from openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/aggregations/functional-form/spec.md rename to openspec/changes/archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/aggregations/functional-form/spec.md diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/computed-dimensions/spec.md b/openspec/changes/archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/computed-dimensions/spec.md similarity index 100% rename from openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/computed-dimensions/spec.md rename to openspec/changes/archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/computed-dimensions/spec.md diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/measure-naming/spec.md b/openspec/changes/archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/measure-naming/spec.md similarity index 100% rename from openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/measure-naming/spec.md rename to openspec/changes/archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/measure-naming/spec.md diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/partitioned-aggregates/spec.md b/openspec/changes/archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/partitioned-aggregates/spec.md similarity index 100% rename from openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/partitioned-aggregates/spec.md rename to openspec/changes/archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/partitioned-aggregates/spec.md diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/semantics/spec.md b/openspec/changes/archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/semantics/spec.md similarity index 100% rename from openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/semantics/spec.md rename to openspec/changes/archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/semantics/spec.md diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/transforms/spec.md b/openspec/changes/archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/transforms/spec.md similarity index 100% rename from openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/transforms/spec.md rename to openspec/changes/archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/specs/queries/transforms/spec.md diff --git a/openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/tasks.md b/openspec/changes/archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/tasks.md similarity index 100% rename from openspec/changes/dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/tasks.md rename to openspec/changes/archive/2026-10-03-dev-2040-rank-family-ordering-required-direction-on-rankdense-rank/tasks.md diff --git a/openspec/specs/aggregations/functional-form/spec.md b/openspec/specs/aggregations/functional-form/spec.md index 51803612..1be00d90 100644 --- a/openspec/specs/aggregations/functional-form/spec.md +++ b/openspec/specs/aggregations/functional-form/spec.md @@ -271,9 +271,9 @@ parameter. #### Scenario: Positional transform parameter equals named - **WHEN** a measure is written - `customers.spend:weighted_avg(rank(sum(amount, partition_by=customers.regions.name)))` + `customers.spend:weighted_avg(rank(sum(amount, partition_by=customers.regions.name), direction='desc'))` rooted at `orders` -- **THEN** it binds to the identical aggregation identity as the `weight=rank(...)` +- **THEN** it binds to the identical aggregation identity as the `weight=rank(..., direction='desc')` spelling and returns identical result keys and values ### Requirement: Repeated keyword arguments are rejected @@ -283,7 +283,7 @@ parse-time error naming the call and the keyword; the parser never keeps the las occurrence and never concatenates the values. #### Scenario: Repeated partition_by on a transform -- **WHEN** a measure names `rank(sum(amount), partition_by=region, partition_by=city)` +- **WHEN** a measure names `rank(sum(amount), partition_by=region, partition_by=city, direction='desc')` - **THEN** parsing fails with an error naming `rank` and `partition_by` #### Scenario: Repeated keyword on an aggregation diff --git a/openspec/specs/queries/computed-dimensions/spec.md b/openspec/specs/queries/computed-dimensions/spec.md index 6d37d50a..31059bb3 100644 --- a/openspec/specs/queries/computed-dimensions/spec.md +++ b/openspec/specs/queries/computed-dimensions/spec.md @@ -21,34 +21,34 @@ Any measure-legal expression SHALL be legal as a computed dimension provided it - THEN rows group by the band with correct executed values and unchanged cardinality #### Scenario: Used as a transform partition -- WHEN a query declares the computed dimension `ureg` = `upper(region)` and selects `rank(sum(amount), partition_by=ureg)` +- WHEN a query declares the computed dimension `ureg` = `upper(region)` and selects `rank(sum(amount), partition_by=ureg, direction='desc')` - THEN the transform partitions by the dimension's value exactly as `sum(amount, partition_by=ureg)` would, in the measure, aggregation-parameter, filter, order and computed-dimension positions (values per `queries/partitioned-aggregates` › Transform partition keys bind like aggregate partition keys) ### Requirement: Transforms inside dimension expressions A transform inside a dimension expression SHALL evaluate at the union of its inner aggregates' effective grains — the grain of its containing context — unlike the same expression used as a measure, which evaluates at the query grain. An inner aggregate's effective grain is its declared `partition_by=` set, plus the query's active time bucket when the aggregate is windowed (`window=`); a `first`/`last` inner aggregate contributes its declared partition set only. Each inner aggregate is computed at its own effective grain and broadcast to the union-grain rows; when all inner aggregates share one grain the union degenerates to that grain (behavior unchanged). The rule is recursive: a nested transform evaluates at the union of its OWN inner aggregates' grains and its result is broadcast into the containing union like any other grained value. Keyword references on the transform (e.g. an explicit `partition_by=`) SHALL resolve against the union grain. A time-ordered transform (e.g. `cumsum`, `lag`, `time_shift`) inside a dimension expression SHALL fail with a clear error when its evaluation grain does not contain its time-ordering key — never duplicated result rows. When a windowed inner aggregate contributes the active time bucket, that synthesized bucket IS the query's bucketed time dimension — one dimension for all grain purposes (union membership, deduplication, attachment keys) — and a mixed-grain transform with a windowed inner aggregate but no resolvable time dimension SHALL fail with the same time-resolution error as windowed measures; single-grain windowed and `first`/`last` transform inputs remain legal. #### Scenario: Rank of partitions as a bandable dimension -- WHEN a query declares the dimension `rank(revenue:sum(partition_by=region))` +- WHEN a query declares the dimension `rank(revenue:sum(partition_by=region), direction='desc')` - THEN each row carries its region's rank among all regions by total revenue, and grouping or banding by that rank is legal and correct #### Scenario: Context grain distinguishes dimension use from measure use -- WHEN `rank(revenue:sum(partition_by=region))` is used once as a dimension and once as a measure in otherwise identical queries +- WHEN `rank(revenue:sum(partition_by=region), direction='desc')` is used once as a dimension and once as a measure in otherwise identical queries - THEN the dimension form ranks regions at region grain while the measure form ranks result rows at query grain #### Scenario: Different grains in one transform union and broadcast -- WHEN a dimension expression applies a transform over an arithmetic of two aggregates at different partition grains (e.g. `rank(a:sum(partition_by=region) - b:sum(partition_by=city))`) +- WHEN a dimension expression applies a transform over an arithmetic of two aggregates at different partition grains (e.g. `rank(a:sum(partition_by=region) - b:sum(partition_by=city), direction='desc')`) - THEN each aggregate is computed at its own declared grain, both are broadcast to the (region, city) union rows, the transform evaluates over exactly those rows, and executed values are correct #### Scenario: Keyless grain in a mixed transform -- WHEN a dimension expression ranks a share-of-total, e.g. `rank(amount:sum(partition_by=region) / amount:sum(partition_by=[]))` +- WHEN a dimension expression ranks a share-of-total, e.g. `rank(amount:sum(partition_by=region) / amount:sum(partition_by=[]), direction='desc')` - THEN the overall total broadcasts to every region row, the ratio and rank evaluate per region, and executed values are correct #### Scenario: A subset grain computes at its own grain -- WHEN a mixed-grain transform combines an aggregate at the union grain with one at a strictly coarser grain (e.g. `rank(a:sum(partition_by=[region, city]) - a:sum(partition_by=region))`) +- WHEN a mixed-grain transform combines an aggregate at the union grain with one at a strictly coarser grain (e.g. `rank(a:sum(partition_by=[region, city]) - a:sum(partition_by=region), direction='desc')`) - THEN the union-grain aggregate is computed directly at the union grain while the coarser one is computed at its own grain and broadcast, and executed values are correct #### Scenario: Nested transform evaluates at its own grain -- WHEN a mixed-grain transform contains a nested transform over a strictly coarser grain (e.g. `rank(cumsum(a:sum(partition_by=[region, ordered_at])) - b:sum(partition_by=city))`) +- WHEN a mixed-grain transform contains a nested transform over a strictly coarser grain (e.g. `rank(cumsum(a:sum(partition_by=[region, ordered_at])) - b:sum(partition_by=city), direction='desc')`) - THEN the inner transform evaluates over its own grain's rows (the cumulative sum accumulates across that grain's time buckets, not across union rows) before broadcasting into the union, and executed values are correct #### Scenario: Temporal transform without its time axis in the grain fails cleanly @@ -56,7 +56,7 @@ A transform inside a dimension expression SHALL evaluate at the union of its inn - THEN the query fails with a clear error directing the author to include the time key in `partition_by`, and never returns duplicated rows #### Scenario: Explicit transform partition over union rows -- WHEN a mixed-grain transform declares `partition_by=` naming a key of the union grain (e.g. `rank(a:sum(partition_by=region) - b:sum(partition_by=city), partition_by=region)`) +- WHEN a mixed-grain transform declares `partition_by=` naming a key of the union grain (e.g. `rank(a:sum(partition_by=region) - b:sum(partition_by=city), partition_by=region, direction='desc')`) - THEN the transform partitions the union-grain rows by the declared key, and executed values are correct #### Scenario: Transform keyword outside the union grain fails cleanly @@ -136,7 +136,7 @@ A grain-self-contained computed dimension (one whose aggregates all carry explic - THEN the query executes with correct values for both #### Scenario: Transform-root dimension with a transform measure -- WHEN a query groups by `rank(amount:sum(partition_by=city))` as a dimension and selects a transform measure +- WHEN a query groups by `rank(amount:sum(partition_by=city), direction='desc')` as a dimension and selects a transform measure - THEN the producer-grain rank and the query-grain transform are both correct in one result #### Scenario: Alongside a partitioned measure @@ -193,7 +193,7 @@ An aggregation-derived dimension (banded, bare partitioned aggregate, or transfo - THEN the query executes with correct values for both #### Scenario: Transform-root dimension with a bare windowed or ranked measure -- WHEN a query groups by `rank(amount:sum(partition_by=city))` as a dimension and selects a bare windowed or bare `first`/`last` measure +- WHEN a query groups by `rank(amount:sum(partition_by=city), direction='desc')` as a dimension and selects a bare windowed or bare `first`/`last` measure - THEN the producer-grain rank and the measure are both correct in one result #### Scenario: Adding a bare windowed or ranked measure is cardinality-neutral @@ -279,7 +279,7 @@ A row regroup attach (computed dimension), a partitioned-aggregate combined atta ### Requirement: An aggregate expression shared by a computed dimension and another position evaluates per position When the same explicitly grained aggregate expression — a partitioned aggregate, a re-aggregation, a transform over them, or a cross-model re-aggregation — appears inside a computed dimension AND in another position of the same query (measure, measure-typed filter conjunct, order target), each occurrence SHALL evaluate as that position defines it: inside the dimension at row scope, broadcast onto the rows its grain determines; elsewhere at query grain (Axiom 13). The shared expression SHALL be computed once (one producer) and the query SHALL execute with correct values or fail with a typed query error — never an internal placeholder, materialisation, hidden-slot, name-collision or join-back error. Filtering or ordering by the computed dimension's NAME uses its banded output; filtering or ordering by the underlying expression uses the expression's value. -Oracles below use the DEV-1847 `sales` fixture with `R` = `avg(sum(amount, partition_by=[city, region]), partition_by=region)` (North 45, South 70, East 60, Gap 10, Void NULL), `rlevel` = `CASE WHEN R > 50 THEN 'hi' ELSE 'lo' END`, `tlevel` = `CASE WHEN rank(R) > 1 THEN 'top' ELSE 'rest' END`, and `tot` = `amount:sum`. +Oracles below use the DEV-1847 `sales` fixture with `R` = `avg(sum(amount, partition_by=[city, region]), partition_by=region)` (North 45, South 70, East 60, Gap 10, Void NULL), `rlevel` = `CASE WHEN R > 50 THEN 'hi' ELSE 'lo' END`, `tlevel` = `CASE WHEN rank(R, direction='desc') > 1 THEN 'top' ELSE 'rest' END`, and `tot` = `amount:sum`. #### Scenario: Re-aggregation in a dimension and a measure-typed filter - WHEN a query over dimensions `[region, rlevel]` selects `tot` and filters on `R < amount:sum` @@ -295,19 +295,19 @@ Oracles below use the DEV-1847 `sales` fixture with `R` = `avg(sum(amount, parti #### Scenario: Transform over a re-aggregation in a dimension - WHEN a query over dimensions `[region, tlevel]` selects `tot` -- THEN South is `rest` and East, North, Gap and Void are `top` +- THEN South and Void (NULL rank) are `rest` and East, North and Gap are `top` #### Scenario: Transform over a re-aggregation in a dimension and elsewhere -- WHEN the `tlevel` query also selects `rank(R)` as a measure, or filters on `rank(R) > 1` or `rank(R) < 4`, or orders by `rank(R)` ascending -- THEN the rank values are South 1, East 2, North 3, Gap 4, Void 5 on every supported dialect; `rank(R) > 1` keeps East, North, Gap, Void; `rank(R) < 4` keeps South, East, North; and the ascending order is South, East, North, Gap, Void +- WHEN the `tlevel` query also selects `rank(R, direction='desc')` as a measure, or filters on `rank(R, direction='desc') > 1` or `rank(R, direction='desc') < 4`, or orders by `rank(R, direction='desc')` ascending +- THEN the rank values are South 1, East 2, North 3, Gap 4, Void NULL on every supported dialect; `rank(R, direction='desc') > 1` keeps East, North, Gap; `rank(R, direction='desc') < 4` keeps South, East, North; and the ascending order is South, East, North, Gap, with Void's NULL rank sorting per the dialect's NULL ordering (last outside T-SQL) #### Scenario: Two dimensions sharing a re-aggregation - WHEN a query declares both `tlevel` and `rlevel` as dimensions and selects `tot` and `R` -- THEN the rows are South (rest, hi), East (top, hi), North (top, lo), Gap (top, lo), Void (top, lo) with `tot` unchanged, and no internal name reaches the user +- THEN the rows are South (rest, hi), East (top, hi), North (top, lo), Gap (top, lo), Void (rest, lo) with `tot` unchanged, and no internal name reaches the user #### Scenario: Ordering by a transform shared with a dimension -- WHEN a computed dimension is `CASE WHEN rank(amount:sum(partition_by=region)) > 1 THEN 'top' ELSE 'rest' END` and the query orders by `rank(amount:sum(partition_by=region))` ascending -- THEN rows arrive East, South, North, Gap, Void, and ordering by the dimension's name instead sorts by its banded value +- WHEN a computed dimension is `CASE WHEN rank(amount:sum(partition_by=region), direction='desc') > 1 THEN 'top' ELSE 'rest' END` and the query orders by `rank(amount:sum(partition_by=region), direction='desc')` ascending +- THEN rows arrive East, South, North, Gap, with Void's NULL rank sorting per the dialect's NULL ordering (last outside T-SQL), and ordering by the dimension's name instead sorts by its banded value #### Scenario: Windowed transform over a re-aggregation in a dimension with a filter - WHEN a monthly query declares the dimension `cumsum(min(, partition_by=region))` and filters on that dimension @@ -335,7 +335,7 @@ Oracles use the DEV-1847 `sales` fixture with `P` = `amount:sum(partition_by=reg - **THEN** the first arrives East, South, North, Gap, Void and the second South, East, North, Gap, Void #### Scenario: Arithmetic over a transform dimension as an order target -- **WHEN** a query over dimensions `[region, rk]`, with `rk` = `rank(P)`, orders by `rank(P) + 1` descending +- **WHEN** a query over dimensions `[region, rk]`, with `rk` = `rank(P, direction='desc')`, orders by `rank(P, direction='desc') + 1` descending - **THEN** rows arrive in descending `rk` order #### Scenario: A finer-grained aggregate dimension read as a measure diff --git a/openspec/specs/queries/measure-naming/spec.md b/openspec/specs/queries/measure-naming/spec.md index 4dc3d2d0..a7fa48e0 100644 --- a/openspec/specs/queries/measure-naming/spec.md +++ b/openspec/specs/queries/measure-naming/spec.md @@ -16,7 +16,9 @@ identifier under the product-wide expression-name convention: lowercase, every run of non-alphanumeric characters collapsed to one `_`, leading/trailing `_` stripped, a leading digit guarded, names over 48 characters folded to `__`, and no `__` in the result. The SQL projection alias -SHALL use the same derived name. +SHALL use the same derived name. In the canonical formula text a rank-family +`direction` SHALL appear as its bare normalised value (`asc` / `desc`), never as +`direction=...`. #### Scenario: Arithmetic composite @@ -28,6 +30,15 @@ SHALL use the same derived name. - **WHEN** an unnamed measure `time_shift(cmrr_eop:sum, -1, 'year')` is queried on model `mart` - **THEN** its result key is `mart.time_shift_cmrr_eop_sum_1_year` +#### Scenario: Rank direction spelled as its bare value + +- **WHEN** the unnamed measures `rank(sum(a), direction='desc')`, + `rank(sum(a), direction='Ascending')` and + `rank(sum(a), partition_by=r, direction='asc')` are queried on model `o` +- **THEN** their result keys are `o.rank_a_sum_desc`, `o.rank_a_sum_asc` and + `o.rank_a_sum_partition_by_r_asc`, while `ntile(sum(a), n=4)` keeps + `o.ntile_a_sum_n_4` + #### Scenario: Formatting-insensitive derivation - **WHEN** the same formula is written with different spacing (`logo_churn:sum/logo_bop:sum`) diff --git a/openspec/specs/queries/partitioned-aggregates/spec.md b/openspec/specs/queries/partitioned-aggregates/spec.md index aae20dca..81ad5612 100644 --- a/openspec/specs/queries/partitioned-aggregates/spec.md +++ b/openspec/specs/queries/partitioned-aggregates/spec.md @@ -35,7 +35,7 @@ A transform SHALL accept a partitioned aggregate as its input when used as a mea - THEN each row's value is the cumulative sum across months, within the row's non-time dimensions, of the attached region-month totals, verified by executed values #### Scenario: Ranking result rows by an attached total -- WHEN a query selects the measure `rank(revenue:sum(partition_by=region))` +- WHEN a query selects the measure `rank(revenue:sum(partition_by=region), direction='desc')` - THEN result rows are ranked by their attached region total at the query grain #### Scenario: Change over a partitioned aggregate executes @@ -155,7 +155,7 @@ An arithmetic expression combining aggregates at different declared partition gr - THEN each row's value is its query-grain total minus its broadcast region total, by executed values #### Scenario: Transform over mixed-grain arithmetic as a measure -- WHEN a query selects the measure `rank(a:sum(partition_by=region) - b:sum(partition_by=city))` +- WHEN a query selects the measure `rank(a:sum(partition_by=region) - b:sum(partition_by=city), direction='desc')` - THEN result rows are ranked at the query grain by the broadcast difference, and adding the measure changes no other column's values #### Scenario: Filter over mixed-grain arithmetic @@ -373,13 +373,13 @@ measure-local `filter=` on the outer aggregation. #### Scenario: Windowed inner under a transform constituent fails closed - **WHEN** a query over a month time dimension selects - `sum(rank(amount:sum(window='90d', partition_by=region)))` + `sum(rank(amount:sum(window='90d', partition_by=region), direction='desc'))` - **THEN** it no longer fails with the windowed time-dimension error — it executes per the next scenario; the former fail-closed pin is retired #### Scenario: Windowed inner under a transform constituent executes - **WHEN** a query over a month time dimension selects - `sum(rank(amount:sum(window='90d', partition_by=region)))` + `sum(rank(amount:sum(window='90d', partition_by=region), direction='desc'))` - **THEN** it executes with hand-computed values on SQLite and DuckDB — exactly one result row per bucket, every value non-NULL — the plan carries exactly one nested producer for the windowed inner grained by the query's active bucket, that exact @@ -389,7 +389,7 @@ measure-local `filter=` on the outer aggregation. #### Scenario: A pure re-aggregation counts operand cells, not base rows - **WHEN** a query over a month time dimension selects - `sum(rank(amount:sum(window='90d', partition_by=region)))` over a source with several + `sum(rank(amount:sum(window='90d', partition_by=region), direction='desc'))` over a source with several base rows per (region, month) cell - **THEN** the outer aggregation counts each operand cell once — its home is the operand dataset (Axiom 2.4), so the producer joins at the query grain as a second-order @@ -447,7 +447,7 @@ measure-local `filter=` on the outer aggregation. error, exactly as the source's constituents are exempt #### Scenario: Transform over a re-aggregated value -- **WHEN** a query selects `rank(avg(sum(amount, partition_by=[city, region])))` +- **WHEN** a query selects `rank(avg(sum(amount, partition_by=[city, region])), direction='desc')` - **THEN** result rows are ranked at the query grain by the attached re-aggregated value, and no other column's values change @@ -492,7 +492,7 @@ measure-local `filter=` on the outer aggregation. #### Scenario: Transform-valued outer parameter rides the carrier - **WHEN** a query over `[region]` selects - `weighted_avg(sum(amount, partition_by=[city, region]), weight=rank(count(id, partition_by=[city, region])))` + `weighted_avg(sum(amount, partition_by=[city, region]), weight=rank(count(id, partition_by=[city, region]), direction='desc'))` - **THEN** the rank of each city cell's row count (across all cells: Alpha/North 1; Alpha/South, NULL/Gap and Xi/Void 2; every other cell 5) is a constituent of the operand carrier, and each region carries the rank-weighted average of its city @@ -500,7 +500,7 @@ measure-local `filter=` on the outer aggregation. values on SQLite and DuckDB, the emitted SQL scope-closed #### Scenario: Transform-valued outer parameter outside the operand grain fails closed -- **WHEN** the outer parameter is `rank(count(id, partition_by=product))` — a grain +- **WHEN** the outer parameter is `rank(count(id, partition_by=product), direction='desc')` — a grain `[product]` the operand grain `[city, region]` does not determine - **THEN** the query fails at plan time with the typed determination error naming the parameter and the grain, never a scope leak or a value @@ -679,7 +679,7 @@ its `partition_by=` still gets the outer attach the grain join needs. #### Scenario: Transform constituent inside a mixed source - **WHEN** a query over `[region]` selects - `sum(quantity * rank(avg(unit_price, partition_by=product)))` + `sum(quantity * rank(avg(unit_price, partition_by=product), direction='desc'))` - **THEN** the transform is exactly one nested producer at its `(product)` grain, row-attached into the outer aggregation's input relation, and each region carries the hand-computed row-weighted value, by executed values — never the former @@ -950,7 +950,7 @@ SHALL be legal in measure, filter (typing as a measure) and ORDER BY positions. #### Scenario: Ranked transform as the attached parameter - **WHEN** a query rooted at `orders` selects - `customers.spend:weighted_avg(weight=rank(sum(amount, partition_by=customers.regions.name)))` + `customers.spend:weighted_avg(weight=rank(sum(amount, partition_by=customers.regions.name), direction='desc'))` — the region cells ranked by their order-amount total, the NULL-name region forming its own ranked cell - **THEN** the transform is the attached input at its result grain, and the query @@ -1043,7 +1043,7 @@ SHALL be legal in measure, filter (typing as a measure) and ORDER BY positions. #### Scenario: Positional transform parameter folds onto the declared name - **WHEN** a query rooted at `orders` over `[customers.tier]` selects - `customers.spend:weighted_avg(rank(sum(amount, partition_by=customers.regions.name)))` + `customers.spend:weighted_avg(rank(sum(amount, partition_by=customers.regions.name), direction='desc'))` - **THEN** it binds to the same aggregation identity as the `weight=` spelling and returns identical result keys and values (gold 475 / 8, silver 97.5, bronze 40) @@ -1056,13 +1056,13 @@ SHALL be legal in measure, filter (typing as a measure) and ORDER BY positions. #### Scenario: Local-root transform parameter - **WHEN** a query rooted at `sales` selects - `weighted_avg(amount, weight=rank(sum(amount, partition_by=region)))` by `region`, + `weighted_avg(amount, weight=rank(sum(amount, partition_by=region), direction='desc'))` by `region`, by `[region, city]` with the inner grained by `city`, and with no dimensions - **THEN** it executes with the row-attached rank of the row's cell — by region North 22.5, South 140 / 3, East 60, Gap 20 / 3, Void NULL; by region and city Alpha/North 10, Beta/North 60, Alpha/South 20, Gamma/South 100, Delta/East 50, Epsilon/East 50, - Zeta/East 80, NULL/Gap 6, Kappa/Gap 8, Xi/Void NULL; globally 810 / 43, the - NULL-total Void cell ranking last (weight 5) on SQLite and DuckDB alike + Zeta/East 80, NULL/Gap 6, Kappa/Gap 8, Xi/Void NULL; globally 810 / 33, the + NULL-total Void cell carrying a NULL rank and so no weight, on SQLite and DuckDB alike #### Scenario: Collapsing transform parameter drops the axis - **WHEN** a query rooted at `orders` over an `ordered_at` month time dimension selects @@ -1091,7 +1091,7 @@ SHALL be legal in measure, filter (typing as a measure) and ORDER BY positions. #### Scenario: Transform over an ungrained inner types at the query grain - **WHEN** a query rooted at `orders` selects - `customers.spend:weighted_avg(weight=rank(sum(amount)))` + `customers.spend:weighted_avg(weight=rank(sum(amount), direction='desc'))` - **THEN** by `customers.tier` the inner is grained at `[customers.tier]` and the query executes in every mode with no warning (gold 61.25, silver 115, bronze 40, NULL tier NULL); by `status` the query fails in every mode with the typed determination error @@ -1099,7 +1099,7 @@ SHALL be legal in measure, filter (typing as a measure) and ORDER BY positions. #### Scenario: Windowed inner joins the bucket to the parameter's grain - **WHEN** a query rooted at `orders` over an `ordered_at` month time dimension selects - `customers.spend:weighted_avg(weight=rank(sum(amount, window='1y', partition_by=customers.regions.name)))` + `customers.spend:weighted_avg(weight=rank(sum(amount, window='1y', partition_by=customers.regions.name), direction='desc'))` - **THEN** it fails in every mode with the typed determination error naming `weight` — the order-month bucket is in the transform's grain and `customers` does not determine it @@ -1107,7 +1107,7 @@ SHALL be legal in measure, filter (typing as a measure) and ORDER BY positions. #### Scenario: Windowed aggregation with a transform parameter - **WHEN** a query rooted at `orders` over a `customers.signup_at` month time dimension selects - `customers.spend:weighted_avg(window='1y', weight=rank(sum(amount, partition_by=customers.regions.name)))` + `customers.spend:weighted_avg(window='1y', weight=rank(sum(amount, partition_by=customers.regions.name), direction='desc'))` - **THEN** each signup-month bucket carries the trailing-window weighted average over the customers signed up in the window, each weighted by its region's rank — 100, 125, 510 / 7, 945 / 14, the NULL bucket NULL — identical under every mode with no @@ -1127,7 +1127,7 @@ SHALL be legal in measure, filter (typing as a measure) and ORDER BY positions. #### Scenario: Nested transform parameter inside an attached aggregate parameter - **WHEN** a query rooted at `orders` selects - `customers.spend:weighted_avg(weight=weighted_avg(amount, weight=rank(sum(amount, partition_by=customers.regions.name)), partition_by=customers.regions.name))` + `customers.spend:weighted_avg(weight=weighted_avg(amount, weight=rank(sum(amount, partition_by=customers.regions.name), direction='desc'), partition_by=customers.regions.name))` - **THEN** each level resolves bottom-up — the innermost rank over the region cells, the middle weighted average per region (North 50 / 3, South 10, NULL 23.5), the outer over customers — and the query executes: 7556.67 / 103.5 broadcast to both @@ -1136,7 +1136,7 @@ SHALL be legal in measure, filter (typing as a measure) and ORDER BY positions. #### Scenario: Cross-model transform parameter on a local root - **WHEN** a query rooted at `orders` selects - `amount:weighted_avg(weight=rank(sum(customers.spend, partition_by=customers.regions.name)))` + `amount:weighted_avg(weight=rank(sum(customers.spend, partition_by=customers.regions.name), direction='desc'))` - **THEN** the parameter's producer is rooted at `customers` grouped by region (spend North 280, South 195, NULL 40 → ranks 1, 2, 3) and attached per order row, the orphan order taking the NULL cell; the query executes in every mode with no warning — @@ -1144,7 +1144,7 @@ SHALL be legal in measure, filter (typing as a measure) and ORDER BY positions. #### Scenario: Transform parameter with its own partition_by outside the query dimensions - **WHEN** a query rooted at `orders` selects - `customers.spend:weighted_avg(weight=rank(sum(amount, partition_by=[customers.regions.name, customers.tier]), partition_by=customers.regions.name))` + `customers.spend:weighted_avg(weight=rank(sum(amount, partition_by=[customers.regions.name, customers.tier]), partition_by=customers.regions.name, direction='desc'))` with no dimensions, and by `customers.tier` - **THEN** the transform's own partition key is exempt from the combined-consumer partition-key rule exactly as a source constituent's is, and the query executes in @@ -1170,43 +1170,43 @@ Values below are on the sales graph with `ureg` declared as the computed dimensi `upper(region)` and `spend_band` banding `sum(amount, partition_by=[city, region]) > 45`. #### Scenario: Measure position -- **WHEN** a query over `[ureg, city]` selects `rank(sum(amount), partition_by=ureg)` -- **THEN** each city is ranked by its total within its upper-cased region — EAST: Zeta 1, Delta 2, Epsilon 2; NORTH: Beta 1, Alpha 2; SOUTH: Gamma 1, Alpha 2; GAP: the NULL city 1, Kappa 2; VOID: Xi 1 — and the bound partition grain equals that of `sum(amount, partition_by=ureg)` +- **WHEN** a query over `[ureg, city]` selects `rank(sum(amount), partition_by=ureg, direction='desc')` +- **THEN** each city is ranked by its total within its upper-cased region — EAST: Zeta 1, Delta 2, Epsilon 2; NORTH: Beta 1, Alpha 2; SOUTH: Gamma 1, Alpha 2; GAP: the NULL city 1, Kappa 2; VOID: Xi NULL (its total is NULL) — and the bound partition grain equals that of `sum(amount, partition_by=ureg)` #### Scenario: Aggregation-parameter position -- **WHEN** a query over `[ureg]` selects `weighted_avg(amount, weight=rank(sum(amount, partition_by=[ureg, city]), partition_by=ureg))` +- **WHEN** a query over `[ureg]` selects `weighted_avg(amount, weight=rank(sum(amount, partition_by=[ureg, city]), partition_by=ureg, direction='desc'))` - **THEN** each region's rows are weighted by their city's rank within the region: EAST 56, NORTH 120/7, SOUTH 36, GAP 7, VOID NULL #### Scenario: Filter position -- **WHEN** a query over `[ureg, city]` filters `rank(sum(amount), partition_by=ureg) <= 1` -- **THEN** exactly the rank-1 rows survive: EAST/Zeta, NORTH/Beta, SOUTH/Gamma, GAP/NULL, VOID/Xi +- **WHEN** a query over `[ureg, city]` filters `rank(sum(amount), partition_by=ureg, direction='desc') <= 1` +- **THEN** exactly the rank-1 rows survive: EAST/Zeta, NORTH/Beta, SOUTH/Gamma, GAP/NULL (VOID/Xi's NULL rank fails the predicate) #### Scenario: Order position -- **WHEN** a query over `[ureg, city]` orders by `rank(sum(amount), partition_by=ureg)` ascending -- **THEN** the five rank-1 rows precede every rank-2 row +- **WHEN** a query over `[ureg, city]` orders by `rank(sum(amount), partition_by=ureg, direction='desc')` ascending +- **THEN** the four rank-1 rows precede every rank-2 row #### Scenario: Computed-dimension position with a member key -- **WHEN** a query declares `ureg` and a second computed dimension `rank(sum(amount, partition_by=[city, ureg]), partition_by=ureg)` named `r`, selecting `sum(amount)` -- **THEN** rows group by `(ureg, r)`: EAST r=1 80 and r=2 100, NORTH r=1 60 and r=2 30, SOUTH r=1 100 and r=2 40, GAP r=1 12 and r=2 8, VOID r=1 NULL +- **WHEN** a query declares `ureg` and a second computed dimension `rank(sum(amount, partition_by=[city, ureg]), partition_by=ureg, direction='desc')` named `r`, selecting `sum(amount)` +- **THEN** rows group by `(ureg, r)`: EAST r=1 80 and r=2 100, NORTH r=1 60 and r=2 30, SOUTH r=1 100 and r=2 40, GAP r=1 12 and r=2 8, VOID r=NULL NULL #### Scenario: Mixed list of a column and a computed dimension -- **WHEN** a measure names `rank(sum(amount), partition_by=[ureg, product])` +- **WHEN** a measure names `rank(sum(amount), partition_by=[ureg, product], direction='desc')` - **THEN** the bound partition grain is `{upper(region), product}`, identical to the grain `sum(amount, partition_by=[ureg, product])` binds #### Scenario: Non-column element names the construct -- **WHEN** a measure names `rank(sum(amount), partition_by=sum(amount))` or `sum(amount, partition_by=sum(amount))` +- **WHEN** a measure names `rank(sum(amount), partition_by=sum(amount), direction='desc')` or `sum(amount, partition_by=sum(amount))` - **THEN** binding fails with "transform 'rank' partition_by must resolve to a column reference; got AggregateKey." or "aggregation partition_by must resolve to a column reference; got AggregateKey." respectively #### Scenario: Undeclared name stays unknown -- **WHEN** a query over `[city]` (no `ureg` dimension) selects `rank(sum(amount), partition_by=ureg)` +- **WHEN** a query over `[city]` (no `ureg` dimension) selects `rank(sum(amount), partition_by=ureg, direction='desc')` - **THEN** binding fails with the unknown-reference error naming `ureg` #### Scenario: Attach-carrying computed dimension in filter and order positions -- **WHEN** a query over `[spend_band, city]` filters `rank(sum(amount), partition_by=spend_band) <= 1` +- **WHEN** a query over `[spend_band, city]` filters `rank(sum(amount), partition_by=spend_band, direction='desc') <= 1` - **THEN** exactly the rows hi/Gamma (100) and lo/Alpha (70) survive, and the same expression as an ascending order target sorts those two rows first #### Scenario: Attach-carrying computed dimension in measure and parameter positions fails closed -- **WHEN** a query over `[spend_band, city]` selects `rank(sum(amount), partition_by=spend_band)`, or a query over `[spend_band]` selects `weighted_avg(amount, weight=rank(sum(amount), partition_by=spend_band))` +- **WHEN** a query over `[spend_band, city]` selects `rank(sum(amount), partition_by=spend_band, direction='desc')`, or a query over `[spend_band]` selects `weighted_avg(amount, weight=rank(sum(amount), partition_by=spend_band, direction='desc'))` - **THEN** the key binds to the dimension's value and planning fails with a planner error, never an unknown-reference error (target behaviour, DEV-1960: both execute like `sum(amount, partition_by=spend_band)`) ### Requirement: Re-aggregation over ranked and windowed operands diff --git a/openspec/specs/queries/semantics/spec.md b/openspec/specs/queries/semantics/spec.md index 2eed1050..c93b29d0 100644 --- a/openspec/specs/queries/semantics/spec.md +++ b/openspec/specs/queries/semantics/spec.md @@ -429,7 +429,7 @@ column's values. #### Scenario: Ungrained inner of a mixed operand types at the query grain - **WHEN** a query over a month time dimension selects - `sum(rank(amount:sum(partition_by=[region, ordered_at]) - amount:sum))` + `sum(rank(amount:sum(partition_by=[region, ordered_at]) - amount:sum, direction='desc'))` - **THEN** the ungrained inner is the month total, computed at the query grain and broadcast onto the `(region, month)` cells before ranking — never re-evaluated per region — by hand-computed executed values distinguishable from the per-cell @@ -677,7 +677,7 @@ other column's values. #### Scenario: Transform constituent inside a mixed source - **WHEN** a query over dimensions `[region]` selects the measure - `sum(quantity * rank(avg(unit_price, partition_by=product)))` + `sum(quantity * rank(avg(unit_price, partition_by=product), direction='desc'))` - **THEN** each region row carries the sum over its base rows of `quantity` times the rank of the row's product among products by average unit price, by executed values, with unchanged cardinality diff --git a/openspec/specs/queries/transforms/spec.md b/openspec/specs/queries/transforms/spec.md index b638de10..0c6971d3 100644 --- a/openspec/specs/queries/transforms/spec.md +++ b/openspec/specs/queries/transforms/spec.md @@ -608,7 +608,7 @@ dimension-position rules are unchanged. #### Scenario: Rank family is covered - **WHEN** a query over `[store]` with a month time dimension selects the - measure `rank(qty)` + measure `rank(qty, direction='desc')` - **THEN** it fails with the same typed error, never a result carrying one row per (store, month, qty-value) @@ -628,7 +628,7 @@ dimension-position rules are unchanged. #### Scenario: A projected grain key stays legal - **WHEN** a query projects `weight` as a dimension and selects the measure - `rank(weight)` + `rank(weight, direction='desc')` - **THEN** it compiles at the query grain and executes with correct values — no error, no extra result rows @@ -653,7 +653,7 @@ dimension-position rules are unchanged. transform's row leaf #### Scenario: Projected grain key under a transform inside a source stays legal -- **WHEN** a query over `[region]` selects the measure `sum(rank(region))` +- **WHEN** a query over `[region]` selects the measure `sum(rank(region, direction='desc'))` - **THEN** it compiles: the transform types at the query grain and the aggregation is the degenerate identity with the degenerate-re-aggregation warning, never an error @@ -675,43 +675,43 @@ in dimension position keeps the existing grain-self-containment error; both take precedence over the membership error. #### Scenario: Non-member query dimension as a measure -- **WHEN** a query over `[city, region, product]` selects `rank(sum(amount, partition_by=[city, region]), partition_by=product)` +- **WHEN** a query over `[city, region, product]` selects `rank(sum(amount, partition_by=[city, region]), partition_by=product, direction='desc')` - **THEN** planning fails with an error naming `rank`, `product`, the operand grain `city, region` and the `partition_by=` remedy — it never executes by widening the grain #### Scenario: Non-member query dimension in a filter -- **WHEN** the same query filters `rank(sum(amount, partition_by=[city, region]), partition_by=product) <= 2` +- **WHEN** the same query filters `rank(sum(amount, partition_by=[city, region]), partition_by=product, direction='desc') <= 2` - **THEN** planning fails with the same error #### Scenario: Non-member key in dimension position, plain column -- **WHEN** a query over `[region]` declares the dimension `rank(sum(amount, partition_by=[city, product]), partition_by=region)` +- **WHEN** a query over `[region]` declares the dimension `rank(sum(amount, partition_by=[city, product]), partition_by=region, direction='desc')` - **THEN** planning fails with the same error naming `region` and the grain `city, product`, never with an internal producer-slot error #### Scenario: Non-member key in dimension position, computed-dimension name -- **WHEN** a query declares `ureg` = `upper(region)` and the dimension `rank(sum(amount, partition_by=[city, region]), partition_by=ureg)` +- **WHEN** a query declares `ureg` = `upper(region)` and the dimension `rank(sum(amount, partition_by=[city, region]), partition_by=ureg, direction='desc')` - **THEN** planning fails with the same error naming the key and the grain `city, region`, never with an internal error #### Scenario: Member key executes -- **WHEN** a query over `[city, region, product]` selects `rank(sum(amount, partition_by=[city, region]), partition_by=region)` -- **THEN** each row carries its (city, region) total's rank within the region: East Zeta 1, Delta 2, Epsilon 2; North Beta 1, Alpha 2; South Gamma 1, Alpha 2; Gap the NULL city 1, Kappa 2; Void Xi 1 +- **WHEN** a query over `[city, region, product]` selects `rank(sum(amount, partition_by=[city, region]), partition_by=region, direction='desc')` +- **THEN** each row carries its (city, region) total's rank within the region: East Zeta 1, Delta 2, Epsilon 2; North Beta 1, Alpha 2; South Gamma 1, Alpha 2; Gap the NULL city 1, Kappa 2; Void Xi NULL (its total is NULL) #### Scenario: Ungrained inner keeps the query-dimension rule -- **WHEN** a query over the banded dimension alone selects `rank(sum(amount), partition_by=region)` +- **WHEN** a query over the banded dimension alone selects `rank(sum(amount), partition_by=region, direction='desc')` - **THEN** planning fails with the existing "partition_by column 'region' is not a query dimension" error listing the available dimensions; over `[region, band]` the same measure executes because the ungrained inner is grained at the query grain and `region` is a member #### Scenario: Windowed inner admits the active bucket -- **WHEN** a monthly query selects `rank(sum(amount, window='1y', partition_by=customers.regions.name), partition_by=ordered_at)` +- **WHEN** a monthly query selects `rank(sum(amount, window='1y', partition_by=customers.regions.name), partition_by=ordered_at, direction='desc')` - **THEN** the partition key passes the operand-grain rule as the query's month bucket, a member contributed by the windowed inner #### Scenario: Nested collapsing transform drops its axis -- **WHEN** a monthly query selects `rank(last(sum(amount, partition_by=[customers.regions.name, ordered_at])), partition_by=ordered_at)` +- **WHEN** a monthly query selects `rank(last(sum(amount, partition_by=[customers.regions.name, ordered_at])), partition_by=ordered_at, direction='desc')` - **THEN** planning fails with the operand-grain error naming `ordered_at` and the grain `customers.regions.name`; with `partition_by=customers.regions.name` the key passes the rule #### Scenario: Aggregate-free input takes the query grain -- **WHEN** a query over `[city, region, product]` selects `rank(city, partition_by=region)` -- **THEN** it executes, ranking each row's city value descending within its region: East Zeta 1, Epsilon 2, Delta 3; North Beta 1, Alpha 2; South Gamma 1, Alpha 2; Gap Kappa 1, the NULL city 2; Void Xi 1 +- **WHEN** a query over `[city, region, product]` selects `rank(city, partition_by=region, direction='desc')` +- **THEN** it executes, ranking each row's city value descending within its region: East Zeta 1, Epsilon 2, Delta 3; North Beta 1, Alpha 2; South Gamma 1, Alpha 2; Gap Kappa 1, the NULL city NULL; Void Xi 1 #### Scenario: Residue error precedes the membership rule -- **WHEN** a query over `[region]` declares the dimension `rank(sum(amount), partition_by=region)` +- **WHEN** a query over `[region]` declares the dimension `rank(sum(amount), partition_by=region, direction='desc')` - **THEN** planning fails with the existing grain-self-containment error ("must declare partition_by= explicitly"), not the operand-grain error ### Requirement: consecutive_periods counts calendar periods @@ -909,3 +909,221 @@ When the query's active time bucket is a custom granularity (`queries/custom-gra - **WHEN** rows fall in sprints starting 2025-01-06, 2025-01-20 and 2025-02-17 (the 2025-02-03 sprint empty) and the query groups at `sprint` with `consecutive_periods(count(*) > 0)` - **THEN** the streaks are 1, 2, 1 + +### Requirement: Rank-family ordering direction + +`rank` and `dense_rank` SHALL take a required keyword argument `direction` whose +value is a string literal `asc`, `desc`, `ascending` or `descending` (any case, +surrounding whitespace ignored), normalised to `asc` / `desc`, and SHALL order +their window by the inner value ascending for `asc` and descending for `desc`. +`direction` SHALL combine with `partition_by=` and SHALL accept any orderable +inner, numeric or not. `ntile` and `percent_rank` SHALL reject `direction` and +SHALL always order ascending, so `ntile` bucket 1 holds the lowest values and a +higher value never gets a lower `percent_rank`. A missing, unrecognised, +non-literal or forbidden `direction` SHALL fail before any SQL runs with a +`TransformArgumentError` (a `QueryTypeError`), in every position (measure, +filter, order, computed dimension, aggregation parameter, saved `ModelMeasure`); +the missing-direction message SHALL show both +`direction='asc'` (lowest first) and `direction='desc'` (highest first). The +importer formula validator SHALL apply the identical rule with the identical +error. `rank(x, direction='asc')` and `rank(x, direction='desc')` SHALL be +distinct values that never deduplicate into one. + +Values below use the DEV-1847 `sales` fixture, whose region totals are North +90, South 140, East 180, Gap 20 and Void NULL. + +#### Scenario: Ascending rank puts the lowest value first + +- **WHEN** a query over `[region]` selects `rank(sum(amount), direction='asc')` +- **THEN** the window orders the inner ascending and the ranks are Gap 1, North 2, + South 3, East 4, Void NULL on SQLite and DuckDB + +#### Scenario: Descending rank puts the highest value first + +- **WHEN** a query over `[region]` selects `rank(sum(amount), direction='desc')` +- **THEN** the window orders the inner descending and the ranks are East 1, South 2, + North 3, Gap 4, Void NULL + +#### Scenario: dense_rank takes the same direction + +- **WHEN** a query over `[region]` selects `dense_rank(sum(amount), direction='asc')` +- **THEN** the ranks are Gap 1, North 2, South 3, East 4, Void NULL + +#### Scenario: Direction synonyms normalise + +- **WHEN** a query selects `rank(sum(amount), direction=' Descending ')` +- **THEN** it binds to the identical value as `rank(sum(amount), direction='desc')` + +#### Scenario: Non-numeric inner ranks ascending + +- **WHEN** a query over `[region]` selects `rank(min(city), direction='asc')` +- **THEN** the ranks are North 1 and South 1 (both `Alpha`), East 3 (`Delta`), Gap 4 + (`Kappa`), Void 5 (`Xi`) + +#### Scenario: Direction combines with partition_by + +- **WHEN** a query over `[region, city]` selects + `rank(sum(amount), partition_by=region, direction='asc')` +- **THEN** each city ranks lowest-first within its region: East Delta 1, Epsilon 1, + Zeta 3; North Alpha 1, Beta 2; South Alpha 1, Gamma 2; Gap Kappa 1, the NULL city + 2; Void Xi NULL + +#### Scenario: Both directions in one query stay distinct + +- **WHEN** one query selects both `rank(sum(amount), direction='asc')` and + `rank(sum(amount), direction='desc')` unnamed +- **THEN** the result carries two columns with the ascending and descending ranks + above, never one deduplicated column + +#### Scenario: Missing direction fails naming both spellings + +- **WHEN** a query selects, filters on, or orders by `rank(sum(amount))` or + `dense_rank(sum(amount), partition_by=region)`, or queries a saved + `ModelMeasure` whose formula is `rank(sum(amount))` +- **THEN** it fails with a `TransformArgumentError` naming the transform and showing + `direction='asc'` (lowest first) and `direction='desc'` (highest first), and no SQL + runs + +#### Scenario: Unrecognised or non-literal direction fails + +- **WHEN** a query selects `rank(sum(amount), direction='up')` or + `rank(sum(amount), direction=region)` +- **THEN** it fails with a `TransformArgumentError` listing the accepted values + +#### Scenario: ntile and percent_rank reject direction + +- **WHEN** a query selects `ntile(sum(amount), n=2, direction='desc')` or + `percent_rank(sum(amount), direction='asc')` +- **THEN** it fails with a `TransformArgumentError` stating that the transform always + orders ascending and takes no `direction` + +#### Scenario: ntile and percent_rank order ascending + +- **WHEN** a query over `[region]` selects `ntile(sum(amount), n=2)` and + `percent_rank(sum(amount))` +- **THEN** `ntile` is Gap 1, North 1, South 2, East 2, Void NULL and `percent_rank` is + Gap 0, North 1/3, South 2/3, East 1, Void NULL + +#### Scenario: Importer validation shares the rule + +- **WHEN** the importer formula validator parses `rank(sum(amount))`, + `rank(sum(amount), direction='sideways')` or `ntile(sum(amount), n=4, direction='asc')` +- **THEN** each fails with the same `TransformArgumentError` the query binder raises + for the same formula + +#### Scenario: Window ordering is pinned across dialects + +- **WHEN** `rank` with each direction, `dense_rank`, `ntile` and `percent_rank` are + rendered for postgres, sqlite, duckdb, tsql and bigquery +- **THEN** each window orders the inner by the stated direction (`ASC` for `ntile` / + `percent_rank`) and the generated SQL matches recorded golden baselines + +### Requirement: Rank-family NULL inputs rank NULL + +For `rank`, `dense_rank`, `ntile` and `percent_rank`, a row whose inner value is +NULL SHALL get a NULL result. NULL rows SHALL NOT take a rank position or an +`ntile` bucket, nor count in `percent_rank`'s denominator, within each partition; +the non-NULL rows SHALL rank exactly as if the NULL rows were absent. The result +SHALL be identical on every supported dialect, independent of the dialect's +native NULL ordering. + +#### Scenario: Mixed NULL and non-NULL inners + +- **WHEN** a query over `[region]` selects `rank(sum(amount), direction='desc')`, + `percent_rank(sum(amount))` and `ntile(sum(amount), n=2)` on the `sales` fixture +- **THEN** Void (NULL total) gets NULL for all three, and the other regions get the + values of the ordering-direction scenarios above, `percent_rank`'s denominator + counting four rows, on SQLite and DuckDB + +#### Scenario: An all-NULL partition ranks NULL without disturbing others + +- **WHEN** a query over `[region, city]` selects + `dense_rank(sum(amount), partition_by=region, direction='desc')` +- **THEN** Void Xi (NULL total) is NULL, and every other region's cities rank as they + would without Void + +#### Scenario: A NULL row inside a partition takes no position + +- **WHEN** a query over `[region, city]` selects + `rank(city, partition_by=region, direction='asc')` +- **THEN** Gap's NULL city is NULL and Kappa is 1; East Delta 1, Epsilon 2, Zeta 3 + +#### Scenario: A filter on rank drops NULL-ranked rows + +- **WHEN** a query over `[region]` filters `rank(sum(amount), direction='asc') <= 5` +- **THEN** East, Gap, North and South survive and Void does not + +#### Scenario: NULL handling does not depend on the dialect's NULL ordering + +- **WHEN** the rank family is rendered for tsql, whose native ordering puts NULLs + first on `ASC` +- **THEN** the emitted SQL nulls the result for a NULL inner and keeps NULL rows out of + the non-NULL rows' window, exactly as on postgres + +### Requirement: Stored rank calls without a direction load as descending + +A persisted model, query or memory whose stored schema version predates this +change SHALL load with every `rank(` / `dense_rank(` call lacking a top-level +`direction=` in its Mode-B fields rewritten to carry `direction='desc'`, +preserving its pre-change ordering. The Mode-B fields are `ModelMeasure.formula` +and a query's `measures`, `filters`, `dimensions`, `time_dimensions`, `order` and +`main_time_dimension`, including queries nested in `source_queries`, an inline +query `source_model`, the measures of an inline `ModelExtension` `source_model`, +and `Memory.query`. Mode-A SQL (`Column.sql`, model +`filters`, `Column.filter`, aggregation templates) and `ntile` / `percent_rank` +calls SHALL never be rewritten. The rewrite SHALL apply only to a payload read +from storage or one that declares an explicit schema version older than the +current one; a payload without a version, or at the current version, SHALL be +left as written, so a bare call in it fails with the missing-direction error. The +rewrite SHALL be idempotent, SHALL leave a formula it cannot tokenise +byte-identical, and a migrated model SHALL be persisted back at the current +version on first load. + +#### Scenario: A stored model measure keeps its descending meaning + +- **WHEN** a model stored at the previous version holds the measure + `rank(sum(amount))` and is loaded +- **THEN** the measure reads `rank(sum(amount), direction='desc')`, the stored document + is rewritten at the current version, and querying it returns the descending ranks + +#### Scenario: A stored query's every Mode-B field is rewritten + +- **WHEN** a stored query-backed model's `source_queries` entry holds bare + `rank(` / `dense_rank(` calls in a measure, a filter, an order item and a computed + dimension expression, nested inside other calls and colon syntax +- **THEN** every call gains `direction='desc'` and nothing else in the formulas changes + +#### Scenario: Unversioned legacy documents are rewritten + +- **WHEN** a stored model with no `version`, whose nested source query also has no + `version`, or a stored memory (YAML and SQLite) with no `version` or with an + unversioned `query`, holds bare `rank(` calls +- **THEN** they load with `direction='desc'` filled in + +#### Scenario: A fresh payload is never filled in + +- **WHEN** a query or model with no `version`, or at the current version, is + submitted through the API, MCP or Python with a bare `rank(sum(amount))` +- **THEN** it fails with the missing-direction `TransformArgumentError` + +#### Scenario: A payload declaring an old version is treated as legacy + +- **WHEN** a query submitted with an explicit older `version` holds `rank(sum(amount))` +- **THEN** it is filled in with `direction='desc'`, exactly as a stored document would be + +#### Scenario: Calls that need no rewrite are untouched + +- **WHEN** a stored document holds `rank(sum(amount), direction='asc')`, the text + `rank(` inside a string literal, an attribute call `x.rank(`, `ntile(sum(amount), n=4)`, + `percent_rank(sum(amount))`, or `dense_rank() over (order by id)` in a `Column.sql` +- **THEN** each is left byte-identical + +#### Scenario: The rewrite is idempotent + +- **WHEN** an already-migrated document is migrated again +- **THEN** it is unchanged + +#### Scenario: An untokenisable formula still loads + +- **WHEN** a stored model's measure formula cannot be tokenised +- **THEN** the formula is left byte-identical and the model still loads