@simplysm/orm-common 14.0.4 → 14.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,216 +2,219 @@
2
2
 
3
3
  Dialect-independent SQL expression builder. Generates JSON AST (`Expr`) instead of SQL strings. The QueryBuilder transforms the AST to target DBMS syntax.
4
4
 
5
- ## expr
6
-
7
- Constant object with all expression builder methods. Used inside Queryable callbacks for `where()`, `select()`, `orderBy()`, etc.
8
-
9
- ### Value Generation
10
-
11
- | Method | Signature | Description |
12
- |---|---|---|
13
- | `val` | `(dataType: ColumnPrimitiveStr, value) => ExprUnit` | Wrap literal value as ExprUnit |
14
- | `col` | `(dataType, ...path: string[]) => ExprUnit` | Generate column reference (internal use) |
15
- | `raw` | `(dataType) => tagged template => ExprUnit` | Raw SQL expression (escape hatch). Interpolated values are auto-parameterized. |
16
-
17
- **Example:**
5
+ ## ExprUnit
18
6
 
19
7
  ```typescript
20
- expr.val("string", "active")
21
- expr.val("number", 100)
22
- expr.val("DateOnly", DateOnly.today())
23
- expr.raw("string")`JSON_EXTRACT(${u.metadata}, '$.email')`
8
+ class ExprUnit<TPrimitive extends ColumnPrimitive> {
9
+ readonly $infer!: TPrimitive;
10
+ readonly dataType: ColumnPrimitiveStr;
11
+ readonly expr: Expr;
12
+ get n(): ExprUnit<NonNullable<TPrimitive>>;
13
+ constructor(dataType: ColumnPrimitiveStr, expr: Expr);
14
+ }
24
15
  ```
25
16
 
26
- ### Comparison Operators (WHERE)
27
-
28
- | Method | Signature | Description |
29
- |---|---|---|
30
- | `eq` | `(source, target) => WhereExprUnit` | Equality (NULL-safe: MySQL `<=>`) |
31
- | `gt` | `(source, target) => WhereExprUnit` | Greater than (`>`) |
32
- | `lt` | `(source, target) => WhereExprUnit` | Less than (`<`) |
33
- | `gte` | `(source, target) => WhereExprUnit` | Greater than or equal (`>=`) |
34
- | `lte` | `(source, target) => WhereExprUnit` | Less than or equal (`<=`) |
35
- | `between` | `(source, from?, to?) => WhereExprUnit` | Range (BETWEEN). Undefined bounds are unbounded. |
36
-
37
- ### NULL Check
38
-
39
- | Method | Signature | Description |
40
- |---|---|---|
41
- | `null` | `(source) => WhereExprUnit` | IS NULL check |
17
+ Type-safe expression wrapper. Tracks the return type via the `TPrimitive` generic parameter.
42
18
 
43
- ### String Search
19
+ | Member | Type | Description |
20
+ |--------|------|-------------|
21
+ | `$infer` | `TPrimitive` | Type inference helper (phantom type) |
22
+ | `dataType` | `ColumnPrimitiveStr` | Runtime type name (`"string"`, `"number"`, etc.) |
23
+ | `expr` | `Expr` | Underlying JSON AST expression |
24
+ | `n` | `ExprUnit<NonNullable<TPrimitive>>` | Getter that narrows nullable to non-nullable |
44
25
 
45
- | Method | Signature | Description |
46
- |---|---|---|
47
- | `like` | `(source, pattern) => WhereExprUnit` | LIKE pattern matching (`%`, `_` wildcards) |
48
- | `regexp` | `(source, pattern) => WhereExprUnit` | Regular expression matching |
26
+ ## WhereExprUnit
49
27
 
50
- ### IN / EXISTS
28
+ ```typescript
29
+ class WhereExprUnit {
30
+ readonly expr: WhereExpr;
31
+ constructor(expr: WhereExpr);
32
+ }
33
+ ```
51
34
 
52
- | Method | Signature | Description |
53
- |---|---|---|
54
- | `in` | `(source, values[]) => WhereExprUnit` | IN value list |
55
- | `inQuery` | `(source, query: Queryable) => WhereExprUnit` | IN subquery (single-column SELECT) |
56
- | `exists` | `(query: Queryable) => WhereExprUnit` | EXISTS subquery |
35
+ WHERE clause expression wrapper. Returned by comparison and logical methods on `expr`.
57
36
 
58
- ### Logical Operators
37
+ ## ExprInput
59
38
 
60
- | Method | Signature | Description |
61
- |---|---|---|
62
- | `not` | `(arg: WhereExprUnit) => WhereExprUnit` | NOT |
63
- | `and` | `(conditions: WhereExprUnit[]) => WhereExprUnit` | AND (all must match) |
64
- | `or` | `(conditions: WhereExprUnit[]) => WhereExprUnit` | OR (any must match) |
39
+ ```typescript
40
+ type ExprInput<TPrimitive extends ColumnPrimitive> = ExprUnit<TPrimitive> | TPrimitive;
41
+ ```
65
42
 
