@an5/orm 1.0.8 → 1.1.0

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.
Files changed (73) hide show
  1. package/README.md +174 -175
  2. package/dist/an5Metadata.js +64 -0
  3. package/dist/an5Orm.d.ts +92 -3
  4. package/dist/an5Orm.js +697 -206
  5. package/dist/config.d.ts +9 -1
  6. package/dist/config.js +13 -18
  7. package/dist/errors.d.ts +1 -1
  8. package/dist/errors.js +1 -1
  9. package/dist/generator/src/api.d.ts +35 -0
  10. package/dist/generator/src/api.js +58 -0
  11. package/dist/generator/src/code-generator.d.ts +14 -13
  12. package/dist/generator/src/code-generator.js +249 -225
  13. package/dist/generator/src/config.d.ts +162 -0
  14. package/dist/generator/src/config.js +354 -0
  15. package/dist/generator/src/dialect.d.ts +88 -0
  16. package/dist/generator/src/dialect.js +318 -0
  17. package/dist/generator/src/dotnet-generator.d.ts +21 -11
  18. package/dist/generator/src/dotnet-generator.js +1053 -546
  19. package/dist/generator/src/field-types.d.ts +96 -0
  20. package/dist/generator/src/field-types.js +433 -0
  21. package/dist/generator/src/golang-generator.d.ts +36 -0
  22. package/dist/generator/src/golang-generator.js +1229 -0
  23. package/dist/generator/src/index.d.ts +1 -1
  24. package/dist/generator/src/index.js +137 -88
  25. package/dist/generator/src/metadata-generator.d.ts +11 -8
  26. package/dist/generator/src/metadata-generator.js +105 -90
  27. package/dist/generator/src/parser.d.ts +37 -11
  28. package/dist/generator/src/parser.js +208 -206
  29. package/dist/generator/src/push-apply.d.ts +37 -0
  30. package/dist/generator/src/push-apply.js +109 -0
  31. package/dist/generator/src/push-schema.d.ts +93 -0
  32. package/dist/generator/src/push-schema.js +189 -0
  33. package/dist/generator/src/python-generator.d.ts +37 -9
  34. package/dist/generator/src/python-generator.js +428 -105
  35. package/dist/generator/src/rust-generator.d.ts +38 -0
  36. package/dist/generator/src/rust-generator.js +971 -0
  37. package/dist/generator/src/suggest.d.ts +17 -0
  38. package/dist/generator/src/suggest.js +45 -0
  39. package/dist/generator/src/type-kinds.d.ts +27 -0
  40. package/dist/generator/src/type-kinds.js +72 -0
  41. package/dist/generator/src/types.d.ts +60 -27
  42. package/dist/generator/src/types.js +41 -2
  43. package/dist/index.d.ts +3 -2
  44. package/dist/index.js +19 -8
  45. package/dist/logger.js +1 -1
  46. package/dist/migration-core.d.ts +102 -0
  47. package/dist/migration-core.js +731 -0
  48. package/dist/orm.d.ts +147 -0
  49. package/dist/orm.js +1654 -0
  50. package/dist/provider-support.d.ts +27 -0
  51. package/dist/provider-support.js +50 -0
  52. package/dist/scripts/release-cli.d.ts +2 -2
  53. package/dist/scripts/release-cli.js +115 -115
  54. package/dist/sql-utils.d.ts +3 -1
  55. package/dist/sql-utils.js +94 -24
  56. package/generator/src/api.ts +45 -0
  57. package/generator/src/code-generator.ts +259 -230
  58. package/generator/src/config.ts +496 -0
  59. package/generator/src/dialect.ts +415 -0
  60. package/generator/src/dotnet-generator.ts +1071 -544
  61. package/generator/src/field-types.ts +473 -0
  62. package/generator/src/golang-generator.ts +1267 -0
  63. package/generator/src/index.ts +146 -116
  64. package/generator/src/metadata-generator.ts +116 -101
  65. package/generator/src/parser.ts +234 -202
  66. package/generator/src/push-apply.ts +148 -0
  67. package/generator/src/push-schema.ts +250 -0
  68. package/generator/src/python-generator.ts +480 -114
  69. package/generator/src/rust-generator.ts +974 -0
  70. package/generator/src/suggest.ts +41 -0
  71. package/generator/src/type-kinds.ts +78 -0
  72. package/generator/src/types.ts +77 -29
  73. package/package.json +32 -6
