diff --git a/CHANGELOG.md b/CHANGELOG.md index 684fed8..b931a10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ Notable changes to this project, following [Keep a Changelog](https://keepachang ## [Unreleased] +### Fixed + +- A window function using a named window (`sum(salary) over w ... window w as (order by id)`) is keyed by the function instead of by `over w`. Only the inline `over (...)` form was recognized, so the entry fell to the bare-alias split and the window reference became the alias ([#301](https://github.com/tiagolauer/OwlSQL/issues/301)). + ## [0.2.0] - 2026-07-29 ### Changed diff --git a/src/parse.ts b/src/parse.ts index 8f3ee9f..d8c2f79 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -412,13 +412,27 @@ type FindOverKeyword = type StripLeadingOpenParen = Trim extends `(${infer Rest}` ? Rest : never; -type SplitWindowExpression = FindOverKeyword extends { - expr: infer Expr extends string; - rest: infer Rest extends string; -} +// The [never] guard is load-bearing now that the paren is no longer required: +// FindOverKeyword resolves to never for an entry with no OVER at all, and +// `never extends { expr: infer E extends string; rest: infer R extends string }` +// passes with both infers falling back to `string` - which the named-window +// branch below would then accept as a window name. +type SplitWindowExpression = [FindOverKeyword] extends [never] + ? never + : FindOverKeyword extends { + expr: infer Expr extends string; + rest: infer Rest extends string; + } ? StripLeadingOpenParen extends infer AfterOpen extends string ? [AfterOpen] extends [never] - ? never + ? // `over w`, referring to a window declared in a WINDOW clause, is the + // other half of the syntax: what follows OVER is a name rather than an + // inline definition. Requiring the paren meant the entry was not + // recognized as a window expression at all, so it fell to the bare-alias + // split and `over w` became the alias of `sum(salary)` (issue #301). + Trim extends '' + ? never + : { expr: Expr; after: Trim>> } : ExtractParenGroup extends { rest: infer AfterClose extends string } ? { expr: Expr; after: Trim } : never diff --git a/tests/window.test-d.ts b/tests/window.test-d.ts index 1ab4146..404f537 100644 --- a/tests/window.test-d.ts +++ b/tests/window.test-d.ts @@ -39,9 +39,37 @@ type WindowFunctionAlongsideRegularColumn = Expect< > >; +// Regression for #301: `over w` refers to a window declared in a WINDOW +// clause, the other half of the syntax. Requiring a paren after OVER meant the +// entry was not read as a window expression at all, so it fell to the +// bare-alias split and `over w` became the alias of `sum(salary)`. +type NamedWindowKeepsTheFunctionName = Expect< + Equal< + Query, + { sum: number }[] + > +>; + +type NamedWindowWithAlias = Expect< + Equal< + Query, + { total: number }[] + > +>; + +type NamedWindowAlongsideRegularColumn = Expect< + Equal< + Query, + { dept: string; rank: number }[] + > +>; + export type WindowLock = [ RowNumberWithAlias, RankWithoutAliasDefaultsToFunctionName, EmptyOverClause, WindowFunctionAlongsideRegularColumn, + NamedWindowKeepsTheFunctionName, + NamedWindowWithAlias, + NamedWindowAlongsideRegularColumn, ];