66
- ### String Functions (SELECT)
43
+ Accepts either an `ExprUnit` or a literal value. Literal values are automatically wrapped.
67
44
 
68
- | Method | Signature | Description |
69
- |---|---|---|
70
- | `concat` | `(...args) => ExprUnit<string>` | String concatenation (CONCAT) |
71
- | `left` | `(source, length) => ExprUnit` | Left N characters |
72
- | `right` | `(source, length) => ExprUnit` | Right N characters |
73
- | `trim` | `(source) => ExprUnit` | Trim whitespace |
74
- | `padStart` | `(source, length, fillString) => ExprUnit` | Left padding (LPAD) |
75
- | `replace` | `(source, from, to) => ExprUnit` | String replacement |
76
- | `upper` | `(source) => ExprUnit` | Uppercase |
77
- | `lower` | `(source) => ExprUnit` | Lowercase |
78
- | `length` | `(source) => ExprUnit<number>` | Character length |
79
- | `byteLength` | `(source) => ExprUnit<number>` | Byte length |
80
- | `substring` | `(source, start, length?) => ExprUnit` | Substring (1-based index) |
81
- | `indexOf` | `(source, search) => ExprUnit<number>` | Find position (1-based, 0 if not found) |
82
-
83
- ### Number Functions (SELECT)
45
+ ## SwitchExprBuilder
84
46
 
85
- | Method | Signature | Description |
86
- |---|---|---|
87
- | `abs` | `(source) => ExprUnit` | Absolute value |
88
- | `round` | `(source, digits) => ExprUnit` | Round |
89
- | `ceil` | `(source) => ExprUnit` | Ceiling |
90
- | `floor` | `(source) => ExprUnit` | Floor |
47
+ ```typescript
48
+ interface SwitchExprBuilder<TPrimitive extends ColumnPrimitive> {
49
+ case(condition: WhereExprUnit, then: ExprInput<TPrimitive>): SwitchExprBuilder<TPrimitive>;
50
+ default(value: ExprInput<TPrimitive>): ExprUnit<TPrimitive>;
51
+ }
52
+ ```
91
53
 
92
- ### Date/Time Functions (SELECT)
54
+ Fluent CASE WHEN builder. Chain `.case()` calls and terminate with `.default()`.
93
55
 
94
- | Method | Signature | Description |
95
- |---|---|---|
96
- | `year` | `(source) => ExprUnit<number>` | Extract year |
97
- | `month` | `(source) => ExprUnit<number>` | Extract month |
98
- | `day` | `(source) => ExprUnit<number>` | Extract day |
99
- | `hour` | `(source) => ExprUnit<number>` | Extract hour |
100
- | `minute` | `(source) => ExprUnit<number>` | Extract minute |
101
- | `second` | `(source) => ExprUnit<number>` | Extract second |
102
- | `isoWeek` | `(source) => ExprUnit<number>` | ISO week number |
103
- | `isoWeekStartDate` | `(source) => ExprUnit<DateOnly>` | ISO week start date (Monday) |
104
- | `isoYearMonth` | `(source) => ExprUnit<DateOnly>` | ISO year-month (YYYYMM format) |
105
- | `dateDiff` | `(unit: DateUnit, from, to) => ExprUnit<number>` | Date difference |
106
- | `dateAdd` | `(unit: DateUnit, source, value) => ExprUnit` | Date arithmetic |
107
- | `formatDate` | `(source, format: string) => ExprUnit<string>` | Date formatting |
108
-
109
- ### Conditional
56
+ ## expr Object
110
57
 
111
- | Method | Signature | Description |
112
- |---|---|---|
113
- | `coalesce` | `(...args) => ExprUnit` | First non-null value (COALESCE) |
114
- | `nullIf` | `(source, value) => ExprUnit` | NULL if equal (NULLIF) |
115
- | `is` | `(condition: WhereExprUnit) => ExprUnit<boolean>` | Convert condition to boolean (0/1) |
116
- | `switch` | `() => SwitchExprBuilder` | CASE WHEN expression (chain `.case().default()`) |
117
- | `if` | `(condition, then, else?) => ExprUnit` | IIF conditional expression |
58
+ The `expr` object provides all expression-building methods. Methods are grouped by category below.
118
59
 
119
- ### Aggregate Functions
60
+ ### Value Creation
120
61
 
121
62
  | Method | Signature | Description |
