@zerotal/orm 1.7.2 → 1.7.4
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/CHANGELOG.md +41 -4
- package/api-surface.md +17 -5
- package/package.json +3 -3
- package/src/db/dialects/MysqlDialect.ts +13 -0
- package/src/db/dialects/PostgresDialect.ts +11 -0
- package/src/db/dialects/SqliteDialect.ts +12 -0
- package/src/db/dialects/types.ts +36 -0
- package/src/schema/Blueprint.ts +13 -8
- package/src/schema/ColumnDefinition.ts +24 -4
package/CHANGELOG.md
CHANGED
|
@@ -8,10 +8,46 @@ follows the Zerotal monorepo's unified versioning.
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [1.7.4] — 2026-08-21
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **A string column could not carry an index on MySQL.** `table.string()` compiled to `TEXT`
|
|
16
|
+
on every engine and discarded its `length` argument — the parameter existed and was
|
|
17
|
+
documented as "accepted for multi-DB compatibility", wired to nothing. MySQL refuses to key
|
|
18
|
+
a TEXT column without a prefix length, so `table.string("email").unique()` failed at
|
|
19
|
+
`CREATE TABLE`:
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
BLOB/TEXT column 'email' used in key specification without a key length
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Any natural key — an email, a slug — was unusable on MySQL, and `index()` the same. The
|
|
26
|
+
storage type now comes from the dialect, beside `booleanType` and `autoIncrementColumn`:
|
|
27
|
+
MySQL gets `VARCHAR(length)`, while SQLite and PostgreSQL keep `TEXT`, which PostgreSQL
|
|
28
|
+
indexes happily. `char()` had the identical bug and the identical fix.
|
|
29
|
+
|
|
30
|
+
Found by the new MySQL smoke suite on its first run against a real server.
|
|
31
|
+
|
|
32
|
+
## [1.7.3] — 2026-08-20
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
|
|
36
|
+
- **A boolean column could not hold a boolean on PostgreSQL.** `table.boolean()` compiled to
|
|
37
|
+
`INTEGER` on every engine — correct on SQLite, which has no boolean type, and rejected outright
|
|
38
|
+
by PostgreSQL: `column "…" is of type integer but expression is of type boolean` (42804) on the
|
|
39
|
+
first insert, and again on any `where(column, true)`. `DEFAULT` clauses failed the same way, a
|
|
40
|
+
boolean default having been serialised to `1`. The storage type now comes from the dialect, as
|
|
41
|
+
the auto-increment column already did. SQLite and MySQL are unchanged; existing PostgreSQL
|
|
42
|
+
tables keep their integer columns until a migration alters them. Found by the new smoke suite
|
|
43
|
+
that runs the ORM against a real PostgreSQL in CI.
|
|
44
|
+
|
|
45
|
+
## [1.7.2] — 2026-08-18
|
|
46
|
+
|
|
11
47
|
### Fixed
|
|
12
48
|
|
|
13
49
|
- **A seeder that failed partway left its rows behind.** `Seeder.call()` has always wrapped
|
|
14
|
-
|
|
50
|
+
_composed_ seeders in a transaction, so a `DatabaseSeeder` that delegates was atomic and one that
|
|
15
51
|
does its work inline — which is most of them — was not. A failure on the fourth table committed
|
|
16
52
|
the first three, so the obvious next move, running it again, died on a unique constraint, and the
|
|
17
53
|
only way out was `migrate:fresh`. Migrations became transactional in 1.7.0; this closes the
|
|
@@ -22,9 +58,6 @@ follows the Zerotal monorepo's unified versioning.
|
|
|
22
58
|
no connection is bound, because a seeder is not obliged to touch the database and an app that has
|
|
23
59
|
not configured one should not fail to seed over a transaction it never needed.
|
|
24
60
|
|
|
25
|
-
|
|
26
|
-
### Fixed
|
|
27
|
-
|
|
28
61
|
- **`DatabaseProvider` now runs in `worker`, so `zt queue:work` can boot.** It did not, and the
|
|
29
62
|
consequence was total rather than partial: `QueueProvider` _does_ run in `worker`, the
|
|
30
63
|
queue's own default driver is `sqlite`, and so the worker asked for a connection this
|
|
@@ -43,6 +76,10 @@ follows the Zerotal monorepo's unified versioning.
|
|
|
43
76
|
|
|
44
77
|
Found building the first cookbook app, whose first queued job could not run.
|
|
45
78
|
|
|
79
|
+
## [1.7.1] — 2026-08-16
|
|
80
|
+
|
|
81
|
+
### Fixed
|
|
82
|
+
|
|
46
83
|
- **Relation keys now accept the JS spelling, like every other identifier.** The convention is
|
|
47
84
|
camelCase in the application and snake_case in the database, converted on the way through —
|
|
48
85
|
and relation keys were the one place it did not happen. `@hasMany(() => Issue, { foreignKey:
|
package/api-surface.md
CHANGED
|
@@ -116,7 +116,7 @@ class Blueprint = {
|
|
|
116
116
|
bigInteger: (name: string) => ColumnBuilder
|
|
117
117
|
binary: (name: string) => ColumnBuilder
|
|
118
118
|
boolean: (name: string) => ColumnBuilder
|
|
119
|
-
char: (name: string,
|
|
119
|
+
char: (name: string, length?: number) => ColumnBuilder
|
|
120
120
|
date: (name: string) => ColumnBuilder
|
|
121
121
|
dateTime: (name: string) => ColumnBuilder
|
|
122
122
|
datetime: (name: string) => ColumnBuilder
|
|
@@ -153,7 +153,7 @@ class Blueprint = {
|
|
|
153
153
|
smallInteger: (name: string) => ColumnBuilder
|
|
154
154
|
softDeletes: (column?: string) => void
|
|
155
155
|
spatialIndex: (columns: string | string[], name?: string) => Blueprint
|
|
156
|
-
string: (name: string,
|
|
156
|
+
string: (name: string, length?: number) => ColumnBuilder
|
|
157
157
|
text: (name: string) => ColumnBuilder
|
|
158
158
|
time: (name: string) => ColumnBuilder
|
|
159
159
|
timestamp: (name: string) => ColumnBuilder
|
|
@@ -182,7 +182,7 @@ class Cast = {
|
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
class ColumnBuilder = {
|
|
185
|
-
new <Locked extends string = never>(name: string, _sqlType: string, isPrimary?: boolean, isAutoIncrement?: boolean): ColumnBuilder<Locked>
|
|
185
|
+
new <Locked extends string = never>(name: string, _sqlType: string, isPrimary?: boolean, isAutoIncrement?: boolean, _isBoolean?: boolean, _stringLength?: number | undefined): ColumnBuilder<Locked>
|
|
186
186
|
after: (_column: string) => ColumnBuilder<Locked>
|
|
187
187
|
alter: () => ColumnBuilder<Locked>
|
|
188
188
|
before: (_column: string) => ColumnBuilder<Locked>
|
|
@@ -566,12 +566,15 @@ class MysqlDialect = {
|
|
|
566
566
|
advisoryLockSql: (key: number) => DialectQuery
|
|
567
567
|
advisoryUnlockSql: (key: number) => DialectQuery
|
|
568
568
|
autoIncrementColumn: (column: string) => string
|
|
569
|
+
booleanLiteral: (value: boolean) => string
|
|
569
570
|
dateExpr: (part: DatePart, column: string) => string
|
|
570
571
|
hasColumnSql: (table: string, column: string) => DialectQuery
|
|
571
572
|
hasTableSql: (table: string) => DialectQuery
|
|
573
|
+
readonly booleanType: 'INTEGER'
|
|
572
574
|
readonly name: 'mysql'
|
|
573
575
|
readonly supportsAdvisoryLocks: true
|
|
574
576
|
readonly supportsTransactionalDdl: false
|
|
577
|
+
stringType: (length: number) => string
|
|
575
578
|
}
|
|
576
579
|
|
|
577
580
|
class NPlusOneDetected = {
|
|
@@ -605,12 +608,15 @@ class PostgresDialect = {
|
|
|
605
608
|
advisoryLockSql: (key: number) => DialectQuery
|
|
606
609
|
advisoryUnlockSql: (key: number) => DialectQuery
|
|
607
610
|
autoIncrementColumn: (column: string) => string
|
|
611
|
+
booleanLiteral: (value: boolean) => string
|
|
608
612
|
dateExpr: (part: DatePart, column: string) => string
|
|
609
613
|
hasColumnSql: (table: string, column: string) => DialectQuery
|
|
610
614
|
hasTableSql: (table: string) => DialectQuery
|
|
615
|
+
readonly booleanType: 'BOOLEAN'
|
|
611
616
|
readonly name: 'postgres'
|
|
612
617
|
readonly supportsAdvisoryLocks: true
|
|
613
618
|
readonly supportsTransactionalDdl: true
|
|
619
|
+
stringType: () => string
|
|
614
620
|
}
|
|
615
621
|
|
|
616
622
|
class QueryBuilder = {
|
|
@@ -742,12 +748,15 @@ class SqliteDialect = {
|
|
|
742
748
|
advisoryLockSql: () => DialectQuery | null
|
|
743
749
|
advisoryUnlockSql: () => DialectQuery | null
|
|
744
750
|
autoIncrementColumn: (column: string) => string
|
|
751
|
+
booleanLiteral: (value: boolean) => string
|
|
745
752
|
dateExpr: (part: DatePart, column: string) => string
|
|
746
753
|
hasColumnSql: (table: string, column: string) => DialectQuery
|
|
747
754
|
hasTableSql: (table: string) => DialectQuery
|
|
755
|
+
readonly booleanType: 'INTEGER'
|
|
748
756
|
readonly name: 'sqlite'
|
|
749
757
|
readonly supportsAdvisoryLocks: false
|
|
750
758
|
readonly supportsTransactionalDdl: true
|
|
759
|
+
stringType: () => string
|
|
751
760
|
}
|
|
752
761
|
|
|
753
762
|
class StateError = {
|
|
@@ -1260,12 +1269,15 @@ interface SqlDialect = {
|
|
|
1260
1269
|
advisoryLockSql: (key: number) => DialectQuery | null
|
|
1261
1270
|
advisoryUnlockSql: (key: number) => DialectQuery | null
|
|
1262
1271
|
autoIncrementColumn: (column: string) => string
|
|
1272
|
+
booleanLiteral: (value: boolean) => string
|
|
1263
1273
|
dateExpr: (part: DatePart, column: string) => string
|
|
1264
1274
|
hasColumnSql: (table: string, column: string) => DialectQuery
|
|
1265
1275
|
hasTableSql: (table: string) => DialectQuery
|
|
1276
|
+
readonly booleanType: string
|
|
1266
1277
|
readonly name: DialectName
|
|
1267
1278
|
readonly supportsAdvisoryLocks: boolean
|
|
1268
1279
|
readonly supportsTransactionalDdl: boolean
|
|
1280
|
+
stringType: (length: number) => string
|
|
1269
1281
|
}
|
|
1270
1282
|
|
|
1271
1283
|
interface SQLInstance = {
|
|
@@ -1307,13 +1319,13 @@ type ClassRef = abstract new (...args: never[]) => unknown
|
|
|
1307
1319
|
|
|
1308
1320
|
type Columns = { [K in keyof T & string]: K extends `_${string}` ? never : T[K] extends (...args: any[]) => any ? never : K; }[keyof T & string]
|
|
1309
1321
|
|
|
1310
|
-
type ColumnShorthand = 'string' | '
|
|
1322
|
+
type ColumnShorthand = 'string' | 'text' | 'integer' | 'number' | 'float' | 'boolean' | 'datetime' | 'date' | 'json' | 'array' | 'encrypted' | 'encrypted:json'
|
|
1311
1323
|
|
|
1312
1324
|
type Constructor = new (...args: any[]) => T
|
|
1313
1325
|
|
|
1314
1326
|
type ContextConnectionResolver = (ModelClass?: typeof BaseModel) => SQLInstance | null
|
|
1315
1327
|
|
|
1316
|
-
type DatePart = 'date' | 'time' | '
|
|
1328
|
+
type DatePart = 'date' | 'time' | 'day' | 'month' | 'year'
|
|
1317
1329
|
|
|
1318
1330
|
type DialectName = 'sqlite' | 'postgres' | 'mysql'
|
|
1319
1331
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zerotal/orm",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"maturity": "stable",
|
|
6
6
|
"private": false,
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"typecheck": "tsc --noEmit"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@zerotal/core": "1.7.
|
|
35
|
-
"@zerotal/validator": "1.7.
|
|
34
|
+
"@zerotal/core": "1.7.4",
|
|
35
|
+
"@zerotal/validator": "1.7.4"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"typescript": "^5.8.0"
|
|
@@ -51,6 +51,19 @@ export class MysqlDialect implements SqlDialect {
|
|
|
51
51
|
return `${column} INT AUTO_INCREMENT PRIMARY KEY`;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
// MySQL BOOLEAN is a synonym for TINYINT(1) and INTEGER accepts 0/1 all the same.
|
|
55
|
+
readonly booleanType = "INTEGER";
|
|
56
|
+
|
|
57
|
+
booleanLiteral(value: boolean): string {
|
|
58
|
+
return value ? "1" : "0";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// VARCHAR so the column can carry an index or a unique constraint; MySQL
|
|
62
|
+
// refuses to key a TEXT column without a prefix length.
|
|
63
|
+
stringType(length: number): string {
|
|
64
|
+
return `VARCHAR(${length})`;
|
|
65
|
+
}
|
|
66
|
+
|
|
54
67
|
advisoryLockSql(key: number): DialectQuery {
|
|
55
68
|
return { sql: `SELECT GET_LOCK(?, -1)`, params: [`zerotal_lock_${key}`] };
|
|
56
69
|
}
|
|
@@ -48,6 +48,17 @@ export class PostgresDialect implements SqlDialect {
|
|
|
48
48
|
return `${column} INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY`;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
readonly booleanType = "BOOLEAN";
|
|
52
|
+
|
|
53
|
+
booleanLiteral(value: boolean): string {
|
|
54
|
+
return value ? "TRUE" : "FALSE";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// PostgreSQL indexes TEXT without a key length, so the portable type stands.
|
|
58
|
+
stringType(): string {
|
|
59
|
+
return "TEXT";
|
|
60
|
+
}
|
|
61
|
+
|
|
51
62
|
advisoryLockSql(key: number): DialectQuery {
|
|
52
63
|
return { sql: `SELECT pg_advisory_lock(?)`, params: [key] };
|
|
53
64
|
}
|
|
@@ -48,6 +48,18 @@ export class SqliteDialect implements SqlDialect {
|
|
|
48
48
|
return `${column} INTEGER PRIMARY KEY AUTOINCREMENT`;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
// SQLite has no boolean type — 0/1 in an INTEGER is the storage class it uses.
|
|
52
|
+
readonly booleanType = "INTEGER";
|
|
53
|
+
|
|
54
|
+
booleanLiteral(value: boolean): string {
|
|
55
|
+
return value ? "1" : "0";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// SQLite has one string type and no length to honour.
|
|
59
|
+
stringType(): string {
|
|
60
|
+
return "TEXT";
|
|
61
|
+
}
|
|
62
|
+
|
|
51
63
|
advisoryLockSql(): DialectQuery | null {
|
|
52
64
|
return null;
|
|
53
65
|
}
|
package/src/db/dialects/types.ts
CHANGED
|
@@ -56,6 +56,42 @@ export interface SqlDialect {
|
|
|
56
56
|
*/
|
|
57
57
|
autoIncrementColumn(column: string): string;
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* The column type a portable `table.boolean()` compiles to.
|
|
61
|
+
*
|
|
62
|
+
* SQLite has no boolean type and stores 0/1 in an `INTEGER`, which is why the
|
|
63
|
+
* Blueprint emitted `INTEGER` for every engine. PostgreSQL has a real `boolean`
|
|
64
|
+
* and refuses to compare or assign one against an integer column, so a table
|
|
65
|
+
* built that way rejected its own booleans — `column "active" is of type integer
|
|
66
|
+
* but expression is of type boolean` (SQLSTATE 42804) on the first insert, and
|
|
67
|
+
* again on any `where("active", true)`. Nothing caught it because no test
|
|
68
|
+
* executed the DDL against a server; `postgres.smoke.test.ts` now does.
|
|
69
|
+
*
|
|
70
|
+
* MySQL keeps `INTEGER`: its `BOOLEAN` is a synonym for `TINYINT(1)` and it
|
|
71
|
+
* accepts 0/1 either way, so there is no defect there to fix and no reason to
|
|
72
|
+
* churn the DDL of an engine no CI job covers.
|
|
73
|
+
*/
|
|
74
|
+
readonly booleanType: string;
|
|
75
|
+
|
|
76
|
+
/** A boolean as this engine spells it in a `DEFAULT` clause. */
|
|
77
|
+
booleanLiteral(value: boolean): string;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The column type a portable `table.string(name, length)` compiles to.
|
|
81
|
+
*
|
|
82
|
+
* SQLite has one string type and ignores the length, so the Blueprint emitted
|
|
83
|
+
* `TEXT` for every engine and threw the length away. MySQL cannot index a TEXT
|
|
84
|
+
* column without a key length, so `table.string("name").unique()` — an email, a
|
|
85
|
+
* slug, any natural key — failed at `CREATE TABLE`:
|
|
86
|
+
*
|
|
87
|
+
* BLOB/TEXT column 'name' used in key specification without a key length
|
|
88
|
+
*
|
|
89
|
+
* The length was already in the signature and already documented as accepted;
|
|
90
|
+
* it just had nowhere to go. PostgreSQL keeps `TEXT`, which it indexes happily
|
|
91
|
+
* and which is the idiomatic choice there.
|
|
92
|
+
*/
|
|
93
|
+
stringType(length: number): string;
|
|
94
|
+
|
|
59
95
|
/** Whether the engine supports application-level advisory locks. */
|
|
60
96
|
readonly supportsAdvisoryLocks: boolean;
|
|
61
97
|
|
package/src/schema/Blueprint.ts
CHANGED
|
@@ -202,19 +202,21 @@ export class Blueprint {
|
|
|
202
202
|
/**
|
|
203
203
|
* Variable-length string column (`VARCHAR`-style), stored as `TEXT`.
|
|
204
204
|
* @param name - Column name.
|
|
205
|
-
* @param
|
|
205
|
+
* @param length - Max length. Ignored on SQLite and PostgreSQL, which have one
|
|
206
|
+
* string type; on MySQL it becomes `VARCHAR(length)`, which is what lets the
|
|
207
|
+
* column carry an index or a unique constraint.
|
|
206
208
|
* @category Column types
|
|
207
209
|
*/
|
|
208
|
-
string(name: string,
|
|
209
|
-
return this._add(new ColumnBuilder(name, "TEXT"));
|
|
210
|
+
string(name: string, length = 255): ColumnBuilder {
|
|
211
|
+
return this._add(new ColumnBuilder(name, "TEXT", false, false, false, length));
|
|
210
212
|
}
|
|
211
213
|
|
|
212
214
|
/**
|
|
213
215
|
* Fixed-length `CHAR` column. Stored as `TEXT` on SQLite; `_length` is ignored.
|
|
214
216
|
* @category Column types
|
|
215
217
|
*/
|
|
216
|
-
char(name: string,
|
|
217
|
-
return this._add(new ColumnBuilder(name, "TEXT"));
|
|
218
|
+
char(name: string, length = 255): ColumnBuilder {
|
|
219
|
+
return this._add(new ColumnBuilder(name, "TEXT", false, false, false, length));
|
|
218
220
|
}
|
|
219
221
|
|
|
220
222
|
/**
|
|
@@ -314,12 +316,15 @@ export class Blueprint {
|
|
|
314
316
|
// ── Boolean ───────────────────────────────────────────────────────────────
|
|
315
317
|
|
|
316
318
|
/**
|
|
317
|
-
* Boolean column.
|
|
318
|
-
*
|
|
319
|
+
* Boolean column. The storage type is the engine's: `INTEGER` holding 0 / 1 on
|
|
320
|
+
* SQLite and MySQL, a real `BOOLEAN` on PostgreSQL — which rejects the integer
|
|
321
|
+
* form for both assignment and comparison, so emitting `INTEGER` everywhere
|
|
322
|
+
* built a column that would not take its own booleans. JS booleans passed to
|
|
323
|
+
* {@link ColumnBuilder.default} follow the same engine's spelling.
|
|
319
324
|
* @category Column types
|
|
320
325
|
*/
|
|
321
326
|
boolean(name: string): ColumnBuilder {
|
|
322
|
-
return this._add(new ColumnBuilder(name, "INTEGER"));
|
|
327
|
+
return this._add(new ColumnBuilder(name, "INTEGER", false, false, true));
|
|
323
328
|
}
|
|
324
329
|
|
|
325
330
|
// ── Date / time columns ───────────────────────────────────────────────────
|
|
@@ -64,6 +64,16 @@ export class ColumnBuilder<Locked extends string = never> {
|
|
|
64
64
|
private _sqlType: string,
|
|
65
65
|
isPrimary = false,
|
|
66
66
|
isAutoIncrement = false,
|
|
67
|
+
/**
|
|
68
|
+
* Logically a boolean, whatever `_sqlType` says. The engine decides the
|
|
69
|
+
* storage type at compile time — see {@link SqlDialect.booleanType}.
|
|
70
|
+
*/
|
|
71
|
+
private _isBoolean = false,
|
|
72
|
+
/**
|
|
73
|
+
* Declared max length for a portable string column. The engine decides
|
|
74
|
+
* whether it matters — see {@link SqlDialect.stringType}.
|
|
75
|
+
*/
|
|
76
|
+
private _stringLength?: number,
|
|
67
77
|
) {
|
|
68
78
|
this._isPrimary = isPrimary;
|
|
69
79
|
this._isAutoIncr = isAutoIncrement;
|
|
@@ -324,13 +334,22 @@ export class ColumnBuilder<Locked extends string = never> {
|
|
|
324
334
|
// PostgreSQL a syntax error and against MySQL a 1064.
|
|
325
335
|
if (this._isAutoIncr) return getDialect(dialect).autoIncrementColumn(this.name);
|
|
326
336
|
|
|
327
|
-
|
|
337
|
+
// Storage type is the engine's to choose wherever the engines disagree:
|
|
338
|
+
// SQLite keeps 0/1 in an INTEGER where PostgreSQL has a real boolean, and
|
|
339
|
+
// MySQL needs a VARCHAR length before it will index a string at all.
|
|
340
|
+
const d = getDialect(dialect);
|
|
341
|
+
const sqlType = this._isBoolean
|
|
342
|
+
? d.booleanType
|
|
343
|
+
: this._stringLength !== undefined
|
|
344
|
+
? d.stringType(this._stringLength)
|
|
345
|
+
: this._sqlType;
|
|
346
|
+
const parts: string[] = [`${this.name} ${sqlType}`];
|
|
328
347
|
|
|
329
348
|
if (this._isPrimary) parts.push("PRIMARY KEY");
|
|
330
349
|
if (!this._isNullable && !this._isPrimary) parts.push("NOT NULL");
|
|
331
350
|
|
|
332
351
|
if (this._useCurrent) parts.push("DEFAULT CURRENT_TIMESTAMP");
|
|
333
|
-
else if (this._hasDefault) parts.push(`DEFAULT ${this._serializeDefault()}`);
|
|
352
|
+
else if (this._hasDefault) parts.push(`DEFAULT ${this._serializeDefault(dialect)}`);
|
|
334
353
|
|
|
335
354
|
if (this._isUnique) parts.push("UNIQUE");
|
|
336
355
|
if (this._check) parts.push(`CHECK (${this._check})`);
|
|
@@ -340,10 +359,11 @@ export class ColumnBuilder<Locked extends string = never> {
|
|
|
340
359
|
return parts.join(" ");
|
|
341
360
|
}
|
|
342
361
|
|
|
343
|
-
private _serializeDefault(): string {
|
|
362
|
+
private _serializeDefault(dialect: DialectName = "sqlite"): string {
|
|
344
363
|
const v = this._default;
|
|
345
364
|
if (v === null) return "NULL";
|
|
346
|
-
|
|
365
|
+
// `DEFAULT 1` on a PostgreSQL boolean column is the same 42804 the value itself hit.
|
|
366
|
+
if (typeof v === "boolean") return getDialect(dialect).booleanLiteral(v);
|
|
347
367
|
if (typeof v === "string") return `'${v.replace(/'/g, "''")}'`;
|
|
348
368
|
return String(v);
|
|
349
369
|
}
|