@simplysm/orm-common 13.0.81 → 13.0.83
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.
- package/README.md +106 -0
- package/docs/db-context.md +238 -0
- package/docs/expressions.md +413 -0
- package/docs/query-builder.md +198 -0
- package/docs/queryable.md +420 -0
- package/docs/schema-builders.md +216 -0
- package/docs/types-and-utilities.md +353 -0
- package/package.json +4 -3
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
# Queryable
|
|
2
|
+
|
|
3
|
+
Chainable query builder for constructing type-safe SELECT, INSERT, UPDATE, DELETE, JOIN, GROUP BY, UNION, and recursive CTE queries.
|
|
4
|
+
|
|
5
|
+
## API Reference
|
|
6
|
+
|
|
7
|
+
### `Queryable<TData, TFrom>`
|
|
8
|
+
|
|
9
|
+
The core query builder class. Created automatically from DbContext table/view accessors (e.g., `db.user()`).
|
|
10
|
+
|
|
11
|
+
- `TData` -- Data type of the query result
|
|
12
|
+
- `TFrom` -- Source table builder (required for CUD operations; `never` for views/subqueries)
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
### SELECT / Projection
|
|
17
|
+
|
|
18
|
+
#### `.select(fn)`
|
|
19
|
+
|
|
20
|
+
Specify columns to SELECT with optional transformations.
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
select<R>(fn: (columns) => R): Queryable<UnwrapQueryableRecord<R>, never>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
db.user().select((u) => ({
|
|
28
|
+
userName: u.name,
|
|
29
|
+
upperEmail: expr.upper(u.email),
|
|
30
|
+
}))
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
#### `.distinct()`
|
|
34
|
+
|
|
35
|
+
Apply DISTINCT to remove duplicate rows.
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
db.user().select((u) => ({ name: u.name })).distinct()
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
#### `.lock()`
|
|
42
|
+
|
|
43
|
+
Apply row lock (`FOR UPDATE`) within a transaction.
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
db.user().where((u) => [expr.eq(u.id, 1)]).lock()
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
### Filtering
|
|
52
|
+
|
|
53
|
+
#### `.where(predicate)`
|
|
54
|
+
|
|
55
|
+
Add WHERE conditions. Multiple calls are combined with AND.
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
where(predicate: (columns) => WhereExprUnit[]): Queryable<TData, TFrom>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
db.user()
|
|
63
|
+
.where((u) => [expr.eq(u.status, "active")])
|
|
64
|
+
.where((u) => [expr.gte(u.age, 18)])
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
#### `.search(fn, searchText)`
|
|
68
|
+
|
|
69
|
+
Full-text search using LIKE patterns. Supports `+required`, `-excluded`, `"exact phrase"`, and `*wildcard` syntax.
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
db.user().search((u) => [u.name, u.email], 'John +active -deleted')
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
### Sorting and Pagination
|
|
78
|
+
|
|
79
|
+
#### `.orderBy(fn, direction?)`
|
|
80
|
+
|
|
81
|
+
Add ORDER BY. Multiple calls add additional sort columns.
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
db.user()
|
|
85
|
+
.orderBy((u) => u.name)
|
|
86
|
+
.orderBy((u) => u.createdAt, "DESC")
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### `.top(count)`
|
|
90
|
+
|
|
91
|
+
Select only top N rows. Can be used without ORDER BY.
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
db.user().top(10)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
#### `.limit(skip, take)`
|
|
98
|
+
|
|
99
|
+
Set LIMIT/OFFSET for pagination. Requires `orderBy()` first.
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
db.user()
|
|
103
|
+
.orderBy((u) => u.createdAt)
|
|
104
|
+
.limit(0, 20) // first 20 rows
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
### Grouping
|
|
110
|
+
|
|
111
|
+
#### `.groupBy(fn)`
|
|
112
|
+
|
|
113
|
+
Add GROUP BY clause.
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
db.order()
|
|
117
|
+
.select((o) => ({
|
|
118
|
+
userId: o.userId,
|
|
119
|
+
totalAmount: expr.sum(o.amount),
|
|
120
|
+
}))
|
|
121
|
+
.groupBy((o) => [o.userId])
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
#### `.having(predicate)`
|
|
125
|
+
|
|
126
|
+
Add HAVING clause (filter after GROUP BY).
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
db.order()
|
|
130
|
+
.select((o) => ({
|
|
131
|
+
userId: o.userId,
|
|
132
|
+
totalAmount: expr.sum(o.amount),
|
|
133
|
+
}))
|
|
134
|
+
.groupBy((o) => [o.userId])
|
|
135
|
+
.having((o) => [expr.gte(o.totalAmount, 10000)])
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
### JOIN
|
|
141
|
+
|
|
142
|
+
#### `.join(as, fn)` -- 1:N JOIN
|
|
143
|
+
|
|
144
|
+
LEFT OUTER JOIN that adds results as an array property.
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
db.user().join("posts", (qr, u) =>
|
|
148
|
+
qr.from(Post).where((p) => [expr.eq(p.authorId, u.id)])
|
|
149
|
+
)
|
|
150
|
+
// Result: { id, name, posts: [{ id, title }, ...] }
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
#### `.joinSingle(as, fn)` -- N:1 / 1:1 JOIN
|
|
154
|
+
|
|
155
|
+
LEFT OUTER JOIN that adds results as a single object property.
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
db.post().joinSingle("author", (qr, p) =>
|
|
159
|
+
qr.from(User).where((u) => [expr.eq(u.id, p.authorId)])
|
|
160
|
+
)
|
|
161
|
+
// Result: { id, title, author: { id, name } | undefined }
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
#### `.include(fn)` -- Automatic Relation JOIN
|
|
165
|
+
|
|
166
|
+
Automatically JOIN based on relations defined in `TableBuilder.relations()`.
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
// Single relation
|
|
170
|
+
db.post().include((p) => p.author)
|
|
171
|
+
|
|
172
|
+
// Nested relation
|
|
173
|
+
db.post().include((p) => p.author.company)
|
|
174
|
+
|
|
175
|
+
// Multiple relations (chain include calls)
|
|
176
|
+
db.user()
|
|
177
|
+
.include((u) => u.company)
|
|
178
|
+
.include((u) => u.posts)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
### Subquery and UNION
|
|
184
|
+
|
|
185
|
+
#### `.wrap()`
|
|
186
|
+
|
|
187
|
+
Wrap current Queryable as a subquery. Required before `count()` after `distinct()` or `groupBy()`.
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
const count = await db.user()
|
|
191
|
+
.select((u) => ({ name: u.name }))
|
|
192
|
+
.distinct()
|
|
193
|
+
.wrap()
|
|
194
|
+
.count();
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
#### `Queryable.union(...queries)`
|
|
198
|
+
|
|
199
|
+
Combine multiple Queryables with UNION ALL.
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
const combined = Queryable.union(
|
|
203
|
+
db.user().where((u) => [expr.eq(u.type, "admin")]),
|
|
204
|
+
db.user().where((u) => [expr.eq(u.type, "manager")]),
|
|
205
|
+
);
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
### Recursive CTE
|
|
211
|
+
|
|
212
|
+
#### `.recursive(fn)`
|
|
213
|
+
|
|
214
|
+
Build recursive Common Table Expressions for hierarchical data.
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
db.employee()
|
|
218
|
+
.where((e) => [expr.null(e.managerId)]) // base case: root nodes
|
|
219
|
+
.recursive((cte) =>
|
|
220
|
+
cte.from(Employee)
|
|
221
|
+
.where((e) => [expr.eq(e.managerId, e.self[0].id)])
|
|
222
|
+
)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
### Execution -- SELECT
|
|
228
|
+
|
|
229
|
+
#### `.execute()`
|
|
230
|
+
|
|
231
|
+
Execute SELECT and return result array.
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
const users: User[] = await db.user()
|
|
235
|
+
.where((u) => [expr.eq(u.isActive, true)])
|
|
236
|
+
.execute();
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
#### `.single()`
|
|
240
|
+
|
|
241
|
+
Return single result. Throws if more than one result.
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
const user = await db.user()
|
|
245
|
+
.where((u) => [expr.eq(u.id, 1)])
|
|
246
|
+
.single();
|
|
247
|
+
// returns User | undefined
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
#### `.first()`
|
|
251
|
+
|
|
252
|
+
Return first result (ignores additional results).
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
const latest = await db.user()
|
|
256
|
+
.orderBy((u) => u.createdAt, "DESC")
|
|
257
|
+
.first();
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
#### `.count(fn?)`
|
|
261
|
+
|
|
262
|
+
Return row count. Cannot be used directly after `distinct()` or `groupBy()` (use `wrap()` first).
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
const count = await db.user()
|
|
266
|
+
.where((u) => [expr.eq(u.isActive, true)])
|
|
267
|
+
.count();
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
#### `.exists()`
|
|
271
|
+
|
|
272
|
+
Check if any matching rows exist.
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
const hasAdmin = await db.user()
|
|
276
|
+
.where((u) => [expr.eq(u.role, "admin")])
|
|
277
|
+
.exists();
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
### Execution -- INSERT
|
|
283
|
+
|
|
284
|
+
#### `.insert(records, outputColumns?)`
|
|
285
|
+
|
|
286
|
+
Insert records. Automatically chunks into batches of 1000 (MSSQL limit).
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
// Simple insert
|
|
290
|
+
await db.user().insert([
|
|
291
|
+
{ name: "Alice", email: "alice@test.com" },
|
|
292
|
+
{ name: "Bob" },
|
|
293
|
+
]);
|
|
294
|
+
|
|
295
|
+
// Insert with output (get auto-generated IDs)
|
|
296
|
+
const inserted = await db.user().insert(
|
|
297
|
+
[{ name: "Alice" }],
|
|
298
|
+
["id"],
|
|
299
|
+
);
|
|
300
|
+
// inserted[0].id
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
#### `.insertIfNotExists(record, outputColumns?)`
|
|
304
|
+
|
|
305
|
+
Insert only if WHERE condition matches no rows.
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
await db.user()
|
|
309
|
+
.where((u) => [expr.eq(u.email, "test@test.com")])
|
|
310
|
+
.insertIfNotExists({ name: "Test", email: "test@test.com" });
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
#### `.insertInto(targetTable, outputColumns?)`
|
|
314
|
+
|
|
315
|
+
INSERT INTO ... SELECT (copy current query results into another table).
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
await db.user()
|
|
319
|
+
.select((u) => ({ name: u.name, createdAt: u.createdAt }))
|
|
320
|
+
.where((u) => [expr.eq(u.isArchived, false)])
|
|
321
|
+
.insertInto(ArchivedUser);
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
### Execution -- UPDATE
|
|
327
|
+
|
|
328
|
+
#### `.update(recordFn, outputColumns?)`
|
|
329
|
+
|
|
330
|
+
Update matching rows. The callback receives current column values for reference.
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
await db.user()
|
|
334
|
+
.where((u) => [expr.eq(u.id, 1)])
|
|
335
|
+
.update(() => ({
|
|
336
|
+
name: expr.val("string", "New Name"),
|
|
337
|
+
}));
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
### Execution -- DELETE
|
|
343
|
+
|
|
344
|
+
#### `.delete(outputColumns?)`
|
|
345
|
+
|
|
346
|
+
Delete matching rows.
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
await db.user()
|
|
350
|
+
.where((u) => [expr.eq(u.status, "deleted")])
|
|
351
|
+
.delete();
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
### Execution -- UPSERT
|
|
357
|
+
|
|
358
|
+
#### `.upsert(record, outputColumns?)`
|
|
359
|
+
|
|
360
|
+
INSERT or UPDATE (MERGE pattern). Requires a WHERE condition to check existence.
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
await db.user()
|
|
364
|
+
.where((u) => [expr.eq(u.email, "test@test.com")])
|
|
365
|
+
.upsert({
|
|
366
|
+
insert: { name: "Test", email: "test@test.com" },
|
|
367
|
+
update: { name: expr.val("string", "Updated") },
|
|
368
|
+
});
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
### QueryDef Accessors
|
|
374
|
+
|
|
375
|
+
Each execution method has a corresponding `get*QueryDef()` method that returns the raw `QueryDef` without executing it:
|
|
376
|
+
|
|
377
|
+
- `getSelectQueryDef()` -- Returns `SelectQueryDef`
|
|
378
|
+
- `getInsertQueryDef(records, outputColumns?)` -- Returns `InsertQueryDef`
|
|
379
|
+
- `getInsertIfNotExistsQueryDef(record, outputColumns?)` -- Returns `InsertIfNotExistsQueryDef`
|
|
380
|
+
- `getInsertIntoQueryDef(targetTable, outputColumns?)` -- Returns `InsertIntoQueryDef`
|
|
381
|
+
- `getResultMeta(outputColumns?)` -- Returns `ResultMeta` for type parsing
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
## Usage Examples
|
|
386
|
+
|
|
387
|
+
### Complex Query with Multiple Features
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
const result = await db.order()
|
|
391
|
+
.include((o) => o.customer)
|
|
392
|
+
.where((o) => [
|
|
393
|
+
expr.gte(o.createdAt, expr.val("DateTime", startDate)),
|
|
394
|
+
expr.eq(o.status, "completed"),
|
|
395
|
+
])
|
|
396
|
+
.select((o) => ({
|
|
397
|
+
orderId: o.id,
|
|
398
|
+
customerName: o.customer.name,
|
|
399
|
+
total: o.totalAmount,
|
|
400
|
+
}))
|
|
401
|
+
.orderBy((o) => o.total, "DESC")
|
|
402
|
+
.limit(0, 50)
|
|
403
|
+
.execute();
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Aggregation with GROUP BY
|
|
407
|
+
|
|
408
|
+
```typescript
|
|
409
|
+
const stats = await db.order()
|
|
410
|
+
.select((o) => ({
|
|
411
|
+
month: expr.month(o.createdAt),
|
|
412
|
+
year: expr.year(o.createdAt),
|
|
413
|
+
totalRevenue: expr.sum(o.amount),
|
|
414
|
+
orderCount: expr.count(),
|
|
415
|
+
}))
|
|
416
|
+
.groupBy((o) => [o.month, o.year])
|
|
417
|
+
.orderBy((o) => o.year)
|
|
418
|
+
.orderBy((o) => o.month)
|
|
419
|
+
.execute();
|
|
420
|
+
```
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# Schema Builders
|
|
2
|
+
|
|
3
|
+
Define database tables, views, and stored procedures with a fluent, type-safe API.
|
|
4
|
+
|
|
5
|
+
## API Reference
|
|
6
|
+
|
|
7
|
+
### `Table(name)`
|
|
8
|
+
|
|
9
|
+
Factory function that creates a `TableBuilder` for defining table schemas.
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
function Table(name: string): TableBuilder<{}, {}>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
#### TableBuilder Methods
|
|
16
|
+
|
|
17
|
+
| Method | Signature | Description |
|
|
18
|
+
|--------|-----------|-------------|
|
|
19
|
+
| `description(desc)` | `.description(desc: string)` | Set table description (DDL comment) |
|
|
20
|
+
| `database(db)` | `.database(db: string)` | Set database name |
|
|
21
|
+
| `schema(schema)` | `.schema(schema: string)` | Set schema name (MSSQL: `dbo`, PostgreSQL: `public`) |
|
|
22
|
+
| `columns(fn)` | `.columns((c) => ({...}))` | Define columns using column factory |
|
|
23
|
+
| `primaryKey(...cols)` | `.primaryKey("col1", "col2")` | Set primary key (single or composite) |
|
|
24
|
+
| `indexes(fn)` | `.indexes((i) => [...])` | Define indexes |
|
|
25
|
+
| `relations(fn)` | `.relations((r) => ({...}))` | Define relationships (FK, reverse FK, logical relations) |
|
|
26
|
+
|
|
27
|
+
#### Type Inference Properties
|
|
28
|
+
|
|
29
|
+
| Property | Description |
|
|
30
|
+
|----------|-------------|
|
|
31
|
+
| `$inferSelect` | Full type (columns + deep relations) |
|
|
32
|
+
| `$inferColumns` | Columns only |
|
|
33
|
+
| `$inferInsert` | Insert type (autoIncrement/nullable/default fields are optional) |
|
|
34
|
+
| `$inferUpdate` | Update type (all fields optional) |
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
### `View(name)`
|
|
39
|
+
|
|
40
|
+
Factory function that creates a `ViewBuilder` for defining database views.
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
function View(name: string): ViewBuilder<any, {}, {}>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
#### ViewBuilder Methods
|
|
47
|
+
|
|
48
|
+
| Method | Signature | Description |
|
|
49
|
+
|--------|-----------|-------------|
|
|
50
|
+
| `description(desc)` | `.description(desc: string)` | Set view description |
|
|
51
|
+
| `database(db)` | `.database(db: string)` | Set database name |
|
|
52
|
+
| `schema(schema)` | `.schema(schema: string)` | Set schema name |
|
|
53
|
+
| `query(viewFn)` | `.query((db) => db.table().select(...))` | Define view SELECT query |
|
|
54
|
+
| `relations(fn)` | `.relations((r) => ({...}))` | Define relationships (logical only, no FK) |
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
### `Procedure(name)`
|
|
59
|
+
|
|
60
|
+
Factory function that creates a `ProcedureBuilder` for defining stored procedures.
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
function Procedure(name: string): ProcedureBuilder<never, never>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
#### ProcedureBuilder Methods
|
|
67
|
+
|
|
68
|
+
| Method | Signature | Description |
|
|
69
|
+
|--------|-----------|-------------|
|
|
70
|
+
| `description(desc)` | `.description(desc: string)` | Set procedure description |
|
|
71
|
+
| `database(db)` | `.database(db: string)` | Set database name |
|
|
72
|
+
| `schema(schema)` | `.schema(schema: string)` | Set schema name |
|
|
73
|
+
| `params(fn)` | `.params((c) => ({...}))` | Define input parameters |
|
|
74
|
+
| `returns(fn)` | `.returns((c) => ({...}))` | Define return columns |
|
|
75
|
+
| `body(sql)` | `.body("SELECT ...")` | Set procedure body SQL |
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
### Column Factory
|
|
80
|
+
|
|
81
|
+
The column factory (`c`) is provided inside `.columns()` and `.params()`/`.returns()` callbacks.
|
|
82
|
+
|
|
83
|
+
#### Column Types
|
|
84
|
+
|
|
85
|
+
| Method | SQL Type | TypeScript Type | Notes |
|
|
86
|
+
|--------|----------|-----------------|-------|
|
|
87
|
+
| `c.int()` | INT | `number` | 4 bytes |
|
|
88
|
+
| `c.bigint()` | BIGINT | `number` | 8 bytes |
|
|
89
|
+
| `c.float()` | FLOAT | `number` | Single precision |
|
|
90
|
+
| `c.double()` | DOUBLE | `number` | Double precision |
|
|
91
|
+
| `c.decimal(p, s)` | DECIMAL(p,s) | `number` | Fixed-point |
|
|
92
|
+
| `c.varchar(len)` | VARCHAR(len) | `string` | Variable-length string |
|
|
93
|
+
| `c.char(len)` | CHAR(len) | `string` | Fixed-length string |
|
|
94
|
+
| `c.text()` | TEXT | `string` | Large text |
|
|
95
|
+
| `c.binary()` | BLOB/VARBINARY/BYTEA | `Bytes` | Binary data |
|
|
96
|
+
| `c.boolean()` | TINYINT(1)/BIT/BOOLEAN | `boolean` | |
|
|
97
|
+
| `c.datetime()` | DATETIME | `DateTime` | Date + time |
|
|
98
|
+
| `c.date()` | DATE | `DateOnly` | Date only |
|
|
99
|
+
| `c.time()` | TIME | `Time` | Time only |
|
|
100
|
+
| `c.uuid()` | BINARY(16)/UNIQUEIDENTIFIER/UUID | `Uuid` | |
|
|
101
|
+
|
|
102
|
+
#### Column Modifiers
|
|
103
|
+
|
|
104
|
+
| Method | Description |
|
|
105
|
+
|--------|-------------|
|
|
106
|
+
| `.autoIncrement()` | Auto-increment (optional in INSERT inference) |
|
|
107
|
+
| `.nullable()` | Allow NULL (adds `undefined` to type) |
|
|
108
|
+
| `.default(value)` | Set default value (optional in INSERT inference) |
|
|
109
|
+
| `.description(desc)` | Set column comment |
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
### Index Factory
|
|
114
|
+
|
|
115
|
+
The index factory (`i`) is provided inside `.indexes()` callbacks.
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
i.index("col1", "col2") // Create index on columns
|
|
119
|
+
.unique() // Make unique
|
|
120
|
+
.orderBy("ASC", "DESC") // Set sort order per column
|
|
121
|
+
.name("IX_Custom_Name") // Custom index name
|
|
122
|
+
.description("desc") // Description
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
### Relation Factory
|
|
128
|
+
|
|
129
|
+
The relation factory (`r`) is provided inside `.relations()` callbacks.
|
|
130
|
+
|
|
131
|
+
| Method | Type | Description |
|
|
132
|
+
|--------|------|-------------|
|
|
133
|
+
| `r.foreignKey(cols, targetFn)` | N:1 | FK constraint created in DB |
|
|
134
|
+
| `r.foreignKeyTarget(targetFn, relName)` | 1:N | Reverse FK reference (array by default) |
|
|
135
|
+
| `r.relationKey(cols, targetFn)` | N:1 | Logical relation (no DB FK) |
|
|
136
|
+
| `r.relationKeyTarget(targetFn, relName)` | 1:N | Logical reverse reference |
|
|
137
|
+
|
|
138
|
+
Both `foreignKeyTarget` and `relationKeyTarget` support `.single()` to indicate a 1:1 relationship (returns single object instead of array).
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Usage Examples
|
|
143
|
+
|
|
144
|
+
### Complete Table Definition
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
const User = Table("User")
|
|
148
|
+
.database("mydb")
|
|
149
|
+
.description("Application users")
|
|
150
|
+
.columns((c) => ({
|
|
151
|
+
id: c.bigint().autoIncrement(),
|
|
152
|
+
name: c.varchar(100),
|
|
153
|
+
email: c.varchar(200).nullable(),
|
|
154
|
+
status: c.varchar(20).default("active"),
|
|
155
|
+
createdAt: c.datetime().default("CURRENT_TIMESTAMP"),
|
|
156
|
+
}))
|
|
157
|
+
.primaryKey("id")
|
|
158
|
+
.indexes((i) => [
|
|
159
|
+
i.index("email").unique(),
|
|
160
|
+
i.index("status", "createdAt").orderBy("ASC", "DESC"),
|
|
161
|
+
]);
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Table with Relations
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
const Post = Table("Post")
|
|
168
|
+
.columns((c) => ({
|
|
169
|
+
id: c.bigint().autoIncrement(),
|
|
170
|
+
authorId: c.bigint(),
|
|
171
|
+
title: c.varchar(200),
|
|
172
|
+
}))
|
|
173
|
+
.primaryKey("id")
|
|
174
|
+
.relations((r) => ({
|
|
175
|
+
author: r.foreignKey(["authorId"], () => User),
|
|
176
|
+
}));
|
|
177
|
+
|
|
178
|
+
const User = Table("User")
|
|
179
|
+
.columns((c) => ({
|
|
180
|
+
id: c.bigint().autoIncrement(),
|
|
181
|
+
name: c.varchar(100),
|
|
182
|
+
}))
|
|
183
|
+
.primaryKey("id")
|
|
184
|
+
.relations((r) => ({
|
|
185
|
+
posts: r.foreignKeyTarget(() => Post, "author"),
|
|
186
|
+
profile: r.foreignKeyTarget(() => Profile, "user").single(),
|
|
187
|
+
}));
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### View Definition
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
const ActiveUsers = View("ActiveUsers")
|
|
194
|
+
.database("mydb")
|
|
195
|
+
.query((db: MyDb) =>
|
|
196
|
+
db.user()
|
|
197
|
+
.where((u) => [expr.eq(u.status, "active")])
|
|
198
|
+
.select((u) => ({ id: u.id, name: u.name, email: u.email }))
|
|
199
|
+
);
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Procedure Definition
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
const GetUserById = Procedure("GetUserById")
|
|
206
|
+
.database("mydb")
|
|
207
|
+
.params((c) => ({
|
|
208
|
+
userId: c.bigint(),
|
|
209
|
+
}))
|
|
210
|
+
.returns((c) => ({
|
|
211
|
+
id: c.bigint(),
|
|
212
|
+
name: c.varchar(100),
|
|
213
|
+
email: c.varchar(200),
|
|
214
|
+
}))
|
|
215
|
+
.body("SELECT id, name, email FROM User WHERE id = userId");
|
|
216
|
+
```
|