122
- |---|---|---|
123
- | `count` | `(arg?, distinct?) => ExprUnit<number>` | COUNT |
124
- | `sum` | `(arg) => ExprUnit<number \| undefined>` | SUM |
125
- | `avg` | `(arg) => ExprUnit<number \| undefined>` | AVG |
126
- | `max` | `(arg) => ExprUnit<T \| undefined>` | MAX |
127
- | `min` | `(arg) => ExprUnit<T \| undefined>` | MIN |
63
+ |--------|-----------|-------------|
64
+ | `val` | `<TStr extends ColumnPrimitiveStr>(dataType: TStr, value: T) => ExprUnit` | Wrap a literal value into an ExprUnit |
65
+ | `col` | `<TStr extends ColumnPrimitiveStr>(dataType: ColumnPrimitiveStr, ...path: string[]) => ExprUnit` | Create a column reference |
66
+ | `raw` | `<T extends ColumnPrimitiveStr>(dataType: T) => (strings: TemplateStringsArray, ...values: ExprInput[]) => ExprUnit` | Raw SQL via tagged template literal; interpolated values become parameters |
128
67
 
129
- ### Utility
68
+ ### Comparison (WHERE)
130
69
 
131
- | Method | Signature | Description |
132
- |---|---|---|
133
- | `greatest` | `(...args) => ExprUnit` | Greatest of values |
134
- | `least` | `(...args) => ExprUnit` | Least of values |
135
- | `rowNum` | `() => ExprUnit<number>` | Simple row number |
136
- | `random` | `() => ExprUnit<number>` | Random number (RAND/RANDOM) |
137
- | `cast` | `(source, targetType: DataType) => ExprUnit` | Type cast |
138
- | `subquery` | `(dataType, queryDef: SelectQueryDef) => ExprUnit` | Scalar subquery |
139
- | `toExpr` | `(value: ExprInput) => Expr` | Convert ExprInput to raw Expr AST |
70
+ | Method | Signature | SQL | Description |
71
+ |--------|-----------|-----|-------------|
72
+ | `eq` | `(source: ExprUnit<T>, target: ExprInput<T>) => WhereExprUnit` | `<=>` / `IS NULL OR =` | NULL-safe equality |
73
+ | `gt` | `(source: ExprUnit<T>, target: ExprInput<T>) => WhereExprUnit` | `>` | Greater than |
74
+ | `lt` | `(source: ExprUnit<T>, target: ExprInput<T>) => WhereExprUnit` | `<` | Less than |
75
+ | `gte` | `(source: ExprUnit<T>, target: ExprInput<T>) => WhereExprUnit` | `>=` | Greater than or equal |
76
+ | `lte` | `(source: ExprUnit<T>, target: ExprInput<T>) => WhereExprUnit` | `<=` | Less than or equal |
77
+ | `between` | `(source: ExprUnit<T>, from?: ExprInput<T>, to?: ExprInput<T>) => WhereExprUnit` | `BETWEEN` | Range comparison (undefined bounds omitted) |
140
78
 
141
- ### Window Functions
79
+ ### NULL Check (WHERE)
142
80
 
143
- All window functions accept a `WinSpecInput: { partitionBy?: ExprInput[]; orderBy?: [ExprInput, ("ASC" | "DESC")?][] }`.
81
+ | Method | Signature | SQL | Description |
82
+ |--------|-----------|-----|-------------|
83
+ | `null` | `(source: ExprUnit<T>) => WhereExprUnit` | `IS NULL` | NULL check |
144
84
 
145
- | Method | Signature | Description |
146
- |---|---|---|
147
- | `rowNumber` | `(spec) => ExprUnit<number>` | ROW_NUMBER() OVER(...) |
148
- | `rank` | `(spec) => ExprUnit<number>` | RANK() OVER(...) |
149
- | `denseRank` | `(spec) => ExprUnit<number>` | DENSE_RANK() OVER(...) |
150
- | `ntile` | `(n, spec) => ExprUnit<number>` | NTILE(n) OVER(...) |
151
- | `lag` | `(column, spec, offset?, defaultValue?) => ExprUnit` | Previous row value |
152
- | `lead` | `(column, spec, offset?, defaultValue?) => ExprUnit` | Next row value |
153
- | `firstValue` | `(column, spec) => ExprUnit` | First value in partition |
154
- | `lastValue` | `(column, spec) => ExprUnit` | Last value in partition |
155
- | `sumOver` | `(column, spec) => ExprUnit<number \| undefined>` | Window SUM |
156
- | `avgOver` | `(column, spec) => ExprUnit<number \| undefined>` | Window AVG |
157
- | `countOver` | `(spec, column?) => ExprUnit<number>` | Window COUNT |
158
- | `minOver` | `(column, spec) => ExprUnit` | Window MIN |
159
- | `maxOver` | `(column, spec) => ExprUnit` | Window MAX |
85
+ ### String Search (WHERE)
160
86
 
161
- ## SwitchExprBuilder
87
+ | Method | Signature | SQL | Description |
88
+ |--------|-----------|-----|-------------|
89
+ | `like` | `(source: ExprUnit<string\|undefined>, pattern: ExprInput<string\|undefined>) => WhereExprUnit` | `LIKE ... ESCAPE '\'` | Pattern matching |
90
+ | `regexp` | `(source: ExprUnit<string\|undefined>, pattern: ExprInput<string\|undefined>) => WhereExprUnit` | `REGEXP` | Regex matching |
162
91
 
