Skip to content

feat(ir): canonicalize SQL ROW_NUMBER partitioned top-k (#24) - #93

Merged
zzylol merged 1 commit into
mainfrom
feat/24-sql-partitioned-topk
Jul 5, 2026
Merged

zzylol merged 1 commit into
mainfrom
feat/24-sql-partitioned-topk

Conversation

@zzylol

@zzylol zzylol commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Closes #24.

State of the issue

The "unsupported subquery (inline view / derived table)" error #24 originally reported is already gone — derived-table + WindowFunc support landed since it was filed, so S8/S9 lower. What remained was the second half of the ask: recognition of the ROW_NUMBER() OVER (PARTITION BY …) pattern. Without it, S8/S9 produced the raw Filter(rn ≤ k) over WindowFunc(RowNumber …) shape — structurally unlike the PromQL topk by (…) they mirror.

Fix

Add one rewrite to the shared canonicalize pass (built in #34): recognise the ROW_NUMBER-partitioned top-k idiom and rewrite it to the generic partitioned Limit{k}{ Sort{ o, partition_by: p } }. The pass applies its rules to a fixpoint, so the existing heavy-hitter rule then composes on top and promotes the count-ranked case:

SQL canonical L3 PromQL twin
S8 — ROW_NUMBER() … ORDER BY COUNT(*) DESC, WHERE rn ≤ 5 Aggregate{ by:[region], [TopK{5}], child: Aggregate([Count], by:[service,region]) } P10 topk by (…) (5, count_over_time(…))
S9 — … ORDER BY AVG(latency) DESC Limit{5}{ Sort{ partition_by:[region] }{ Aggregate([Avg]) } } P9 topk by (…) (5, rate(…))

So a SQL ROW_NUMBER top-k and the PromQL topk by it mirrors now converge on the same canonical IR.

The match is conservative: it fires only when the filtered column is exactly the ROW_NUMBER window output — the single column the WindowFunc appends, verified against the inner relation's schema — ranked with <= k. Any other predicate/column/window function is left untouched.

Tests

  • asap-l2 unit tests: count-ranked ROW_NUMBER → partitioned heavy-hitter; avg-ranked → partitioned Sort+Limit; a filter on a non-row-number column is left as a Filter.
  • crates/lower/tests/cross_language.rs: end-to-end via lower_sql — S8 reaches the same heavy-hitter shape as PromQL P10, S9 the same generic partitioned form as P9.

Full workspace suite green; clippy clean.

The "unsupported subquery" error #24 reported is already gone (derived-table +
WindowFunc support landed), so S8/S9 lowered — but only to the raw
`Filter(rn <= k) over WindowFunc(RowNumber PARTITION BY p ORDER BY o)` shape,
structurally unlike the PromQL `topk by (…)` they mirror.

Add a `canonicalize` rewrite (issue #34's pass) that recognises the
`ROW_NUMBER()`-partitioned top-k idiom and rewrites it to the generic
partitioned `Limit{k}{ Sort{ o, partition_by: p } }`. Because the pass runs
its rules to a fixpoint, the existing heavy-hitter rule then promotes the
count-ranked case to an `Aggregate([TopK])`, so:

- S8 (ROW_NUMBER … ORDER BY COUNT(*) DESC) → `Aggregate{ by: [region],
  [TopK{k}], child: Aggregate([Count], by: [service, region]) }` — the same
  partitioned heavy-hitter shape as PromQL P10.
- S9 (… ORDER BY AVG(latency) DESC) → `Limit{k}{ Sort{ partition_by: [region] }
  { Aggregate([Avg]) } }` — the generic partitioned form, mirroring PromQL P9.

The match is conservative: it fires only when the filtered column is exactly
the `ROW_NUMBER` window output (the appended last column, verified via the
inner schema) ranked with `<= k`; any other predicate/column is left alone.

Tests:
- `asap-l2` unit tests: count case → partitioned heavy-hitter; avg case →
  partitioned Sort+Limit; a filter on a non-row-number column is untouched.
- `crates/lower/tests/cross_language.rs`: end-to-end, S8 reaches the same
  heavy-hitter shape as PromQL P10 and S9 the same generic partitioned form
  as P9.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@zzylol
zzylol merged commit b0c21d0 into main Jul 5, 2026
1 check passed
@zzylol
zzylol deleted the feat/24-sql-partitioned-topk branch July 5, 2026 14:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SQL: partitioned topk (top-k per group via window functions) is unsupported

1 participant