@@ -0,0 +1,415 @@
1
+ /**
2
+ * SQL dialect per database provider, for the commands that write to a database.
3
+ *
4
+ * Field types are per provider (see `./field-types`), and so is the SQL that
5
+ * carries them. Until now `db:push` emitted SQL Server T-SQL for every connection
6
+ * string: `sys.tables` lookups, `[bracket]` quoting, `DEFAULT IDENTITY(1,1)`,
7
+ * `NEWID()`. Pointed at PostgreSQL that failed on the first catalog query with a
8
+ * driver error saying nothing about the real problem.
9
+ *
10
+ * Each dialect answers the same questions — how to quote, where the catalog lives,
11
+ * what a column definition looks like — so `db:push` reads them once instead of
12
+ * branching per provider. SQL Server's answers are the ones it already produced, so
13
+ * an existing SQL Server project sees no change apart from the two bugs called out
14
+ * on the builders below.
15
+ */
16
+ import { Provider } from './field-types';
17
+
18
+ /** `schema.table` reduced to the bare object name, e.g. `dbo.widgets`. */
19
+ function objectName(raw: string): string {
20
+ return raw.replace(/[\[\]"`]/g, '').split('.').filter(Boolean).join('.');
21
+ }
22
+
23
+ /** A possibly schema-qualified name split into parts, unquoting each. */
24
+ function splitQualified(raw: string): { schema: string | undefined; name: string } {
25
+ const parts = raw.replace(/[\[\]"`]/g, '').split('.').filter(Boolean);
26
+ if (parts.length >= 2) {
27
+ return { schema: parts[parts.length - 2], name: parts[parts.length - 1] };
28
+ }
29
+ return { schema: undefined, name: parts[0] ?? raw };
30
+ }
31
+
32
+ /**
33
+ * A single-quoted SQL string literal.
34
+ *
35
+ * Every provider doubles an embedded quote, so one helper covers them all. These
36
+ * values come from the schema files, so they are the project's own — but an
37
+ * apostrophe in a column name would otherwise close the literal and produce SQL
38
+ * that means something else entirely.
39
+ */
40
+ export function sqlLiteral(value: string): string {
41
+ return `'${value.replace(/'/g, "''")}'`;
42
+ }
43
+
44
+ /** One column of a table, as the schema describes it — same shape `db:push` parses. */
45
+ export interface ColumnSpec {
46
+ name: string;
47
+ /** The type as written in the schema, already validated for the provider. */
48
+ sqlType: string;
49
+ /** `@id`. Named to match the rest of the schema model. */
50
+ isId: boolean;
51
+ isUnique: boolean;
52
+ isOptional: boolean;
53
+ /** The raw `@default(...)` expression, e.g. `now()` or `"draft"`. */
54
+ defaultExpr?: string | undefined;
55
+ }
56
+
57
+ export interface PushDialect {
58
+ readonly provider: Provider;
59
+ /** Whether this database can be written to with DDL at all. */
60
+ readonly supportsDdl: boolean;
61
+ /** Quotes a single identifier. */
62
+ quote(name: string): string;
63
+ /** Quotes a possibly schema-qualified name; an empty schema means no prefix. */
64
+ quoteTable(raw: string): string;
65
+ /**
66
+ * Whether a compound unique can be a named table constraint.
67
+ *
68
+ * SQLite has no such thing — a UNIQUE becomes an implicit index the engine names
69
+ * itself — so it becomes a uniquely named index instead, which is the only way to
70
+ * keep `db:push` idempotent.
71
+ */
72
+ readonly supportsNamedUniqueConstraint: boolean;
73
+ /**
74
+ * Whether `ALTER TABLE ... ADD <column>` may carry a UNIQUE.
75
+ *
76
+ * SQLite refuses outright ("Cannot add a UNIQUE column"), so there the unique is
77
+ * added as a separate index — otherwise a new `@unique` column could not be
78
+ * pushed onto an existing table at all.
79
+ */
80
+ readonly supportsUniqueInlineOnAddColumn: boolean;
81
+ /** Query returning one row when the table exists. */
82
+ tableExists(raw: string): string;
83
+ /** Query returning one row when the column exists. */
84
+ columnExists(raw: string, column: string): string;
85
+ /** Query returning one row when the unique constraint exists. */
86
+ uniqueConstraintExists(raw: string, name: string): string;
87
+ /** Query returning one row when the index exists. */
88
+ indexExists(raw: string, name: string): string;
89
+ /** One column definition, as it appears inside `CREATE TABLE`. */
90
+ columnDefinition(column: ColumnSpec): string;
91
+ createTable(quotedTable: string, columnDefs: string[]): string;
92
+ createIndex(quotedName: string, quotedTable: string, quotedFields: string[]): string;
93
+ createUniqueIndex(quotedName: string, quotedTable: string, quotedFields: string[]): string;
94
+ /** `ALTER TABLE ... ADD <column>`, nullability included. */
95
+ addColumn(quotedTable: string, column: ColumnSpec): string;
96
+ addUniqueConstraint(quotedName: string, quotedTable: string, quotedFields: string[]): string;
97
+ }
98
+
99
+ /** Bracketed identifiers: SQL Server, and SQLite which accepts the same form. */
100
+ function bracketQuote(name: string): string {
101
+ return `[${name.replace(/^\[(.*)\]$/, '$1').replace(/\]/g, ']]')}]`;
102
+ }
103
+
104
+ /** Double-quoted identifiers: the SQL standard, so PostgreSQL. */
105
+ function doubleQuote(name: string): string {
106
+ return `"${name.replace(/^"(.*)"$/, '$1').replace(/"/g, '""')}"`;
107
+ }
108
+
109
+ /** Backtick identifiers: MySQL. */
110
+ function backtickQuote(name: string): string {
111
+ return `\`${name.replace(/^`(.*)`$/, '$1').replace(/`/g, '``')}\``;
112
+ }
113
+
114
+ /**
115
+ * The schema an unqualified table lands in.
116
+ *
117
+ * `''` means "whatever the connection resolves", which is also what the DDL emits
118
+ * for an unqualified name — the two must agree or `db:push` cannot find the table it
119
+ * just created. PostgreSQL therefore queries `current_schema()` rather than
120
+ * hard-coding `public`: with the usual `search_path = "$user", public`, a table
121
+ * created unqualified lands in the role's own schema, and looking only in `public`
122
+ * made every push re-issue `CREATE TABLE` and fail with "relation already exists".
123
+ */
124
+ function defaultSchema(provider: Provider, schema: string | undefined): string {
125
+ if (schema !== undefined) return schema;
126
+ return provider === 'postgres' ? 'current_schema()' : '';
127
+ }
128
+
129
+ /**
130
+ * Where a table's existence is recorded.
131
+ *
132
+ * SQL Server needs `OBJECT_ID` because the query is by object, not by name; the
133
+ * others keep their metadata in a catalog view keyed by schema plus name.
134
+ */
135
+ function buildTableExists(provider: Provider): (raw: string) => string {
136
+ return (raw) => {
137
+ const { schema, name } = splitQualified(raw);
138
+ switch (provider) {
139
+ case 'mssql':
140
+ return `SELECT name FROM sys.tables WHERE object_id = OBJECT_ID(${sqlLiteral(objectName(raw))})`;
141
+ case 'postgres':
142
+ return `SELECT tablename FROM pg_tables WHERE schemaname = ${schema ? sqlLiteral(schema) : defaultSchema(provider, undefined)} AND tablename = ${sqlLiteral(name)}`;
143
+ case 'mysql':
144
+ // An unqualified table belongs to the database the connection selected.
145
+ return (
146
+ `SELECT table_name FROM information_schema.tables WHERE table_schema = ${schema ? sqlLiteral(schema) : 'DATABASE()'} ` +
147
+ `AND table_name = ${sqlLiteral(name)}`
148
+ );
149
+ case 'sqlite':
150
+ return `SELECT name FROM sqlite_master WHERE type = 'table' AND name = ${sqlLiteral(name)}`;
151
+ default:
152
+ return '';
153
+ }
154
+ };
155
+ }
156
+
157
+ function buildColumnExists(provider: Provider): (raw: string, column: string) => string {
158
+ return (raw, column) => {
159
+ const { schema, name } = splitQualified(raw);
160
+ const columnLiteral = sqlLiteral(column);
161
+ switch (provider) {
162
+ case 'mssql':
163
+ return `SELECT name FROM sys.columns WHERE object_id = OBJECT_ID(${sqlLiteral(objectName(raw))}) AND name = ${columnLiteral}`;
164
+ case 'postgres':
165
+ return (
166
+ `SELECT column_name FROM information_schema.columns WHERE table_schema = ${schema ? sqlLiteral(schema) : defaultSchema(provider, undefined)} ` +
167
+ `AND table_name = ${sqlLiteral(name)} AND column_name = ${columnLiteral}`
168
+ );
169
+ case 'mysql':
170
+ return (
171
+ `SELECT column_name FROM information_schema.columns WHERE table_schema = ${schema ? sqlLiteral(schema) : 'DATABASE()'} ` +
172
+ `AND table_name = ${sqlLiteral(name)} AND column_name = ${columnLiteral}`
173
+ );
174
+ case 'sqlite':
175
+ // A table-valued pragma, so this stays one round trip. Needs SQLite 3.16
176
+ // (2017) for `pragma_table_info` to be usable as a table function.
177
+ return `SELECT name FROM pragma_table_info(${sqlLiteral(name)}) WHERE name = ${columnLiteral}`;
178
+ default:
179
+ return '';
180
+ }
181
+ };
182
+ }
183
+
184
+ function buildIndexExists(provider: Provider): (raw: string, name: string) => string {
185
+ return (raw, name) => {
186
+ const { schema, name: table } = splitQualified(raw);
187
+ const tableLiteral = sqlLiteral(table);
188
+ const indexLiteral = sqlLiteral(name);
189
+ const schemaClause = schema ? sqlLiteral(schema) : 'DATABASE()';
190
+ switch (provider) {
191
+ case 'mssql':
192
+ return `SELECT name FROM sys.indexes WHERE object_id = OBJECT_ID(${sqlLiteral(objectName(raw))}) AND name = ${indexLiteral}`;
193
+ case 'postgres':
194
+ return (
195
+ `SELECT indexname FROM pg_indexes WHERE schemaname = ${schema ? sqlLiteral(schema) : defaultSchema(provider, undefined)} ` +
196
+ `AND tablename = ${tableLiteral} AND indexname = ${indexLiteral}`
197
+ );
198
+ case 'mysql':
199
+ return (
200
+ `SELECT index_name FROM information_schema.statistics WHERE table_schema = ${schemaClause} ` +
201
+ `AND table_name = ${tableLiteral} AND index_name = ${indexLiteral} LIMIT 1`
202
+ );
203
+ case 'sqlite':
204
+ return `SELECT name FROM sqlite_master WHERE type = 'index' AND tbl_name = ${tableLiteral} AND name = ${indexLiteral}`;
205
+ default:
206
+ return '';
207
+ }
208
+ };
209
+ }
210
+
211
+ function buildUniqueConstraintExists(provider: Provider): (raw: string, name: string) => string {
212
+ return (raw, name) => {
213
+ const { schema, name: table } = splitQualified(raw);
214
+ const tableLiteral = sqlLiteral(table);
215
+ const constraintLiteral = sqlLiteral(name);
216
+ switch (provider) {
217
+ case 'mssql':
218
+ return `SELECT name FROM sys.objects WHERE type = 'UQ' AND parent_object_id = OBJECT_ID(${sqlLiteral(objectName(raw))}) AND name = ${constraintLiteral}`;
219
+ case 'postgres':
220
+ return (
221
+ `SELECT con.conname FROM pg_constraint con ` +
222
+ `JOIN pg_class t ON t.oid = con.conrelid AND t.relname = ${tableLiteral} ` +
223
+ `JOIN pg_namespace n ON n.oid = t.relnamespace ` +
224
+ `WHERE con.contype = 'u' AND con.conname = ${constraintLiteral} ` +
225
+ `AND n.nspname = ${schema ? sqlLiteral(schema) : defaultSchema(provider, undefined)}`
226
+ );
227
+ case 'mysql':
228
+ return (
229
+ `SELECT constraint_name FROM information_schema.table_constraints ` +
230
+ `WHERE table_schema = ${schema ? sqlLiteral(schema) : 'DATABASE()'} AND table_name = ${tableLiteral} ` +
231
+ `AND constraint_name = ${constraintLiteral} AND constraint_type = 'UNIQUE'`
232
+ );
233
+ case 'sqlite':
234
+ // No named constraints to look for; the unique index carries the name, so
235
+ // the index catalog answers for it.
236
+ return buildIndexExists(provider)(raw, name);
237
+ default:
238
+ return '';
239
+ }
240
+ };
241
+ }
242
+
243
+ /**
244
+ * The keyword that makes a column auto-incrementing, or an empty string when the
245
+ * expression did not ask for one.
246
+ *
247
+ * SQLite is checked rather than passed through: it accepts AUTOINCREMENT only on
248
+ * an `INTEGER PRIMARY KEY`, so a schema asking for `@default(autoincrement())` on
249
+ * anything else has to hear about it here rather than from the engine.
250
+ */
251
+ function identityKeyword(column: ColumnSpec, provider: Provider): string {
252
+ if (column.defaultExpr?.trim() !== 'autoincrement()') return '';
253
+ switch (provider) {
254
+ case 'mssql':
255
+ return 'IDENTITY(1,1)';
256
+ case 'postgres':
257
+ return 'GENERATED BY DEFAULT AS IDENTITY';
258
+ case 'mysql':
259
+ return 'AUTO_INCREMENT';
260
+ case 'sqlite': {
261
+ // The whole declared type, not the base name: `INTEGER(8)` is not an
262
+ // INTEGER PRIMARY KEY as far as SQLite is concerned, and letting it through
263
+ // would replace this message with the engine's.
264
+ const declared = column.sqlType.trim().toUpperCase();
265
+ if (!column.isId) {
266
+ throw new Error(
267
+ `SQLite: @default(autoincrement()) on "${column.name}" needs @id — ` +
268
+ 'SQLite only auto-increments an INTEGER PRIMARY KEY.',
269
+ );
270
+ }
271
+ if (declared !== 'INTEGER') {
272
+ throw new Error(
273
+ `SQLite: @default(autoincrement()) on "${column.name}" needs the type INTEGER, ` +
274
+ `not ${column.sqlType} — SQLite only auto-increments an INTEGER PRIMARY KEY.`,
275
+ );
276
+ }
277
+ return 'AUTOINCREMENT';
278
+ }
279
+ default:
280
+ return '';
281
+ }
282
+ }
283
+
284
+ function createDialect(provider: Provider, quote: (name: string) => string): PushDialect {
285
+ const supportsUniqueInlineOnAddColumn = provider !== 'sqlite';
286
+ const quoteTable = (raw: string): string => {
287
+ const { schema, name } = splitQualified(raw);
288
+ return schema ? `${quote(schema)}.${quote(name)}` : quote(name);
289
+ };
290
+ const list = (fields: string[]): string => fields.join(', ');
291
+
292
+ // Named rather than a method so `addColumn` keeps working if the dialect is
293
+ // destructured by a caller.
294
+ const columnDefinition = (column: ColumnSpec): string => {
295
+ const identity = identityKeyword(column, provider);
296
+ // Where the identity keyword goes is not a detail: SQL Server takes it as part
297
+ // of the column type (`INT IDENTITY(1,1)`), while SQLite only accepts it after
298
+ // `PRIMARY KEY` and only spells the documented form `INTEGER PRIMARY KEY
299
+ // AUTOINCREMENT`.
300
+ const type = identity && provider !== 'sqlite'
301
+ ? `${quote(column.name)} ${column.sqlType} ${identity}`
302
+ : `${quote(column.name)} ${column.sqlType}`;
303
+ const nullable = column.isOptional ? '' : ' NOT NULL';
304
+ const unique = column.isUnique && !column.isId ? ' UNIQUE' : '';
305
+ const defaultSql = defaultClause(column.defaultExpr, provider);
306
+ const defaultPart = defaultSql ? ` ${defaultSql}` : '';
307
+
308
+ if (column.isId) {
309
+ const key = `${type} PRIMARY KEY`;
310
+ return `${key}${identity && provider === 'sqlite' ? ` ${identity}` : ''}${defaultPart}`;
311
+ }
312
+ return `${type}${nullable}${defaultPart}${unique}`;
313
+ };
314
+
315
+ return {
316
+ provider,
317
+ supportsDdl: true,
318
+ quote,
319
+ quoteTable,
320
+ supportsNamedUniqueConstraint: provider !== 'sqlite',
321
+ supportsUniqueInlineOnAddColumn,
322
+ tableExists: buildTableExists(provider),
323
+ columnExists: buildColumnExists(provider),
324
+ uniqueConstraintExists: buildUniqueConstraintExists(provider),
325
+ indexExists: buildIndexExists(provider),
326
+ columnDefinition,
327
+ createTable: (table, columnDefs) => `CREATE TABLE ${table} (\n ${columnDefs.join(',\n ')}\n)`,
328
+ createIndex: (name, table, fields) => `CREATE INDEX ${name} ON ${table} (${list(fields)})`,
329
+ createUniqueIndex: (name, table, fields) => `CREATE UNIQUE INDEX ${name} ON ${table} (${list(fields)})`,
330
+ addColumn: (table, column) => {
331
+ // SQLite refuses "Cannot add a UNIQUE column", so the unique is left out here
332
+ // and added as a separate index by the caller — silently dropping it instead
333
+ // would push a column that is not unique after all.
334
+ const withoutUnique = supportsUniqueInlineOnAddColumn
335
+ ? column
336
+ : { ...column, isUnique: false };
337
+ return `ALTER TABLE ${table} ADD ${columnDefinition(withoutUnique)}`;
338
+ },
339
+ addUniqueConstraint: (name, table, fields) => `ALTER TABLE ${table} ADD CONSTRAINT ${name} UNIQUE (${list(fields)})`,
340
+ };
341
+ }
342
+
343
+ /**
344
+ * Google Sheets has no tables and therefore no DDL: a sheet is a range of cells the
345
+ * adapter reads and writes. Every builder is inert and `supportsDdl` is false, so
346
+ * the caller explains that instead of sending SQL to a database that cannot run it.
347
+ */
348
+ const SHEETS_DIALECT: PushDialect = {
349
+ provider: 'googlesheets',
350
+ supportsDdl: false,
351
+ quote: (name) => name,
352
+ quoteTable: (raw) => objectName(raw),
353
+ supportsNamedUniqueConstraint: false,
354
+ supportsUniqueInlineOnAddColumn: false,
355
+ tableExists: () => '',
356
+ columnExists: () => '',
357
+ uniqueConstraintExists: () => '',
358
+ indexExists: () => '',
359
+ columnDefinition: () => '',
360
+ createTable: () => '',
361
+ createIndex: () => '',
362
+ createUniqueIndex: () => '',
363
+ addColumn: () => '',
364
+ addUniqueConstraint: () => '',
365
+ };
366
+
367
+ /** The dialect that writes DDL for a provider. */
368
+ export function dialectFor(provider: Provider): PushDialect {
369
+ switch (provider) {
370
+ case 'postgres':
371
+ return createDialect(provider, doubleQuote);
372
+ case 'mysql':
373
+ return createDialect(provider, backtickQuote);
374
+ case 'sqlite':
375
+ return createDialect(provider, bracketQuote);
376
+ case 'googlesheets':
377
+ return SHEETS_DIALECT;
378
+ case 'mssql':
379
+ default:
380
+ return createDialect(provider, bracketQuote);
381
+ }
382
+ }
383
+
384
+ /**
385
+ * Renders a `@default(...)` expression as the SQL that follows the column type.
386
+ *
387
+ * Returns an empty string when the provider has no equivalent — SQLite has no
388
+ * default functions, so `uuid()` is left to the adapter, which fills a string
389
+ * primary key in before inserting.
390
+ */
391
+ export function defaultClause(expression: string | undefined, provider: Provider): string {
392
+ const value = (expression ?? '').trim();
393
+ switch (value) {
394
+ case '':
395
+ case 'autoincrement()':
396
+ // Not a default value: it is the identity clause on the column type.
397
+ return '';
398
+ case 'uuid()':
399
+ case 'cuid()':
400
+ if (provider === 'mssql') return 'DEFAULT NEWID()';
401
+ if (provider === 'postgres') return 'DEFAULT gen_random_uuid()';
402
+ if (provider === 'mysql') return 'DEFAULT (UUID())';
403
+ return '';
404
+ case 'now()':
405
+ return 'DEFAULT CURRENT_TIMESTAMP';
406
+ case 'true':
407
+ // PostgreSQL has a real boolean, so use it rather than 1/0.
408
+ return provider === 'postgres' ? 'DEFAULT TRUE' : 'DEFAULT 1';
409
+ case 'false':
410
+ return provider === 'postgres' ? 'DEFAULT FALSE' : 'DEFAULT 0';
411
+ default:
412
+ if (/^".*"$/.test(value)) return `DEFAULT '${value.slice(1, -1).replace(/'/g, "''")}'`;
413
+ return `DEFAULT ${value}`;
414
+ }
415
+ }