163
- Builder interface for CASE WHEN expressions. Created by `expr.switch()`.
92
+ ### IN / EXISTS (WHERE)
164
93
 
165
- ```typescript
166
- interface SwitchExprBuilder<TPrimitive extends ColumnPrimitive> {
167
- case(condition: WhereExprUnit, then: ExprInput<TPrimitive>): SwitchExprBuilder<TPrimitive>;
168
- default(value: ExprInput<TPrimitive>): ExprUnit<TPrimitive>;
169
- }
170
- ```
94
+ | Method | Signature | SQL | Description |
95
+ |--------|-----------|-----|-------------|
96
+ | `in` | `(source: ExprUnit<T>, values: ExprInput<T>[]) => WhereExprUnit` | `IN (...)` | List membership |
97
+ | `inQuery` | `(source: ExprUnit<T>, query: Queryable<...>) => WhereExprUnit` | `IN (SELECT ...)` | Subquery membership (single column) |
98
+ | `exists` | `(query: Queryable<any, any>) => WhereExprUnit` | `EXISTS (SELECT ...)` | Subquery existence check |
171
99
 
172
- **Example:**
100
+ ### Logical (WHERE)
173
101
 
174
- ```typescript
175
- db.user().select((u) => ({
176
- tier: expr.switch<string>()
177
- .case(expr.gte(u.score, 90), "gold")
178
- .case(expr.gte(u.score, 70), "silver")
179
- .default("bronze"),
180
- }))
181
- ```
102
+ | Method | Signature | SQL | Description |
103
+ |--------|-----------|-----|-------------|
104
+ | `not` | `(arg: WhereExprUnit) => WhereExprUnit` | `NOT (...)` | Negate condition |
105
+ | `and` | `(conditions: WhereExprUnit[]) => WhereExprUnit` | `... AND ...` | All conditions must match |
106
+ | `or` | `(conditions: WhereExprUnit[]) => WhereExprUnit` | `... OR ...` | At least one condition must match |
182
107
 
183
- ## ExprUnit
108
+ ### String Functions (SELECT)
184
109
 
185
- Type-safe expression wrapper that tracks the return type through TypeScript generics.
110
+ | Method | Signature | SQL | Description |
111
+ |--------|-----------|-----|-------------|
112
+ | `concat` | `(...args: ExprInput<string\|undefined>[]) => ExprUnit<string>` | `CONCAT(...)` | String concatenation (NULL becomes empty) |
113
+ | `left` | `(source, length) => ExprUnit<T>` | `LEFT(source, n)` | Left substring |
114
+ | `right` | `(source, length) => ExprUnit<T>` | `RIGHT(source, n)` | Right substring |
115
+ | `trim` | `(source) => ExprUnit<T>` | `TRIM(source)` | Remove leading/trailing whitespace |
116
+ | `padStart` | `(source, length, fillString) => ExprUnit<T>` | `LPAD(...)` | Left-pad to target length |
117
+ | `replace` | `(source, from, to) => ExprUnit<T>` | `REPLACE(...)` | String replacement |
118
+ | `upper` | `(source) => ExprUnit<T>` | `UPPER(source)` | Uppercase |
119
+ | `lower` | `(source) => ExprUnit<T>` | `LOWER(source)` | Lowercase |
120
+ | `length` | `(source) => ExprUnit<number>` | `CHAR_LENGTH(source)` | Character count |
121
+ | `byteLength` | `(source) => ExprUnit<number>` | `OCTET_LENGTH(source)` | Byte count |
122
+ | `substring` | `(source, start, length?) => ExprUnit<T>` | `SUBSTRING(source, start, length)` | Extract substring (1-based index) |
123
+ | `indexOf` | `(source, search) => ExprUnit<number>` | `LOCATE(search, source)` | Find position (1-based, 0 if not found) |
124
+
125
+ ### Math Functions (SELECT)
126
+
127
+ | Method | Signature | SQL | Description |
128
+ |--------|-----------|-----|-------------|
129
+ | `abs` | `(source) => ExprUnit<T>` | `ABS(source)` | Absolute value |
130
+ | `round` | `(source, digits) => ExprUnit<T>` | `ROUND(source, digits)` | Round to N decimal places |
131
+ | `ceil` | `(source) => ExprUnit<T>` | `CEILING(source)` | Round up |
132
+ | `floor` | `(source) => ExprUnit<T>` | `FLOOR(source)` | Round down |
133
+
134
+ ### Date Functions (SELECT)
135
+
136
+ | Method | Signature | SQL | Description |
137
+ |--------|-----------|-----|-------------|
138
+ | `year` | `(source) => ExprUnit<number>` | `YEAR(source)` | Extract year (4-digit) |
139
+ | `month` | `(source) => ExprUnit<number>` | `MONTH(source)` | Extract month (1-12) |
140
+ | `day` | `(source) => ExprUnit<number>` | `DAY(source)` | Extract day (1-31) |
141
+ | `hour` | `(source) => ExprUnit<number>` | `HOUR(source)` | Extract hour (0-23) |
142
+ | `minute` | `(source) => ExprUnit<number>` | `MINUTE(source)` | Extract minute (0-59) |
143
+ | `second` | `(source) => ExprUnit<number>` | `SECOND(source)` | Extract second (0-59) |
144
+ | `isoWeek` | `(source) => ExprUnit<number>` | `WEEK(source, 3)` | ISO week number (1-53) |
145
+ | `isoWeekStartDate` | `(source) => ExprUnit<T>` | Computed | Monday of the source date's week |
146
+ | `isoYearMonth` | `(source) => ExprUnit<T>` | Computed | First day of the source date's month |
147
+ | `dateDiff` | `(unit: DateUnit, from, to) => ExprUnit<number>` | `DATEDIFF(unit, from, to)` | Date difference (to - from) |
148
+ | `dateAdd` | `(unit: DateUnit, source, value) => ExprUnit<T>` | `DATEADD(unit, value, source)` | Add time to date |
149
+ | `formatDate` | `(source, format: string) => ExprUnit<string>` | `DATE_FORMAT(...)` | Format date as string |
150
+
151
+ ### Conditional (SELECT)
152
+
153
+ | Method | Signature | SQL | Description |
154
+ |--------|-----------|-----|-------------|
155
+ | `coalesce` | `(...args) => ExprUnit<T>` | `COALESCE(...)` | First non-null value |
156
+ | `nullIf` | `(source, value) => ExprUnit<T\|undefined>` | `NULLIF(source, value)` | Return NULL if source equals value |
157
+ | `is` | `(condition: WhereExprUnit) => ExprUnit<boolean>` | Condition as boolean | Convert WHERE expression to boolean column |
158
+ | `switch` | `<T>() => SwitchExprBuilder<T>` | `CASE WHEN ... END` | Fluent CASE WHEN builder |
159
+ | `if` | `(condition, then, else_) => ExprUnit<T>` | `IF(...)` / `IIF(...)` | Ternary conditional |
160
+
161
+ ### Aggregate (SELECT)
162
+
163
+ | Method | Signature | SQL | Description |
164
+ |--------|-----------|-----|-------------|
165
+ | `count` | `(arg?, distinct?) => ExprUnit<number>` | `COUNT(...)` | Row count (all rows if arg omitted) |
166
+ | `sum` | `(arg) => ExprUnit<number\|undefined>` | `SUM(arg)` | Sum (NULL if all values NULL) |
167
+ | `avg` | `(arg) => ExprUnit<number\|undefined>` | `AVG(arg)` | Average (NULL if all values NULL) |
168
+ | `max` | `(arg) => ExprUnit<T\|undefined>` | `MAX(arg)` | Maximum value |
169
+ | `min` | `(arg) => ExprUnit<T\|undefined>` | `MIN(arg)` | Minimum value |
170
+
171
+ ### Other (SELECT)
172
+
173
+ | Method | Signature | SQL | Description |
174
+ |--------|-----------|-----|-------------|
175
+ | `greatest` | `(...args) => ExprUnit<T>` | `GREATEST(...)` | Greatest of multiple values |
176
+ | `least` | `(...args) => ExprUnit<T>` | `LEAST(...)` | Least of multiple values |
177
+ | `rowNum` | `() => ExprUnit<number>` | `ROW_NUMBER` variant | Simple row numbering |
178
+ | `random` | `() => ExprUnit<number>` | `RAND()` / `RANDOM()` | Random number (0 to 1) |
179
+ | `cast` | `(source, targetType: DataType) => ExprUnit` | `CAST(source AS type)` | Type conversion |
180
+ | `subquery` | `(dataType, queryable) => ExprUnit` | `(SELECT ...)` | Scalar subquery in SELECT |
181
+
182
+ ### Window Functions (SELECT)
183
+
184
+ | Method | Signature | SQL | Description |
185
+ |--------|-----------|-----|-------------|
186
+ | `rowNumber` | `(spec: WinSpecInput) => ExprUnit<number>` | `ROW_NUMBER() OVER (...)` | Row number within partition |
187
+ | `rank` | `(spec: WinSpecInput) => ExprUnit<number>` | `RANK() OVER (...)` | Rank (gaps after ties: 1,1,3) |
188
+ | `denseRank` | `(spec: WinSpecInput) => ExprUnit<number>` | `DENSE_RANK() OVER (...)` | Dense rank (no gaps: 1,1,2) |
189
+ | `ntile` | `(n: number, spec: WinSpecInput) => ExprUnit<number>` | `NTILE(n) OVER (...)` | Split into n groups |
190
+ | `lag` | `(column, spec, options?) => ExprUnit<T\|undefined>` | `LAG(...) OVER (...)` | Previous row value |
191
+ | `lead` | `(column, spec, options?) => ExprUnit<T\|undefined>` | `LEAD(...) OVER (...)` | Next row value |
192
+ | `firstValue` | `(column, spec) => ExprUnit<T\|undefined>` | `FIRST_VALUE(...) OVER (...)` | First value in partition |
193
+ | `lastValue` | `(column, spec) => ExprUnit<T\|undefined>` | `LAST_VALUE(...) OVER (...)` | Last value in partition |
194
+ | `sumOver` | `(column, spec) => ExprUnit<number\|undefined>` | `SUM(...) OVER (...)` | Window sum |
195
+ | `avgOver` | `(column, spec) => ExprUnit<number\|undefined>` | `AVG(...) OVER (...)` | Window average |
196
+ | `countOver` | `(spec, column?) => ExprUnit<number>` | `COUNT(...) OVER (...)` | Window count |
197
+ | `minOver` | `(column, spec) => ExprUnit<T\|undefined>` | `MIN(...) OVER (...)` | Window minimum |
198
+ | `maxOver` | `(column, spec) => ExprUnit<T\|undefined>` | `MAX(...) OVER (...)` | Window maximum |
199
+
200
+ **WinSpecInput** (window specification input):
186
201
 
187
202
  ```typescript
188
- class ExprUnit<TPrimitive extends ColumnPrimitive> {
189
- readonly $infer!: TPrimitive;
190
- readonly dataType: ColumnPrimitiveStr;
191
- readonly expr: Expr;
192
-
193
- /** Non-nullable assertion accessor */
194
- get n(): ExprUnit<NonNullable<TPrimitive>>;
195
-
196
- constructor(dataType: ColumnPrimitiveStr, expr: Expr);
203
+ interface WinSpecInput {
204
+ partitionBy?: ExprInput<ColumnPrimitive>[];
205
+ orderBy?: [ExprInput<ColumnPrimitive>, ("ASC" | "DESC")?][];
197
206
  }
198
207
  ```
199
208
 
200
- ## WhereExprUnit
209
+ **lag/lead options**:
201
210
 
202
- WHERE clause expression wrapper.
211
+ | Option | Type | Default | Description |
212
+ |--------|------|---------|-------------|
213
+ | `offset` | `number` | `1` | Number of rows to look back/forward |
214
+ | `default` | `ExprInput<T>` | `undefined` (NULL) | Default value when no row exists |
203
215
 
204
- ```typescript
205
- class WhereExprUnit {
206
- readonly expr: WhereExpr;
207
- constructor(expr: WhereExpr);
208
- }
209
- ```
210
-
211
- ## ExprInput
212
-
213
- Union type accepting either an `ExprUnit` or a literal value.
216
+ ### Helper
214
217
 
215
- ```typescript
216
- type ExprInput<TPrimitive extends ColumnPrimitive> = ExprUnit<TPrimitive> | TPrimitive;
217
- ```
218
+ | Method | Signature | Description |
219
+ |--------|-----------|-------------|
220
+ | `toExpr` | `(value: ExprInput<ColumnPrimitive>) => Expr` | Convert ExprInput to Expr JSON AST (internal use) |
@@ -4,123 +4,147 @@ Transforms `QueryDef` AST into dialect-specific SQL strings.
4
4
 
5
5
  ## createQueryBuilder
6
6
 
7
- Factory function that creates the appropriate `QueryBuilderBase` for a given dialect.
8
-
9
7
  ```typescript
10
- function createQueryBuilder(dialect: Dialect): QueryBuilderBase;
8
+ function createQueryBuilder(dialect: Dialect): QueryBuilderBase
11
9
  ```
12
10
 
13
- **Parameters:**
11
+ Factory function that creates a dialect-specific QueryBuilder instance.
14
12
 
15
13
  | Parameter | Type | Description |
16
- |---|---|---|
17
- | `dialect` | `"mysql" \| "mssql" \| "postgresql"` | Target database dialect |
14
+ |-----------|------|-------------|
15
+ | `dialect` | `"mysql" \| "mssql" \| "postgresql"` | Target DBMS dialect |
18
16
 
19
- **Returns:** `QueryBuilderBase` instance (`MysqlQueryBuilder`, `MssqlQueryBuilder`, or `PostgresqlQueryBuilder`)
17
+ Returns `MysqlQueryBuilder`, `MssqlQueryBuilder`, or `PostgresqlQueryBuilder`.
20
18
 
21
- **Example:**
19
+ ## QueryBuilderBase
22
20
 
23
21
  ```typescript
24
- const builder = createQueryBuilder("mysql");
25
- const result = builder.build(selectQueryDef);
26
- console.log(result.sql);
22
+ abstract class QueryBuilderBase {
23
+ build(def: QueryDef): QueryBuildResult;
24
+ }
27
25
  ```
28
26
 
29
- ## QueryBuilderBase
27
+ Abstract base class for rendering `QueryDef` AST to SQL. Implements common dispatch logic; dialect-specific differences are handled by abstract methods in subclasses.
30
28
 
31
- Abstract base class for QueryDef-to-SQL rendering. Implements dispatch logic shared across all dialects. Dialect-specific differences are delegated to abstract methods.
29
+ | Method | Signature | Description |
30
+ |--------|-----------|-------------|
31
+ | `build` | `(def: QueryDef) => QueryBuildResult` | Convert any QueryDef to SQL |
32
32
 
33
- ```typescript
34
- abstract class QueryBuilderBase {
35
- abstract readonly exprRenderer: ExprRendererBase;
33
+ `QueryBuildResult` contains:
36
34
 
37
- build(def: QueryDef): QueryBuildResult;
38
- }
35
+ | Field | Type | Description |
36
+ |-------|------|-------------|
37
+ | `sql` | `string` | Generated SQL string |
38
+ | `resultSetIndex` | `number?` | Result set index for multi-statement queries |
39
+ | `resultSetStride` | `number?` | Stride for extracting results from multi-statement queries |
40
+
41
+ ## Dialect-Specific Query Builders
42
+
43
+ ### MysqlQueryBuilder
44
+
45
+ ```typescript
46
+ class MysqlQueryBuilder extends QueryBuilderBase
39
47
  ```
40
48
 
41
- ### build
49
+ MySQL 8.0.14+ query builder. Handles MySQL-specific syntax such as backtick quoting, `<=>` for NULL-safe equality, `LIMIT ... OFFSET`, `LOAD DATA LOCAL INFILE`, etc.
42
50
 
43
- The main entry point. Accepts any `QueryDef` (SELECT, INSERT, UPDATE, DELETE, UPSERT, DDL, etc.) and returns a `QueryBuildResult` containing the SQL string and optional result set metadata.
51
+ ### MssqlQueryBuilder
44
52
 
45
53
  ```typescript
46
- build(def: QueryDef): QueryBuildResult;
54
+ class MssqlQueryBuilder extends QueryBuilderBase
47
55
  ```
48
56
 
49
- **Returns:** `QueryBuildResult` with:
57
+ MSSQL 2012+ query builder. Handles bracket quoting, `TOP`, `OFFSET ... FETCH NEXT`, `MERGE` for upsert, `SET IDENTITY_INSERT`, etc.
50
58
 
51
- | Field | Type | Description |
52
- |---|---|---|
53
- | `sql` | `string` | Generated SQL |
54
- | `resultSetIndex?` | `number` | Which result set to read (default: 0) |
55
- | `resultSetStride?` | `number` | For multi-result sets, read every Nth |
59
+ ### PostgresqlQueryBuilder
56
60
 
57
- ## ExprRendererBase
61
+ ```typescript
62
+ class PostgresqlQueryBuilder extends QueryBuilderBase
63
+ ```
58
64
 
59
- Abstract base class for rendering `Expr` and `WhereExpr` AST nodes to SQL strings. Each dialect implements its own renderer for DBMS-specific syntax.
65
+ PostgreSQL 9.0+ query builder. Handles double-quote quoting, `LIMIT ... OFFSET`, `ON CONFLICT` for upsert, `COPY FROM STDIN`, etc.
66
+
67
+ ## ExprRendererBase
60
68
 
61
69
  ```typescript
62
70
  abstract class ExprRendererBase {
63
- render(expr: Expr): string;
64
- renderWhere(expr: WhereExpr): string;
71
+ renderExpr(expr: Expr): string;
72
+ renderWhereExpr(expr: WhereExpr): string;
65
73
  }
66
74
  ```
67
75
 
68
- ## MysqlQueryBuilder
76
+ Abstract base class for rendering `Expr` and `WhereExpr` AST nodes to SQL fragments. Each QueryBuilder uses a corresponding ExprRenderer.
77
+
78
+ | Method | Signature | Description |
79
+ |--------|-----------|-------------|
80
+ | `renderExpr` | `(expr: Expr) => string` | Render a value/function expression to SQL |
81
+ | `renderWhereExpr` | `(expr: WhereExpr) => string` | Render a WHERE condition expression to SQL |
82
+
83
+ ## Dialect-Specific Expression Renderers
69
84
 
70
- MySQL-specific query builder. Extends `QueryBuilderBase`.
85
+ ### MysqlExprRenderer
71
86
 
72
87
  ```typescript
73
- class MysqlQueryBuilder extends QueryBuilderBase {
74
- readonly exprRenderer: MysqlExprRenderer;
75
- }
88
+ class MysqlExprRenderer extends ExprRendererBase
76
89
  ```
77
90
 
78
- MySQL-specific behaviors:
79
- - Uses `<=>` for NULL-safe equality
80
- - Uses backtick quoting for identifiers
81
- - INSERT OUTPUT via separate SELECT with `LAST_INSERT_ID()`
82
- - UPSERT via `INSERT ... ON DUPLICATE KEY UPDATE`
83
-
84
- ## MssqlQueryBuilder
91
+ MySQL expression renderer. Uses `<=>` for NULL-safe equality, `IFNULL`, `LOCATE`, MySQL `DATE_FORMAT` patterns, etc.
85
92
 
86
- MSSQL-specific query builder. Extends `QueryBuilderBase`.
93
+ ### MssqlExprRenderer
87
94
 
88
95
  ```typescript
89
- class MssqlQueryBuilder extends QueryBuilderBase {
90
- readonly exprRenderer: MssqlExprRenderer;
91
- }
96
+ class MssqlExprRenderer extends ExprRendererBase
92
97
  ```
93
98
 
94
- MSSQL-specific behaviors:
95
- - Uses `[` `]` for identifier quoting
96
- - TOP instead of LIMIT
97
- - OUTPUT clause for INSERT/UPDATE/DELETE
98
- - `IDENTITY_INSERT` for auto-increment override
99
- - `OFFSET...FETCH` for pagination
99
+ MSSQL expression renderer. Uses `IIF`, `CHARINDEX`, `FORMAT` for date formatting, `DATALENGTH` for byte length, etc.
100
+
101
+ ### PostgresqlExprRenderer
100
102
 
101
- ## PostgresqlQueryBuilder
103
+ ```typescript
104
+ class PostgresqlExprRenderer extends ExprRendererBase
105
+ ```
106
+
107
+ PostgreSQL expression renderer. Uses `IS NOT DISTINCT FROM` for NULL-safe equality, `POSITION`, `TO_CHAR` for date formatting, `OCTET_LENGTH`, etc.
102
108
 
103
- PostgreSQL-specific query builder. Extends `QueryBuilderBase`.
109
+ ## parseQueryResult
104
110
 
105
111
  ```typescript
106
- class PostgresqlQueryBuilder extends QueryBuilderBase {
107
- readonly exprRenderer: PostgresqlExprRenderer;
108
- }
112
+ async function parseQueryResult<TRecord>(
113
+ rawResults: Record<string, unknown>[],
114
+ meta: ResultMeta,
115
+ ): Promise<TRecord[] | undefined>
109
116
  ```
110
117
 
111
- PostgreSQL-specific behaviors:
112
- - Uses `"` for identifier quoting
113
- - `RETURNING` clause for INSERT/UPDATE/DELETE output
114
- - `LIMIT...OFFSET` for pagination
115
- - `LATERAL` subquery JOINs
116
- - `IS NOT DISTINCT FROM` for NULL-safe equality
118
+ Parses raw database query results into typed TypeScript objects. Handles:
117
119
 
118
- ## Dialect-Specific Expression Renderers
120
+ - **Type conversion**: Converts raw values (strings, numbers) to proper TypeScript types (`DateTime`, `DateOnly`, `Uuid`, etc.) based on `ResultMeta.columns`
121
+ - **JOIN nesting**: Flattens `"posts.id"` keys into nested `{ posts: { id: ... } }` objects
122
+ - **Grouping**: Groups rows by non-JOIN columns, collecting JOIN data into arrays (1:N) or single objects (1:1)
123
+ - **Event loop yielding**: Yields to the event loop every 100 records for large result sets
119
124
 
120
- | Class | Dialect | Description |
121
- |---|---|---|
122
- | `MysqlExprRenderer` | MySQL | Renders Expr AST to MySQL SQL syntax |
123
- | `MssqlExprRenderer` | MSSQL | Renders Expr AST to MSSQL SQL syntax |
124
- | `PostgresqlExprRenderer` | PostgreSQL | Renders Expr AST to PostgreSQL SQL syntax |
125
+ | Parameter | Type | Description |
126
+ |-----------|------|-------------|
127
+ | `rawResults` | `Record<string, unknown>[]` | Raw DB result rows |
128
+ | `meta` | `ResultMeta` | Column types and JOIN structure info |
129
+
130
+ Returns `undefined` if the input is empty or all records parse to empty objects.
125
131
 
126
- Each renderer extends `ExprRendererBase` and overrides methods for dialect-specific expression rendering (e.g., date functions, string functions, type casting).
132
+ ```typescript
133
+ // Simple type parsing
134
+ const raw = [{ id: "1", createdAt: "2026-01-07T10:00:00.000Z" }];
135
+ const meta = { columns: { id: "number", createdAt: "DateTime" }, joins: {} };
136
+ const result = await parseQueryResult(raw, meta);
137
+ // [{ id: 1, createdAt: DateTime(...) }]
138
+
139
+ // JOIN nesting
140
+ const raw = [
141
+ { id: 1, name: "User1", "posts.id": 10, "posts.title": "Post1" },
142
+ { id: 1, name: "User1", "posts.id": 11, "posts.title": "Post2" },
143
+ ];
144
+ const meta = {
145
+ columns: { id: "number", name: "string", "posts.id": "number", "posts.title": "string" },
146
+ joins: { posts: { isSingle: false } },
147
+ };
148
+ const result = await parseQueryResult(raw, meta);
149
+ // [{ id: 1, name: "User1", posts: [{ id: 10, title: "Post1" }, { id: 11, title: "Post2" }] }]
150
+ ```