uql-orm 0.44.0 → 0.45.1
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 +3 -2
- package/dist/dialect/abstractSqlDialect.d.ts +2 -2
- package/dist/dialect/abstractSqlDialect.js +1 -1
- package/dist/dialect/mysqlLikeSqlDialect.d.ts +8 -1
- package/dist/dialect/mysqlLikeSqlDialect.js +8 -1
- package/dist/dialect/pgLikeSqlDialect.d.ts +1 -1
- package/dist/dialect/pgLikeSqlDialect.js +1 -1
- package/dist/migrate/builder/migrationBuilder.js +1 -2
- package/dist/migrate/builder/tableBuilder.d.ts +1 -0
- package/dist/migrate/builder/tableBuilder.js +9 -10
- package/dist/migrate/builder/types.d.ts +3 -19
- package/dist/migrate/generator/definitionToNode.js +2 -2
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +2 -5
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +2 -0
- package/dist/migrate/introspection/baseSqlIntrospector.js +4 -3
- package/dist/migrate/introspection/mysqlIntrospector.js +1 -2
- package/dist/migrate/introspection/postgresIntrospector.js +1 -2
- package/dist/migrate/introspection/sqliteIntrospector.js +1 -2
- package/dist/migrate/migrator.js +14 -2
- package/dist/migrate/schemaGenerator.d.ts +20 -5
- package/dist/migrate/schemaGenerator.js +117 -33
- package/dist/schema/schemaASTBuilder.js +2 -1
- package/dist/schema/schemaASTDiffer.d.ts +11 -1
- package/dist/schema/schemaASTDiffer.js +23 -7
- package/dist/schema/types.d.ts +19 -4
- package/dist/sqlite/sqliteDialect.d.ts +1 -1
- package/dist/sqlite/sqliteDialect.js +1 -1
- package/dist/type/entity.d.ts +1 -1
- package/dist/type/migration.d.ts +50 -10
- package/dist/util/field.util.d.ts +7 -2
- package/dist/util/field.util.js +9 -12
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
</picture>
|
|
8
8
|
</a>
|
|
9
9
|
|
|
10
|
-
<h3>The JSON-native ORM</h3>
|
|
10
|
+
<h3>The JSON-native TypeScript ORM</h3>
|
|
11
11
|
|
|
12
|
-
<p>
|
|
12
|
+
<p>UQL stands for Unified Query Language. With pure (type-safe) JSON queries, complex jobs can be done simply across SQL vendors + MongoDB. It got some inspiration from Mongo's best syntax.
|
|
13
|
+
</p>
|
|
13
14
|
|
|
14
15
|
<p>
|
|
15
16
|
<a href="https://uql-orm.dev"><b>Website</b></a> ·
|
|
@@ -16,9 +16,9 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
|
|
|
16
16
|
* declares over its columns as one named constraint. SQLite is the exception - see
|
|
17
17
|
* {@link serialDeclaresPrimaryKey}.
|
|
18
18
|
*/
|
|
19
|
-
abstract readonly
|
|
19
|
+
abstract readonly autoIncrementSuffix: string;
|
|
20
20
|
/**
|
|
21
|
-
* Whether {@link
|
|
21
|
+
* Whether {@link autoIncrementSuffix} states `PRIMARY KEY` itself, so the table must not state it again.
|
|
22
22
|
*
|
|
23
23
|
* True on SQLite alone, where `AUTOINCREMENT` is legal only in the exact phrase
|
|
24
24
|
* `INTEGER PRIMARY KEY AUTOINCREMENT` - the key cannot be lifted out of the column there.
|
|
@@ -11,7 +11,7 @@ import { resolveVectorCast } from './vectorCast.js';
|
|
|
11
11
|
import { VectorSqlDialect } from './vectorSqlDialect.js';
|
|
12
12
|
export class AbstractSqlDialect extends VectorSqlDialect {
|
|
13
13
|
/**
|
|
14
|
-
* Whether {@link
|
|
14
|
+
* Whether {@link autoIncrementSuffix} states `PRIMARY KEY` itself, so the table must not state it again.
|
|
15
15
|
*
|
|
16
16
|
* True on SQLite alone, where `AUTOINCREMENT` is legal only in the exact phrase
|
|
17
17
|
* `INTEGER PRIMARY KEY AUTOINCREMENT` - the key cannot be lifted out of the column there.
|
|
@@ -24,7 +24,14 @@ export declare abstract class MysqlLikeSqlDialect extends AbstractSqlDialect {
|
|
|
24
24
|
estimatedCount<E>(ctx: QueryContext, entity: Type<E>): void;
|
|
25
25
|
/** `OFFSET` is only legal after a `LIMIT` here, so a bare `$skip` needs one. */
|
|
26
26
|
pager(ctx: QueryContext, opts: QueryPager): void;
|
|
27
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Signed, though MySQL's own convention is `UNSIGNED`: a foreign key column takes its type from the
|
|
29
|
+
* key it points at, resolved through the *canonical* type, which has no way to know this string said
|
|
30
|
+
* `UNSIGNED`. The two then disagree and the engine refuses the constraint - the same trap knex hit
|
|
31
|
+
* (knex#6129) and MikroORM still carries (mikro-orm#5485). Signed is also the portable half: no
|
|
32
|
+
* other engine here has unsigned integers, so an `@Id` means one range everywhere.
|
|
33
|
+
*/
|
|
34
|
+
readonly autoIncrementSuffix = "AUTO_INCREMENT";
|
|
28
35
|
readonly escapeIdChar = "`";
|
|
29
36
|
readonly tableOptions = "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4";
|
|
30
37
|
readonly beginTransactionCommand = "START TRANSACTION";
|
|
@@ -63,7 +63,14 @@ export class MysqlLikeSqlDialect extends AbstractSqlDialect {
|
|
|
63
63
|
}
|
|
64
64
|
super.pager(ctx, opts);
|
|
65
65
|
}
|
|
66
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Signed, though MySQL's own convention is `UNSIGNED`: a foreign key column takes its type from the
|
|
68
|
+
* key it points at, resolved through the *canonical* type, which has no way to know this string said
|
|
69
|
+
* `UNSIGNED`. The two then disagree and the engine refuses the constraint - the same trap knex hit
|
|
70
|
+
* (knex#6129) and MikroORM still carries (mikro-orm#5485). Signed is also the portable half: no
|
|
71
|
+
* other engine here has unsigned integers, so an `@Id` means one range everywhere.
|
|
72
|
+
*/
|
|
73
|
+
autoIncrementSuffix = 'AUTO_INCREMENT';
|
|
67
74
|
escapeIdChar = '`';
|
|
68
75
|
tableOptions = 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4';
|
|
69
76
|
beginTransactionCommand = 'START TRANSACTION';
|
|
@@ -15,7 +15,7 @@ export declare abstract class PgLikeSqlDialect extends AbstractSqlDialect {
|
|
|
15
15
|
/** Default {@link DialectFeatures} for Postgres-wire dialects. */
|
|
16
16
|
protected readonly featureDefaults: DialectFeatures;
|
|
17
17
|
readonly escapeIdChar = "\"";
|
|
18
|
-
readonly
|
|
18
|
+
readonly autoIncrementSuffix: string;
|
|
19
19
|
readonly tableOptions = "";
|
|
20
20
|
readonly beginTransactionCommand = "BEGIN";
|
|
21
21
|
readonly commitTransactionCommand = "COMMIT";
|
|
@@ -40,7 +40,7 @@ export class PgLikeSqlDialect extends AbstractSqlDialect {
|
|
|
40
40
|
// under heavy concurrent insert load (writes concentrate on one range); this default still beats
|
|
41
41
|
// `SERIAL` (CockroachDB's `unique_rowid()`, a ~64-bit value that overflows JS's safe-integer
|
|
42
42
|
// range). High-throughput CockroachDB users should override this per-entity with a UUID PK.
|
|
43
|
-
|
|
43
|
+
autoIncrementSuffix = 'GENERATED BY DEFAULT AS IDENTITY';
|
|
44
44
|
tableOptions = '';
|
|
45
45
|
beginTransactionCommand = 'BEGIN';
|
|
46
46
|
commitTransactionCommand = 'COMMIT';
|
|
@@ -49,8 +49,7 @@ function addForeignKeyOperation(tableName, columns, target, options = {}) {
|
|
|
49
49
|
foreignKey: {
|
|
50
50
|
name: options.name,
|
|
51
51
|
columns,
|
|
52
|
-
|
|
53
|
-
referencesColumns: target.columns,
|
|
52
|
+
references: { table: target.table, columns: target.columns },
|
|
54
53
|
onDelete: options.onDelete ?? 'NO ACTION',
|
|
55
54
|
onUpdate: options.onUpdate ?? 'NO ACTION',
|
|
56
55
|
},
|
|
@@ -16,6 +16,7 @@ export declare class TableBuilder implements ITableBuilder {
|
|
|
16
16
|
private _foreignKeyBuilders;
|
|
17
17
|
private _comment?;
|
|
18
18
|
constructor(name: string);
|
|
19
|
+
/** Big, matching an entity's `@Id`: a key is spelled from this type, so it has to state the real one. */
|
|
19
20
|
id(name?: string, options?: BaseColumnOptions): IColumnBuilder;
|
|
20
21
|
integer(name: string, options?: BaseColumnOptions): IColumnBuilder;
|
|
21
22
|
smallint(name: string, options?: BaseColumnOptions): IColumnBuilder;
|
|
@@ -12,8 +12,8 @@ import { expr } from './expressions.js';
|
|
|
12
12
|
*/
|
|
13
13
|
class TableForeignKeyBuilder {
|
|
14
14
|
_columns;
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
_referencedTable;
|
|
16
|
+
_referencedColumns = [];
|
|
17
17
|
_onDelete = 'NO ACTION';
|
|
18
18
|
_onUpdate = 'NO ACTION';
|
|
19
19
|
_name;
|
|
@@ -21,8 +21,8 @@ class TableForeignKeyBuilder {
|
|
|
21
21
|
this._columns = columns;
|
|
22
22
|
}
|
|
23
23
|
references(table, columns) {
|
|
24
|
-
this.
|
|
25
|
-
this.
|
|
24
|
+
this._referencedTable = table;
|
|
25
|
+
this._referencedColumns = columns;
|
|
26
26
|
return this;
|
|
27
27
|
}
|
|
28
28
|
onDelete(action) {
|
|
@@ -41,13 +41,12 @@ class TableForeignKeyBuilder {
|
|
|
41
41
|
* Build the foreign key definition.
|
|
42
42
|
*/
|
|
43
43
|
build() {
|
|
44
|
-
if (!this.
|
|
44
|
+
if (!this._referencedTable)
|
|
45
45
|
return undefined;
|
|
46
46
|
return {
|
|
47
47
|
name: this._name,
|
|
48
48
|
columns: this._columns,
|
|
49
|
-
|
|
50
|
-
referencesColumns: this._referencesColumns,
|
|
49
|
+
references: { table: this._referencedTable, columns: this._referencedColumns },
|
|
51
50
|
onDelete: this._onDelete,
|
|
52
51
|
onUpdate: this._onUpdate,
|
|
53
52
|
};
|
|
@@ -66,8 +65,9 @@ export class TableBuilder {
|
|
|
66
65
|
constructor(name) {
|
|
67
66
|
this._name = name;
|
|
68
67
|
}
|
|
68
|
+
/** Big, matching an entity's `@Id`: a key is spelled from this type, so it has to state the real one. */
|
|
69
69
|
id(name = 'id', options = {}) {
|
|
70
|
-
return this.add(name, { category: 'integer' }, { ...options, primaryKey: true, autoIncrement: true });
|
|
70
|
+
return this.add(name, { category: 'integer', size: 'big' }, { ...options, primaryKey: true, autoIncrement: true });
|
|
71
71
|
}
|
|
72
72
|
integer(name, options) {
|
|
73
73
|
return this.add(name, { category: 'integer' }, options);
|
|
@@ -217,8 +217,7 @@ export class TableBuilder {
|
|
|
217
217
|
foreignKeys.push({
|
|
218
218
|
name: col.foreignKey.name,
|
|
219
219
|
columns: [col.name],
|
|
220
|
-
|
|
221
|
-
referencesColumns: col.foreignKey.columns,
|
|
220
|
+
references: { table: col.foreignKey.table, columns: col.foreignKey.columns },
|
|
222
221
|
onDelete: col.foreignKey.onDelete,
|
|
223
222
|
onUpdate: col.foreignKey.onUpdate,
|
|
224
223
|
});
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { CanonicalType, ForeignKeyAction } from '../../schema/types.js';
|
|
8
8
|
import type { IndexColumnInput, IndexOptions, IndexSchema } from '../../type/index.js';
|
|
9
|
+
import type { ForeignKeySchema } from '../../type/migration.js';
|
|
9
10
|
/**
|
|
10
11
|
* Foreign key reference options.
|
|
11
12
|
*/
|
|
@@ -124,27 +125,10 @@ export interface TableDefinition {
|
|
|
124
125
|
/** Index definitions */
|
|
125
126
|
indexes: IndexSchema[];
|
|
126
127
|
/** Foreign key definitions at table level */
|
|
127
|
-
foreignKeys:
|
|
128
|
+
foreignKeys: ForeignKeySchema[];
|
|
128
129
|
/** Table comment */
|
|
129
130
|
comment?: string;
|
|
130
131
|
}
|
|
131
|
-
/**
|
|
132
|
-
* Table-level foreign key (for composite FKs).
|
|
133
|
-
*/
|
|
134
|
-
export interface TableForeignKeyDefinition {
|
|
135
|
-
/** Constraint name */
|
|
136
|
-
name?: string;
|
|
137
|
-
/** Local columns */
|
|
138
|
-
columns: string[];
|
|
139
|
-
/** Referenced table */
|
|
140
|
-
referencesTable: string;
|
|
141
|
-
/** Referenced columns */
|
|
142
|
-
referencesColumns: string[];
|
|
143
|
-
/** Action on delete */
|
|
144
|
-
onDelete: ForeignKeyAction;
|
|
145
|
-
/** Action on update */
|
|
146
|
-
onUpdate: ForeignKeyAction;
|
|
147
|
-
}
|
|
148
132
|
/**
|
|
149
133
|
* Type of migration operation.
|
|
150
134
|
*/
|
|
@@ -238,7 +222,7 @@ export interface DropIndexOperation extends MigrationOperation {
|
|
|
238
222
|
export interface AddForeignKeyOperation extends MigrationOperation {
|
|
239
223
|
type: 'addForeignKey';
|
|
240
224
|
tableName: string;
|
|
241
|
-
foreignKey:
|
|
225
|
+
foreignKey: ForeignKeySchema;
|
|
242
226
|
}
|
|
243
227
|
/**
|
|
244
228
|
* Drop foreign key operation.
|
|
@@ -40,8 +40,8 @@ export function tableDefinitionToNode(def) {
|
|
|
40
40
|
columns: fkDef.columns.map((name) => columns.get(name)).filter((c) => c !== undefined),
|
|
41
41
|
},
|
|
42
42
|
to: {
|
|
43
|
-
table: { name: fkDef.
|
|
44
|
-
columns: fkDef.
|
|
43
|
+
table: { name: fkDef.references.table },
|
|
44
|
+
columns: fkDef.references.columns.map((name) => ({ name })),
|
|
45
45
|
},
|
|
46
46
|
onDelete: fkDef.onDelete,
|
|
47
47
|
onUpdate: fkDef.onUpdate,
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
+
import type { ForeignKeyAction } from '../../schema/types.js';
|
|
1
2
|
import type { ColumnSchema, ForeignKeySchema, IndexSchema, QuerierPool, RawRow, SchemaIntrospector, SqlQuerier, TableSchema } from '../../type/index.js';
|
|
2
3
|
import { BaseSqlIntrospector } from './baseSqlIntrospector.js';
|
|
3
|
-
/**
|
|
4
|
-
* Referential action type for foreign key constraints.
|
|
5
|
-
*/
|
|
6
|
-
export type ReferentialAction = 'CASCADE' | 'SET NULL' | 'RESTRICT' | 'NO ACTION';
|
|
7
4
|
/**
|
|
8
5
|
* Reads the rows of one statement while introspecting a table.
|
|
9
6
|
*
|
|
@@ -72,7 +69,7 @@ export declare abstract class AbstractSqlSchemaIntrospector extends BaseSqlIntro
|
|
|
72
69
|
/**
|
|
73
70
|
* Normalize referential action string to standard type.
|
|
74
71
|
*/
|
|
75
|
-
protected normalizeReferentialAction(action: string):
|
|
72
|
+
protected normalizeReferentialAction(action: string): ForeignKeyAction | undefined;
|
|
76
73
|
/**
|
|
77
74
|
* Convert bigint/null values to number safely.
|
|
78
75
|
*/
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { canonicalColumnType } from '../../schema/canonicalType.js';
|
|
2
2
|
import { createTableNode, SchemaAST } from '../../schema/schemaAST.js';
|
|
3
3
|
import { escapeSqlId } from '../../util/index.js';
|
|
4
|
+
import { derivedForeignKeyName } from '../../util/sql.util.js';
|
|
4
5
|
/**
|
|
5
6
|
* Base class for SQL introspectors with shared AST building logic.
|
|
6
7
|
*/
|
|
@@ -83,14 +84,14 @@ export class BaseSqlIntrospector {
|
|
|
83
84
|
if (!fromTable)
|
|
84
85
|
continue;
|
|
85
86
|
for (const fk of schema.foreignKeys) {
|
|
86
|
-
const toTable = tableNodes.get(fk.
|
|
87
|
+
const toTable = tableNodes.get(fk.references.table);
|
|
87
88
|
if (!toTable)
|
|
88
89
|
continue;
|
|
89
90
|
const fromColumns = fk.columns.flatMap((name) => fromTable.columns.get(name) ?? []);
|
|
90
|
-
const toColumns = fk.
|
|
91
|
+
const toColumns = fk.references.columns.flatMap((name) => toTable.columns.get(name) ?? []);
|
|
91
92
|
if (fromColumns.length > 0 && toColumns.length > 0) {
|
|
92
93
|
const rel = {
|
|
93
|
-
name: fk.name,
|
|
94
|
+
name: fk.name ?? derivedForeignKeyName(schema.name, fk.columns),
|
|
94
95
|
type: fromColumns[0].isUnique ? 'OneToOne' : 'ManyToOne',
|
|
95
96
|
from: { table: fromTable, columns: fromColumns },
|
|
96
97
|
to: { table: toTable, columns: toColumns },
|
|
@@ -123,8 +123,7 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
123
123
|
return results.map((row) => ({
|
|
124
124
|
name: row.constraint_name,
|
|
125
125
|
columns: (row.columns || '').split(','),
|
|
126
|
-
|
|
127
|
-
referencedColumns: (row.referenced_columns || '').split(','),
|
|
126
|
+
references: { table: row.referenced_table, columns: (row.referenced_columns || '').split(',') },
|
|
128
127
|
onDelete: this.normalizeReferentialAction(row.delete_rule),
|
|
129
128
|
onUpdate: this.normalizeReferentialAction(row.update_rule),
|
|
130
129
|
}));
|
|
@@ -204,8 +204,7 @@ export class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
204
204
|
return results.map((row) => ({
|
|
205
205
|
name: row.constraint_name,
|
|
206
206
|
columns: row.columns,
|
|
207
|
-
|
|
208
|
-
referencedColumns: row.referenced_columns,
|
|
207
|
+
references: { table: row.referenced_table, columns: row.referenced_columns },
|
|
209
208
|
onDelete: this.normalizeReferentialAction(row.delete_rule),
|
|
210
209
|
onUpdate: this.normalizeReferentialAction(row.update_rule),
|
|
211
210
|
}));
|
|
@@ -113,8 +113,7 @@ export class SqliteSchemaIntrospector extends AbstractSqlSchemaIntrospector {
|
|
|
113
113
|
// derives it. Seeded from the columns, not the PRAGMA's row id, which nothing else knows.
|
|
114
114
|
name: derivedForeignKeyName(tableName, columns),
|
|
115
115
|
columns,
|
|
116
|
-
|
|
117
|
-
referencedColumns: rows.map((r) => r.to),
|
|
116
|
+
references: { table: first.table, columns: rows.map((r) => r.to) },
|
|
118
117
|
onDelete: this.normalizeReferentialAction(first.on_delete),
|
|
119
118
|
onUpdate: this.normalizeReferentialAction(first.on_update),
|
|
120
119
|
};
|
package/dist/migrate/migrator.js
CHANGED
|
@@ -282,12 +282,16 @@ export class Migrator {
|
|
|
282
282
|
throw new TypeError('Schema generator and introspector must be set');
|
|
283
283
|
}
|
|
284
284
|
const ast = await this.introspectClaimedSchemas();
|
|
285
|
+
// Both sides built once: the database's above, the entities' here. Left to `diffSchema`, each
|
|
286
|
+
// entity would rebuild the whole AST, which is quadratic in the number of entities. Absent on a
|
|
287
|
+
// generator that compares no schema of its own - MongoDB, which reads only indexes.
|
|
288
|
+
const desiredAst = this.schemaGenerator.buildAST?.(this.entities);
|
|
285
289
|
const diffs = [];
|
|
286
290
|
for (const entity of this.entities) {
|
|
287
291
|
const meta = getMeta(entity);
|
|
288
292
|
const tableName = this.schemaGenerator.resolveTableName(meta);
|
|
289
293
|
const currentTable = ast.getTable(tableName);
|
|
290
|
-
const diff = this.schemaGenerator.diffSchema(entity, currentTable);
|
|
294
|
+
const diff = this.schemaGenerator.diffSchema(entity, currentTable, desiredAst);
|
|
291
295
|
if (diff) {
|
|
292
296
|
diffs.push(diff);
|
|
293
297
|
}
|
|
@@ -390,7 +394,9 @@ export class Migrator {
|
|
|
390
394
|
}
|
|
391
395
|
/** The same for one entity against the table it already has, and nothing where the two agree. */
|
|
392
396
|
alterFromEntity(entity, table, options) {
|
|
393
|
-
|
|
397
|
+
// Spanning the set for the reason `planEntity` spells out: a foreign key needs the table it
|
|
398
|
+
// points at, which a sync of one entity outside the configured list would not otherwise have.
|
|
399
|
+
const diff = this.generator.diffSchema(entity, table, this.generator.buildAST?.(this.entitiesWith(entity)));
|
|
394
400
|
return diff?.type === 'alter' ? this.alterFromDiff(diff, options) : [];
|
|
395
401
|
}
|
|
396
402
|
/** The configured entities, with `entity` among them however the migrator was built. */
|
|
@@ -480,6 +486,12 @@ export class Migrator {
|
|
|
480
486
|
this.logger.logSkippedMigration(`[AutoSync] Skipped changing the primary key of '${diff.tableName}' from (${filteredDiff.primaryKey.from.join(', ')}) to (${filteredDiff.primaryKey.to.join(', ')}) (safe mode active). Use a migration or { safe: false } to apply.`);
|
|
481
487
|
delete filteredDiff.primaryKey;
|
|
482
488
|
}
|
|
489
|
+
if (filteredDiff.foreignKeysToAlter?.length) {
|
|
490
|
+
// Altering one is dropping it and adding it back, so letting the add through while the drop
|
|
491
|
+
// is held would emit `ADD CONSTRAINT` for a constraint the table still has.
|
|
492
|
+
this.logger.logSkippedMigration(`[AutoSync] Skipped altering ${filteredDiff.foreignKeysToAlter.length} foreign keys in table '${diff.tableName}': ${filteredDiff.foreignKeysToAlter.map((fk) => fk.to.name).join(', ')} (safe mode active). Use a migration or { safe: false } to apply.`);
|
|
493
|
+
delete filteredDiff.foreignKeysToAlter;
|
|
494
|
+
}
|
|
483
495
|
delete filteredDiff.indexesToDrop;
|
|
484
496
|
delete filteredDiff.foreignKeysToDrop;
|
|
485
497
|
}
|
|
@@ -2,8 +2,8 @@ import { type AbstractDialect, AbstractSqlDialect } from '../dialect/index.js';
|
|
|
2
2
|
import type { SchemaAST } from '../schema/schemaAST.js';
|
|
3
3
|
import { type DiffOptions } from '../schema/schemaASTDiffer.js';
|
|
4
4
|
import type { CanonicalType, ColumnNode, ForeignKeyAction, IndexNode, TableNode } from '../schema/types.js';
|
|
5
|
-
import type { ColumnSchema, CreateSchemaOptions, DialectFeatures, DropSchemaOptions, EntityMeta, FieldMeta, FieldOptions, IndexSchema, NamingStrategy, SchemaDiff, SchemaGenerator, SqlDdlGenerator, Type } from '../type/index.js';
|
|
6
|
-
import type { FullColumnDefinition, TableDefinition
|
|
5
|
+
import type { ColumnSchema, CreateSchemaOptions, DialectFeatures, DropSchemaOptions, EntityMeta, FieldMeta, FieldOptions, ForeignKeySchema, IndexSchema, NamingStrategy, SchemaDiff, SchemaGenerator, SqlDdlGenerator, Type } from '../type/index.js';
|
|
6
|
+
import type { FullColumnDefinition, TableDefinition } from './builder/types.js';
|
|
7
7
|
import { type IndexDdl } from './ddl/index.js';
|
|
8
8
|
/**
|
|
9
9
|
* Unified SQL schema generator.
|
|
@@ -26,8 +26,19 @@ export declare class SqlSchemaGenerator implements SqlDdlGenerator {
|
|
|
26
26
|
/**
|
|
27
27
|
* Primary key type for auto-increment integer IDs
|
|
28
28
|
*/
|
|
29
|
-
|
|
29
|
+
/**
|
|
30
|
+
* How an auto-increment key of `type` is spelled: the type as any other column renders it, plus what
|
|
31
|
+
* the engine appends to make it generated.
|
|
32
|
+
*
|
|
33
|
+
* Derived rather than a fixed string per dialect, because a foreign key column takes its type from
|
|
34
|
+
* the key it points at, resolved through the same canonical type. A key whose spelling ignored that
|
|
35
|
+
* type could never be referenced: `@Id({ columnType: 'int' })` emitted `BIGINT` while the column
|
|
36
|
+
* pointing at it emitted `INT`, and every engine refuses that constraint.
|
|
37
|
+
*/
|
|
38
|
+
protected serialType(type: CanonicalType): string;
|
|
30
39
|
protected canonicalTypeToSql(type: CanonicalType): string;
|
|
40
|
+
/** The entity side as an AST, carrying this generator's default referential action. */
|
|
41
|
+
buildAST(entities: readonly Type<unknown>[]): SchemaAST;
|
|
31
42
|
/**
|
|
32
43
|
* Every `CREATE TABLE` for `entities`, then their foreign keys.
|
|
33
44
|
*
|
|
@@ -53,6 +64,10 @@ export declare class SqlSchemaGenerator implements SqlDdlGenerator {
|
|
|
53
64
|
private orderedTables;
|
|
54
65
|
generateDropTable(tableName: string, options?: DropSchemaOptions): string;
|
|
55
66
|
generateAlterTable(diff: SchemaDiff): string[];
|
|
67
|
+
/** `ADD CONSTRAINT` for each of `foreignKeys`. */
|
|
68
|
+
private addForeignKeyStatements;
|
|
69
|
+
/** `DROP CONSTRAINT` for each of `constraintNames`, the mirror of {@link addForeignKeyStatements}. */
|
|
70
|
+
private dropForeignKeyStatements;
|
|
56
71
|
generateAlterTableDown(diff: SchemaDiff): string[];
|
|
57
72
|
generateCreateIndex(tableName: string, index: IndexSchema, options?: {
|
|
58
73
|
ifNotExists?: boolean;
|
|
@@ -100,7 +115,7 @@ export declare class SqlSchemaGenerator implements SqlDdlGenerator {
|
|
|
100
115
|
* longer disagree about what has changed. Only two things are this side's own: the entity becomes a
|
|
101
116
|
* table node first, and types are compared as the *engine* would store them - see `normalizeType`.
|
|
102
117
|
*/
|
|
103
|
-
diffSchema<E>(entity: Type<E>, currentTable: TableNode | undefined): SchemaDiff | undefined;
|
|
118
|
+
diffSchema<E>(entity: Type<E>, currentTable: TableNode | undefined, desiredAst?: SchemaAST): SchemaDiff | undefined;
|
|
104
119
|
/**
|
|
105
120
|
* What the shared differ needs from a dialect: a type as this engine would actually store it.
|
|
106
121
|
*
|
|
@@ -144,7 +159,7 @@ export declare class SqlSchemaGenerator implements SqlDdlGenerator {
|
|
|
144
159
|
generateAlterColumnSql(tableName: string, columnName: string, column: FullColumnDefinition): string;
|
|
145
160
|
generateDropColumnSql(tableName: string, columnName: string): string;
|
|
146
161
|
generateRenameColumnSql(tableName: string, oldName: string, newName: string): string;
|
|
147
|
-
generateAddForeignKeySql(tableName: string, foreignKey:
|
|
162
|
+
generateAddForeignKeySql(tableName: string, foreignKey: ForeignKeySchema): string;
|
|
148
163
|
generateDropForeignKeySql(tableName: string, constraintName: string): string;
|
|
149
164
|
/**
|
|
150
165
|
* `ALTER TABLE ... ADD CONSTRAINT <name> PRIMARY KEY (...)`, the other half of
|
|
@@ -3,7 +3,7 @@ import { getMeta, soleIdOf } from '../entity/index.js';
|
|
|
3
3
|
import { areTypesEqual, canonicalToSql, engineType, fieldOptionsToCanonical, isVectorCategory, } from '../schema/canonicalType.js';
|
|
4
4
|
import { indexSignature } from '../schema/indexDifferences.js';
|
|
5
5
|
import { buildSchemaAST } from '../schema/schemaASTBuilder.js';
|
|
6
|
-
import { diffTable } from '../schema/schemaASTDiffer.js';
|
|
6
|
+
import { diffRelationshipNodes, diffTable } from '../schema/schemaASTDiffer.js';
|
|
7
7
|
import { getKeys, isAutoIncrement, isSoleIdField, qualifyName } from '../util/index.js';
|
|
8
8
|
import { derivedCheckName, derivedForeignKeyName, derivedPrimaryKeyName } from '../util/sql.util.js';
|
|
9
9
|
import { formatDefaultValue, SqlExpression } from './builder/expressions.js';
|
|
@@ -49,12 +49,25 @@ export class SqlSchemaGenerator {
|
|
|
49
49
|
/**
|
|
50
50
|
* Primary key type for auto-increment integer IDs
|
|
51
51
|
*/
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
/**
|
|
53
|
+
* How an auto-increment key of `type` is spelled: the type as any other column renders it, plus what
|
|
54
|
+
* the engine appends to make it generated.
|
|
55
|
+
*
|
|
56
|
+
* Derived rather than a fixed string per dialect, because a foreign key column takes its type from
|
|
57
|
+
* the key it points at, resolved through the same canonical type. A key whose spelling ignored that
|
|
58
|
+
* type could never be referenced: `@Id({ columnType: 'int' })` emitted `BIGINT` while the column
|
|
59
|
+
* pointing at it emitted `INT`, and every engine refuses that constraint.
|
|
60
|
+
*/
|
|
61
|
+
serialType(type) {
|
|
62
|
+
return `${this.canonicalTypeToSql(type)} ${this.dialect.autoIncrementSuffix}`;
|
|
54
63
|
}
|
|
55
64
|
canonicalTypeToSql(type) {
|
|
56
65
|
return canonicalToSql(type, this.dialect);
|
|
57
66
|
}
|
|
67
|
+
/** The entity side as an AST, carrying this generator's default referential action. */
|
|
68
|
+
buildAST(entities) {
|
|
69
|
+
return buildEntityAST(this, entities, this.defaultForeignKeyAction);
|
|
70
|
+
}
|
|
58
71
|
/**
|
|
59
72
|
* Every `CREATE TABLE` for `entities`, then their foreign keys.
|
|
60
73
|
*
|
|
@@ -76,16 +89,7 @@ export class SqlSchemaGenerator {
|
|
|
76
89
|
statements.push(...tables.flatMap((table) => this.generateCreateTableFromNode(inline ? table : { ...table, outgoingRelations: [] }, options)));
|
|
77
90
|
if (withForeignKeys && !inline) {
|
|
78
91
|
for (const table of tables) {
|
|
79
|
-
|
|
80
|
-
statements.push(this.generateAddForeignKeySql(qualifyName(table.name, table.schema), {
|
|
81
|
-
name: rel.name,
|
|
82
|
-
columns: rel.from.columns.map((c) => c.name),
|
|
83
|
-
referencesTable: qualifyName(rel.to.table.name, rel.to.table.schema),
|
|
84
|
-
referencesColumns: rel.to.columns.map((c) => c.name),
|
|
85
|
-
onDelete: rel.onDelete ?? this.defaultForeignKeyAction,
|
|
86
|
-
onUpdate: rel.onUpdate ?? this.defaultForeignKeyAction,
|
|
87
|
-
}));
|
|
88
|
-
}
|
|
92
|
+
statements.push(...this.addForeignKeyStatements(qualifyName(table.name, table.schema), table.outgoingRelations.map(foreignKeyOf)));
|
|
89
93
|
}
|
|
90
94
|
}
|
|
91
95
|
return statements;
|
|
@@ -129,11 +133,16 @@ export class SqlSchemaGenerator {
|
|
|
129
133
|
if (diff.primaryKey?.from.length) {
|
|
130
134
|
statements.push(this.generateDropPrimaryKeySql(diff.tableName, diff.primaryKey.fromName));
|
|
131
135
|
}
|
|
136
|
+
// Before the columns: a constraint holds its columns down, so one the entity dropped cannot go
|
|
137
|
+
// while a foreign key still names it. An alter is a drop and an add, and this is its drop half.
|
|
138
|
+
statements.push(...this.dropForeignKeyStatements(diff.tableName, [
|
|
139
|
+
...(diff.foreignKeysToDrop ?? []),
|
|
140
|
+
...(diff.foreignKeysToAlter ?? []).map((it) => constraintNameOf(diff.tableName, it.from)),
|
|
141
|
+
]));
|
|
132
142
|
// Add new columns
|
|
133
143
|
if (diff.columnsToAdd?.length) {
|
|
134
144
|
for (const column of diff.columnsToAdd) {
|
|
135
|
-
|
|
136
|
-
statements.push(`ALTER TABLE ${tableName} ADD COLUMN ${colDef};`);
|
|
145
|
+
statements.push(`ALTER TABLE ${tableName} ADD COLUMN ${this.generateColumnDefinitionFromSchema(column)};`);
|
|
137
146
|
}
|
|
138
147
|
}
|
|
139
148
|
// Alter existing columns
|
|
@@ -166,14 +175,34 @@ export class SqlSchemaGenerator {
|
|
|
166
175
|
if (diff.primaryKey?.to.length) {
|
|
167
176
|
statements.push(this.generateAddPrimaryKeySql(diff.tableName, diff.primaryKey.to));
|
|
168
177
|
}
|
|
178
|
+
// After the columns, for the same reason the key is: a constraint cannot name one that is not
|
|
179
|
+
// there yet. The add half of an alter rides along, its drop having gone out above.
|
|
180
|
+
statements.push(...this.addForeignKeyStatements(diff.tableName, [
|
|
181
|
+
...(diff.foreignKeysToAdd ?? []),
|
|
182
|
+
...(diff.foreignKeysToAlter ?? []).map((it) => it.to),
|
|
183
|
+
]));
|
|
169
184
|
return statements;
|
|
170
185
|
}
|
|
186
|
+
/** `ADD CONSTRAINT` for each of `foreignKeys`. */
|
|
187
|
+
addForeignKeyStatements(tableName, foreignKeys) {
|
|
188
|
+
return foreignKeys.map((foreignKey) => this.generateAddForeignKeySql(tableName, foreignKey));
|
|
189
|
+
}
|
|
190
|
+
/** `DROP CONSTRAINT` for each of `constraintNames`, the mirror of {@link addForeignKeyStatements}. */
|
|
191
|
+
dropForeignKeyStatements(tableName, constraintNames) {
|
|
192
|
+
return constraintNames.map((name) => this.generateDropForeignKeySql(tableName, name));
|
|
193
|
+
}
|
|
171
194
|
generateAlterTableDown(diff) {
|
|
172
195
|
const statements = [];
|
|
173
196
|
const tableName = this.escapeId(diff.tableName);
|
|
174
|
-
//
|
|
175
|
-
//
|
|
176
|
-
|
|
197
|
+
// Constraints first, mirroring the up direction: the up added them last, so the down drops them
|
|
198
|
+
// first, and a column it is about to drop is then free of anything naming it.
|
|
199
|
+
statements.push(...this.dropForeignKeyStatements(diff.tableName, [
|
|
200
|
+
...(diff.foreignKeysToAdd ?? []).map((it) => constraintNameOf(diff.tableName, it)),
|
|
201
|
+
...(diff.foreignKeysToAlter ?? []).map((it) => constraintNameOf(diff.tableName, it.to)),
|
|
202
|
+
]));
|
|
203
|
+
// The key next, for the same reason: a column the up added cannot be dropped below while the new
|
|
204
|
+
// key still names it. Restored under the name the database gave it, which is what the table had
|
|
205
|
+
// before, rather than a derived one that was never on it.
|
|
177
206
|
if (diff.primaryKey?.to.length) {
|
|
178
207
|
statements.push(this.generateDropPrimaryKeySql(diff.tableName, derivedPrimaryKeyName(diff.tableName, diff.primaryKey.to)));
|
|
179
208
|
}
|
|
@@ -200,8 +229,11 @@ export class SqlSchemaGenerator {
|
|
|
200
229
|
if (diff.primaryKey?.from.length) {
|
|
201
230
|
statements.push(this.generateAddPrimaryKeySql(diff.tableName, diff.primaryKey.from, diff.primaryKey.fromName));
|
|
202
231
|
}
|
|
203
|
-
|
|
204
|
-
|
|
232
|
+
// The constraint the up replaced, back under the name the database had for it. A foreign key the
|
|
233
|
+
// up *dropped* is not restored: only its name survived the diff, never what it pointed at.
|
|
234
|
+
statements.push(...this.addForeignKeyStatements(diff.tableName, (diff.foreignKeysToAlter ?? []).map((it) => it.from)));
|
|
235
|
+
if (diff.columnsToDrop?.length || diff.indexesToDrop?.length || diff.foreignKeysToDrop?.length) {
|
|
236
|
+
statements.push(`-- TODO: Manual reversal needed for dropped columns/indexes/foreign keys`);
|
|
205
237
|
}
|
|
206
238
|
return statements;
|
|
207
239
|
}
|
|
@@ -283,7 +315,7 @@ export class SqlSchemaGenerator {
|
|
|
283
315
|
const canonical = fieldOptionsToCanonical(field);
|
|
284
316
|
// Special case for serial primary keys
|
|
285
317
|
if (isAutoIncrement(field, field.isId === true)) {
|
|
286
|
-
return this.
|
|
318
|
+
return this.serialType(canonical);
|
|
287
319
|
}
|
|
288
320
|
return this.canonicalTypeToSql(canonical);
|
|
289
321
|
}
|
|
@@ -337,16 +369,16 @@ export class SqlSchemaGenerator {
|
|
|
337
369
|
* longer disagree about what has changed. Only two things are this side's own: the entity becomes a
|
|
338
370
|
* table node first, and types are compared as the *engine* would store them - see `normalizeType`.
|
|
339
371
|
*/
|
|
340
|
-
diffSchema(entity, currentTable) {
|
|
372
|
+
diffSchema(entity, currentTable, desiredAst) {
|
|
341
373
|
const meta = getMeta(entity);
|
|
342
374
|
const tableName = this.resolveTableName(meta);
|
|
343
375
|
const schema = this.resolveSchema(meta);
|
|
344
376
|
if (!currentTable) {
|
|
345
377
|
return { tableName, schema, type: 'create' };
|
|
346
378
|
}
|
|
347
|
-
// Keyed by the qualified name this generator resolves, which is the key the AST
|
|
348
|
-
//
|
|
349
|
-
const desired = buildEntityAST(this, [entity]).getTable(tableName);
|
|
379
|
+
// Keyed by the qualified name this generator resolves, which is the key the AST stores the table
|
|
380
|
+
// under.
|
|
381
|
+
const desired = (desiredAst ?? buildEntityAST(this, [entity], this.defaultForeignKeyAction)).getTable(tableName);
|
|
350
382
|
if (!desired) {
|
|
351
383
|
return undefined;
|
|
352
384
|
}
|
|
@@ -359,18 +391,43 @@ export class SqlSchemaGenerator {
|
|
|
359
391
|
const columnDiffs = tableDiff?.columnDiffs ?? [];
|
|
360
392
|
const columnsToAdd = columnDiffs.flatMap((it) => (it.type === 'add' ? [this.columnNodeToSchema(it.expected)] : []));
|
|
361
393
|
const columnsToDrop = columnDiffs.flatMap((it) => (it.type === 'drop' ? [it.column] : []));
|
|
394
|
+
// Without its values: an alter restates the whole column, and MySQL answers a restated `CHECK` by
|
|
395
|
+
// adding a *second* constraint rather than replacing the first, so the column would accumulate one
|
|
396
|
+
// per alter. An enum's values reach the database with the column and are never restated - which is
|
|
397
|
+
// also why changing them is a hand-written migration. See architecture/roadmap.md.
|
|
362
398
|
const columnsToAlter = columnDiffs.flatMap((it) => it.type === 'alter'
|
|
363
|
-
? [
|
|
399
|
+
? [
|
|
400
|
+
{
|
|
401
|
+
from: this.columnNodeToSchema(it.actual),
|
|
402
|
+
to: { ...this.columnNodeToSchema(it.expected), enum: undefined },
|
|
403
|
+
},
|
|
404
|
+
]
|
|
364
405
|
: []);
|
|
365
406
|
const primaryKey = tableDiff?.primaryKeyDiff && {
|
|
366
407
|
from: tableDiff.primaryKeyDiff.actual,
|
|
367
408
|
to: tableDiff.primaryKeyDiff.expected,
|
|
368
409
|
fromName: tableDiff.primaryKeyDiff.actualName,
|
|
369
410
|
};
|
|
411
|
+
// This table's own constraints, which is what `outgoingRelations` holds on both sides: the
|
|
412
|
+
// entity's as the AST derived them, the database's as the introspector read them back.
|
|
413
|
+
//
|
|
414
|
+
// None at all where the engine cannot alter one: SQLite resolves foreign keys lazily and keeps
|
|
415
|
+
// them inline at CREATE time, and its only way to change one afterwards is the twelve-step table
|
|
416
|
+
// rebuild, which a sync does not do. Reporting a difference nothing can apply would throw on
|
|
417
|
+
// every sync of an entity that has a relation. `drift:check` still names it.
|
|
418
|
+
const relationDiffs = this.features.foreignKeyAlter
|
|
419
|
+
? diffRelationshipNodes(desired.outgoingRelations, currentTable.outgoingRelations, this.diffOptions())
|
|
420
|
+
: [];
|
|
421
|
+
const foreignKeysToAdd = relationDiffs.flatMap((it) => (it.type === 'create' ? [foreignKeyOf(it.expected)] : []));
|
|
422
|
+
const foreignKeysToDrop = relationDiffs.flatMap((it) => (it.type === 'drop' ? [it.name] : []));
|
|
423
|
+
const foreignKeysToAlter = relationDiffs.flatMap((it) => it.type === 'alter' ? [{ from: foreignKeyOf(it.actual), to: foreignKeyOf(it.expected) }] : []);
|
|
370
424
|
if (!columnsToAdd.length &&
|
|
371
425
|
!columnsToAlter.length &&
|
|
372
426
|
!columnsToDrop.length &&
|
|
373
427
|
!indexesToAdd.length &&
|
|
428
|
+
!foreignKeysToAdd.length &&
|
|
429
|
+
!foreignKeysToDrop.length &&
|
|
430
|
+
!foreignKeysToAlter.length &&
|
|
374
431
|
!primaryKey) {
|
|
375
432
|
return undefined;
|
|
376
433
|
}
|
|
@@ -383,6 +440,9 @@ export class SqlSchemaGenerator {
|
|
|
383
440
|
columnsToAlter: columnsToAlter.length ? columnsToAlter : undefined,
|
|
384
441
|
columnsToDrop: columnsToDrop.length ? columnsToDrop : undefined,
|
|
385
442
|
indexesToAdd: indexesToAdd.length ? indexesToAdd : undefined,
|
|
443
|
+
foreignKeysToAdd: foreignKeysToAdd.length ? foreignKeysToAdd : undefined,
|
|
444
|
+
foreignKeysToDrop: foreignKeysToDrop.length ? foreignKeysToDrop : undefined,
|
|
445
|
+
foreignKeysToAlter: foreignKeysToAlter.length ? foreignKeysToAlter : undefined,
|
|
386
446
|
};
|
|
387
447
|
}
|
|
388
448
|
/**
|
|
@@ -413,13 +473,14 @@ export class SqlSchemaGenerator {
|
|
|
413
473
|
name: col.name,
|
|
414
474
|
// The same rule `generateColumnFromNode` renders by, so a column added to an existing table
|
|
415
475
|
// gets the type it would have had if the table were created from scratch.
|
|
416
|
-
type: col.isPrimaryKey && col.isAutoIncrement ? this.serialType : this.canonicalTypeToSql(col.type),
|
|
476
|
+
type: col.isPrimaryKey && col.isAutoIncrement ? this.serialType(col.type) : this.canonicalTypeToSql(col.type),
|
|
417
477
|
nullable: col.nullable,
|
|
418
478
|
defaultValue: col.defaultValue,
|
|
419
479
|
isPrimaryKey: col.isPrimaryKey,
|
|
420
480
|
isAutoIncrement: col.isAutoIncrement,
|
|
421
481
|
isUnique: col.isUnique,
|
|
422
482
|
comment: col.comment,
|
|
483
|
+
enum: col.enum,
|
|
423
484
|
};
|
|
424
485
|
}
|
|
425
486
|
/**
|
|
@@ -518,7 +579,7 @@ export class SqlSchemaGenerator {
|
|
|
518
579
|
generateColumnFromNode(col) {
|
|
519
580
|
return this.renderColumn({
|
|
520
581
|
...col,
|
|
521
|
-
type: col.isPrimaryKey && col.isAutoIncrement ? this.serialType : this.canonicalTypeToSql(col.type),
|
|
582
|
+
type: col.isPrimaryKey && col.isAutoIncrement ? this.serialType(col.type) : this.canonicalTypeToSql(col.type),
|
|
522
583
|
});
|
|
523
584
|
}
|
|
524
585
|
/**
|
|
@@ -555,15 +616,13 @@ export class SqlSchemaGenerator {
|
|
|
555
616
|
}
|
|
556
617
|
generateAddForeignKeySql(tableName, foreignKey) {
|
|
557
618
|
const fkCols = foreignKey.columns.map((c) => this.escapeId(c)).join(', ');
|
|
558
|
-
const refCols = foreignKey.
|
|
559
|
-
const constraintName = foreignKey
|
|
560
|
-
? this.escapeId(foreignKey.name)
|
|
561
|
-
: this.escapeId(derivedForeignKeyName(tableName, foreignKey.columns));
|
|
619
|
+
const refCols = foreignKey.references.columns.map((c) => this.escapeId(c)).join(', ');
|
|
620
|
+
const constraintName = this.escapeId(constraintNameOf(tableName, foreignKey));
|
|
562
621
|
if (!this.features.foreignKeyAlter) {
|
|
563
622
|
throw new TypeError(`Dialect ${this.dialect} does not support adding foreign keys to existing tables`);
|
|
564
623
|
}
|
|
565
624
|
return (`ALTER TABLE ${this.escapeId(tableName)} ADD CONSTRAINT ${constraintName} ` +
|
|
566
|
-
`FOREIGN KEY (${fkCols}) REFERENCES ${this.escapeId(foreignKey.
|
|
625
|
+
`FOREIGN KEY (${fkCols}) REFERENCES ${this.escapeId(foreignKey.references.table)} (${refCols}) ` +
|
|
567
626
|
`ON DELETE ${foreignKey.onDelete ?? this.defaultForeignKeyAction} ON UPDATE ${foreignKey.onUpdate ?? this.defaultForeignKeyAction};`);
|
|
568
627
|
}
|
|
569
628
|
generateDropForeignKeySql(tableName, constraintName) {
|
|
@@ -608,6 +667,31 @@ export class SqlSchemaGenerator {
|
|
|
608
667
|
'for it. Recreate the table in a written migration.');
|
|
609
668
|
}
|
|
610
669
|
}
|
|
670
|
+
/**
|
|
671
|
+
* What a constraint is called: its own name, or one derived from its columns where nothing named it.
|
|
672
|
+
* Shared by the add and the drop so a `DROP CONSTRAINT` names exactly what an `ADD CONSTRAINT` made.
|
|
673
|
+
*/
|
|
674
|
+
function constraintNameOf(tableName, foreignKey) {
|
|
675
|
+
return foreignKey.name ?? derivedForeignKeyName(tableName, foreignKey.columns);
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* A relationship node as the migration's own `ForeignKeySchema`. The node's `name` is kept rather
|
|
679
|
+
* than derived: on the database's side it is the only name a `DROP` can use, and on the entity's it
|
|
680
|
+
* is already the derived one. An unset action stays unset - the default belongs to the one place
|
|
681
|
+
* that spends it, `addForeignKeyStatements`.
|
|
682
|
+
*/
|
|
683
|
+
function foreignKeyOf(relation) {
|
|
684
|
+
return {
|
|
685
|
+
name: relation.name,
|
|
686
|
+
columns: relation.from.columns.map((column) => column.name),
|
|
687
|
+
references: {
|
|
688
|
+
table: qualifyName(relation.to.table.name, relation.to.table.schema),
|
|
689
|
+
columns: relation.to.columns.map((column) => column.name),
|
|
690
|
+
},
|
|
691
|
+
onDelete: relation.onDelete,
|
|
692
|
+
onUpdate: relation.onUpdate,
|
|
693
|
+
};
|
|
694
|
+
}
|
|
611
695
|
/**
|
|
612
696
|
* The entities as an AST, named the way `generator` names things.
|
|
613
697
|
*
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { getMeta, soleIdOf } from '../entity/metadata/definition.js';
|
|
9
9
|
import { isSoleIdField } from '../util/field.util.js';
|
|
10
|
+
import { isAutoIncrement } from '../util/field.util.js';
|
|
10
11
|
import { derivedForeignKeyName, derivedIndexName, qualifyName } from '../util/sql.util.js';
|
|
11
12
|
import { fieldOptionsToCanonical } from './canonicalType.js';
|
|
12
13
|
import { createTableNode, SchemaAST } from './schemaAST.js';
|
|
@@ -96,7 +97,7 @@ function addTableFromEntity(ctx, meta) {
|
|
|
96
97
|
nullable: isPrimaryKey ? false : (field.nullable ?? true),
|
|
97
98
|
defaultValue: field.defaultValue,
|
|
98
99
|
isPrimaryKey,
|
|
99
|
-
isAutoIncrement: field
|
|
100
|
+
isAutoIncrement: isAutoIncrement(field, isSoleKey),
|
|
100
101
|
isUnique: field.unique ?? false,
|
|
101
102
|
comment: field.comment,
|
|
102
103
|
enum: field.enum,
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { type IndexFacet } from './indexDifferences.js';
|
|
11
11
|
import type { SchemaAST } from './schemaAST.js';
|
|
12
12
|
import type { CanonicalType } from './types.js';
|
|
13
|
-
import type { SchemaDiffResult, TableDiff, TableNode } from './types.js';
|
|
13
|
+
import type { RelationshipDiff, RelationshipNode, SchemaDiffResult, TableDiff, TableNode } from './types.js';
|
|
14
14
|
/**
|
|
15
15
|
* Options for schema diffing.
|
|
16
16
|
*/
|
|
@@ -60,3 +60,13 @@ export declare function diffSchemas(source: SchemaAST, target: SchemaAST, option
|
|
|
60
60
|
* comparison serves both, so drift and migrations can no longer disagree about what has changed.
|
|
61
61
|
*/
|
|
62
62
|
export declare function diffTable(source: TableNode, target: TableNode, options?: DiffOptions): TableDiff | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* Compare two lists of relationships.
|
|
65
|
+
*
|
|
66
|
+
* Lists rather than whole schemas, because a migration diffs one table: its two sides are that
|
|
67
|
+
* table's `outgoingRelations`, where `diffSchemas` passes the schema's every relationship.
|
|
68
|
+
*
|
|
69
|
+
* Matched by columns, never by name - the engine named every constraint that already exists, so
|
|
70
|
+
* pairing on names would report every hand-named one as a drop and an add.
|
|
71
|
+
*/
|
|
72
|
+
export declare function diffRelationshipNodes(source: readonly RelationshipNode[], target: readonly RelationshipNode[], opts?: DiffOptions): RelationshipDiff[];
|
|
@@ -65,7 +65,9 @@ export function diffSchemas(source, target, options = {}) {
|
|
|
65
65
|
const indexDiffs = tablesToAlter.flatMap((tableDiff) => tableDiff.indexDiffs ?? []);
|
|
66
66
|
const primaryKeyDiffs = tablesToAlter.flatMap((tableDiff) => tableDiff.primaryKeyDiff ?? []);
|
|
67
67
|
// Relationships span tables, so they are compared over the whole schema rather than per table.
|
|
68
|
-
const relationshipDiffs = opts.compareRelationships
|
|
68
|
+
const relationshipDiffs = opts.compareRelationships
|
|
69
|
+
? diffRelationshipNodes(source.relationships, target.relationships, opts)
|
|
70
|
+
: [];
|
|
69
71
|
const hasDifferences = tablesToCreate.length > 0 ||
|
|
70
72
|
tablesToDrop.length > 0 ||
|
|
71
73
|
tablesToAlter.length > 0 ||
|
|
@@ -167,7 +169,7 @@ function diffColumn(tableName, source, target, opts) {
|
|
|
167
169
|
const differences = [];
|
|
168
170
|
// Two things a key column implies rather than states, and catalogues report inconsistently: its
|
|
169
171
|
// type, which is the dialect's serial spelling rather than one the entity chose and does not round
|
|
170
|
-
// trip (`BIGINT
|
|
172
|
+
// trip (`BIGINT AUTO_INCREMENT` reads back as `BIGINT(20)`), and its nullability,
|
|
171
173
|
// which is NOT NULL in every engine whatever is reported - SQLite's `PRAGMA table_info` says
|
|
172
174
|
// `notnull: 0` for the `INTEGER PRIMARY KEY` that is the table's own rowid. Comparing either asked
|
|
173
175
|
// to rewrite the column on every sync, and on SQLite, which cannot alter one at all, failed
|
|
@@ -179,6 +181,14 @@ function diffColumn(tableName, source, target, opts) {
|
|
|
179
181
|
if (typeChanged) {
|
|
180
182
|
differences.push(`type: ${formatType(source.type)} → ${formatType(target.type)}`);
|
|
181
183
|
}
|
|
184
|
+
// Signedness is the one thing compared on a generated key, because it is the one part of the serial
|
|
185
|
+
// spelling that does round trip - and a key left unsigned refuses every foreign key pointing at it,
|
|
186
|
+
// since the referencing column takes its type from the canonical one, which is signed. Without this
|
|
187
|
+
// a database created before the serial became signed could never gain a foreign key.
|
|
188
|
+
const signednessChanged = generatedType && !!source.type.unsigned !== !!target.type.unsigned;
|
|
189
|
+
if (signednessChanged) {
|
|
190
|
+
differences.push(`type: ${formatType(source.type)} → ${formatType(target.type)}`);
|
|
191
|
+
}
|
|
182
192
|
if (!impliedNotNull && source.nullable !== target.nullable) {
|
|
183
193
|
differences.push(`nullable: ${target.nullable} → ${source.nullable}`);
|
|
184
194
|
}
|
|
@@ -206,7 +216,7 @@ function diffColumn(tableName, source, target, opts) {
|
|
|
206
216
|
// Only the type this diff actually reports: a column altered for its default carries no data loss,
|
|
207
217
|
// and a generated key's type - never compared above - reads as unsigned against an entity that
|
|
208
218
|
// cannot say so.
|
|
209
|
-
isBreaking: typeChanged && isBreakingTypeChange(target.type, source.type),
|
|
219
|
+
isBreaking: (typeChanged || signednessChanged) && isBreakingTypeChange(target.type, source.type),
|
|
210
220
|
description: differences.join(', '),
|
|
211
221
|
};
|
|
212
222
|
}
|
|
@@ -228,11 +238,17 @@ function diffIndex(tableName, source, target, facets) {
|
|
|
228
238
|
};
|
|
229
239
|
}
|
|
230
240
|
/**
|
|
231
|
-
* Compare
|
|
241
|
+
* Compare two lists of relationships.
|
|
242
|
+
*
|
|
243
|
+
* Lists rather than whole schemas, because a migration diffs one table: its two sides are that
|
|
244
|
+
* table's `outgoingRelations`, where `diffSchemas` passes the schema's every relationship.
|
|
245
|
+
*
|
|
246
|
+
* Matched by columns, never by name - the engine named every constraint that already exists, so
|
|
247
|
+
* pairing on names would report every hand-named one as a drop and an add.
|
|
232
248
|
*/
|
|
233
|
-
function
|
|
234
|
-
const normalizeName = nameNormalizer(opts);
|
|
235
|
-
const { created, dropped, matched } = matchByKey(source
|
|
249
|
+
export function diffRelationshipNodes(source, target, opts = {}) {
|
|
250
|
+
const normalizeName = nameNormalizer({ ...DEFAULT_OPTIONS, ...opts });
|
|
251
|
+
const { created, dropped, matched } = matchByKey(source, target, (relation) => getRelationshipKey(relation, normalizeName));
|
|
236
252
|
return [
|
|
237
253
|
...created.map((relation) => ({
|
|
238
254
|
...relationEnds(relation),
|
package/dist/schema/types.d.ts
CHANGED
|
@@ -272,14 +272,29 @@ export interface IndexDiff {
|
|
|
272
272
|
/**
|
|
273
273
|
* Difference between two relationship definitions.
|
|
274
274
|
*/
|
|
275
|
-
|
|
275
|
+
interface RelationshipDiffBase {
|
|
276
276
|
readonly name: string;
|
|
277
277
|
readonly fromTable: string;
|
|
278
278
|
readonly toTable: string;
|
|
279
|
-
readonly type: 'create' | 'drop' | 'alter';
|
|
280
|
-
readonly expected?: RelationshipNode;
|
|
281
|
-
readonly actual?: RelationshipNode;
|
|
282
279
|
}
|
|
280
|
+
/**
|
|
281
|
+
* Difference between two relationships, shaped like {@link ColumnDiff} and for the same reason: which
|
|
282
|
+
* node is present follows from the kind of difference, so a reader never asserts its way past an
|
|
283
|
+
* `undefined` the kind had already ruled out.
|
|
284
|
+
*/
|
|
285
|
+
export type RelationshipDiff = RelationshipDiffBase & ({
|
|
286
|
+
readonly type: 'create';
|
|
287
|
+
readonly expected: RelationshipNode;
|
|
288
|
+
readonly actual?: undefined;
|
|
289
|
+
} | {
|
|
290
|
+
readonly type: 'drop';
|
|
291
|
+
readonly expected?: undefined;
|
|
292
|
+
readonly actual: RelationshipNode;
|
|
293
|
+
} | {
|
|
294
|
+
readonly type: 'alter';
|
|
295
|
+
readonly expected: RelationshipNode;
|
|
296
|
+
readonly actual: RelationshipNode;
|
|
297
|
+
});
|
|
283
298
|
/**
|
|
284
299
|
* Complete diff between two schemas.
|
|
285
300
|
*/
|
|
@@ -5,7 +5,7 @@ export declare class SqliteDialect extends AbstractSqlDialect {
|
|
|
5
5
|
protected readonly featureDefaults: DialectFeatures;
|
|
6
6
|
readonly dialectName = "sqlite";
|
|
7
7
|
readonly escapeIdChar = "`";
|
|
8
|
-
readonly
|
|
8
|
+
readonly autoIncrementSuffix = "PRIMARY KEY AUTOINCREMENT";
|
|
9
9
|
readonly serialDeclaresPrimaryKey = true;
|
|
10
10
|
readonly tableOptions = "";
|
|
11
11
|
readonly beginTransactionCommand = "BEGIN TRANSACTION";
|
|
@@ -22,7 +22,7 @@ export class SqliteDialect extends AbstractSqlDialect {
|
|
|
22
22
|
};
|
|
23
23
|
dialectName = 'sqlite';
|
|
24
24
|
escapeIdChar = '`';
|
|
25
|
-
|
|
25
|
+
autoIncrementSuffix = 'PRIMARY KEY AUTOINCREMENT';
|
|
26
26
|
// `AUTOINCREMENT` is only legal in that exact phrase, so the key cannot be lifted to table level.
|
|
27
27
|
serialDeclaresPrimaryKey = true;
|
|
28
28
|
tableOptions = '';
|
package/dist/type/entity.d.ts
CHANGED
|
@@ -232,7 +232,7 @@ export type RelationValue<E> = E[RelationKey<E>];
|
|
|
232
232
|
/**
|
|
233
233
|
* SQL numeric column types
|
|
234
234
|
*/
|
|
235
|
-
export type NumericColumnType = 'int' | 'integer' | 'tinyint' | 'smallint' | 'bigint' | 'float' | 'float4' | 'float8' | 'double' | 'double precision' | 'decimal' | 'numeric' | 'real'
|
|
235
|
+
export type NumericColumnType = 'int' | 'integer' | 'tinyint' | 'smallint' | 'bigint' | 'float' | 'float4' | 'float8' | 'double' | 'double precision' | 'decimal' | 'numeric' | 'real';
|
|
236
236
|
/**
|
|
237
237
|
* SQL string column types
|
|
238
238
|
*/
|
package/dist/type/migration.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { VectorCast } from '../dialect/vectorCast.js';
|
|
2
|
-
import type { FullColumnDefinition, TableDefinition
|
|
2
|
+
import type { FullColumnDefinition, TableDefinition } from '../migrate/builder/types.js';
|
|
3
3
|
import type { IndexFacet } from '../schema/indexDifferences.js';
|
|
4
4
|
import type { SchemaAST } from '../schema/schemaAST.js';
|
|
5
|
-
import type { ForeignKeyAction, IndexNode, IndexType, TableNode } from '../schema/types.js';
|
|
5
|
+
import type { EnumValues, ForeignKeyAction, IndexNode, IndexType, TableNode } from '../schema/types.js';
|
|
6
6
|
import type { EntityMeta, FieldOptions, IndexColumnSchema, LoggingOptions, SqlQuerier, Type, VectorIndexOptions } from './index.js';
|
|
7
7
|
/**
|
|
8
8
|
* Defines a migration using a simple object literal
|
|
@@ -116,6 +116,13 @@ export interface ColumnSchema {
|
|
|
116
116
|
readonly precision?: number;
|
|
117
117
|
readonly scale?: number;
|
|
118
118
|
readonly comment?: string;
|
|
119
|
+
/**
|
|
120
|
+
* The values the column accepts, rendered as an inline `CHECK`. Carried only so a column *added* to
|
|
121
|
+
* an existing table is constrained the way one created with its table is; an alter drops it, since
|
|
122
|
+
* MySQL adds a second check rather than replacing the first. Introspection never sets it: a database
|
|
123
|
+
* reports a check as a constraint, not as a property of the column.
|
|
124
|
+
*/
|
|
125
|
+
readonly enum?: EnumValues;
|
|
119
126
|
}
|
|
120
127
|
/**
|
|
121
128
|
* Represents a database table schema
|
|
@@ -161,15 +168,25 @@ export interface IndexSchema extends VectorIndexOptions {
|
|
|
161
168
|
readonly vectorType?: VectorCast;
|
|
162
169
|
}
|
|
163
170
|
/**
|
|
164
|
-
*
|
|
171
|
+
* A foreign key constraint, wherever one is described: read back by introspection, planned into a
|
|
172
|
+
* {@link SchemaDiff}, or declared through the migration builder. One shape for all three - they
|
|
173
|
+
* differed only in spelling, and the translation between them was pure overhead.
|
|
165
174
|
*/
|
|
166
175
|
export interface ForeignKeySchema {
|
|
167
|
-
|
|
176
|
+
/** Absent when nothing named it, which the generator fills in with `derivedForeignKeyName`. */
|
|
177
|
+
readonly name?: string;
|
|
168
178
|
readonly columns: string[];
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
179
|
+
/**
|
|
180
|
+
* The far end, as one thing. Same shape and same name as everywhere else a relationship's target is
|
|
181
|
+
* described - `@Field({ references })`, `addForeignKey`'s `target`, a `RelationshipNode`'s `to` -
|
|
182
|
+
* so nothing has to be destructured on the way between them.
|
|
183
|
+
*/
|
|
184
|
+
readonly references: {
|
|
185
|
+
readonly table: string;
|
|
186
|
+
readonly columns: string[];
|
|
187
|
+
};
|
|
188
|
+
readonly onDelete?: ForeignKeyAction;
|
|
189
|
+
readonly onUpdate?: ForeignKeyAction;
|
|
173
190
|
}
|
|
174
191
|
/**
|
|
175
192
|
* Represents a difference between current and desired schema
|
|
@@ -204,7 +221,17 @@ export interface SchemaDiff {
|
|
|
204
221
|
readonly indexesToAdd?: IndexSchema[];
|
|
205
222
|
readonly indexesToDrop?: string[];
|
|
206
223
|
readonly foreignKeysToAdd?: ForeignKeySchema[];
|
|
224
|
+
/** Dropped under the name the *database* reported, which is the only name a `DROP` can use. */
|
|
207
225
|
readonly foreignKeysToDrop?: string[];
|
|
226
|
+
/**
|
|
227
|
+
* A constraint whose referential actions changed. Its own field rather than a pair of entries in
|
|
228
|
+
* the two above, because no engine alters an action in place: it is a drop and an add that have to
|
|
229
|
+
* travel together, and safe mode has to hold back both or neither.
|
|
230
|
+
*/
|
|
231
|
+
readonly foreignKeysToAlter?: {
|
|
232
|
+
readonly from: ForeignKeySchema;
|
|
233
|
+
readonly to: ForeignKeySchema;
|
|
234
|
+
}[];
|
|
208
235
|
}
|
|
209
236
|
/**
|
|
210
237
|
* What every sync entry point takes: `safe` keeps it additive, `drop` lets it remove a column, and
|
|
@@ -278,8 +305,21 @@ export interface SchemaGenerator {
|
|
|
278
305
|
getSqlType(fieldOptions: FieldOptions): string;
|
|
279
306
|
/**
|
|
280
307
|
* Compare an entity with a database table node and return the differences.
|
|
308
|
+
*
|
|
309
|
+
* `desiredAst` is the entity side, from {@link buildAST}, and must span every entity a foreign key
|
|
310
|
+
* on this table points at: a relation whose target is absent resolves to nothing, so the constraint
|
|
311
|
+
* reads as missing from both sides, which is a match and no statement. Defaults to this entity
|
|
312
|
+
* alone, which is right only where it has no relations.
|
|
313
|
+
*/
|
|
314
|
+
diffSchema<E>(entity: Type<E>, currentTable: TableNode | undefined, desiredAst?: SchemaAST): SchemaDiff | undefined;
|
|
315
|
+
/**
|
|
316
|
+
* The entity side as an AST, to hand to every {@link diffSchema} of one run - building it per
|
|
317
|
+
* entity instead is quadratic in the number of entities.
|
|
318
|
+
*
|
|
319
|
+
* Optional because not every generator compares one: MongoDB has no foreign keys and diffs only
|
|
320
|
+
* indexes, so it neither implements this nor reads the argument.
|
|
281
321
|
*/
|
|
282
|
-
|
|
322
|
+
buildAST?(entities: readonly Type<unknown>[]): SchemaAST;
|
|
283
323
|
/**
|
|
284
324
|
* The table's key: {@link resolveTableAlias} behind {@link resolveSchema}, which is how a
|
|
285
325
|
* `SchemaAST` stores it and how a diff finds it again.
|
|
@@ -326,7 +366,7 @@ export interface SqlDdlGenerator extends SchemaGenerator {
|
|
|
326
366
|
/** Generate RENAME COLUMN statement */
|
|
327
367
|
generateRenameColumnSql(tableName: string, oldName: string, newName: string): string;
|
|
328
368
|
/** Generate ADD FOREIGN KEY statement */
|
|
329
|
-
generateAddForeignKeySql(tableName: string, foreignKey:
|
|
369
|
+
generateAddForeignKeySql(tableName: string, foreignKey: ForeignKeySchema): string;
|
|
330
370
|
/** Generate DROP FOREIGN KEY statement */
|
|
331
371
|
generateDropForeignKeySql(tableName: string, constraintName: string): string;
|
|
332
372
|
}
|
|
@@ -12,7 +12,7 @@ export type ColumnFamily = 'string' | 'numeric' | 'boolean' | 'date' | 'json' |
|
|
|
12
12
|
* restates the unions: the compile-time side of the same question reads them directly.
|
|
13
13
|
*/
|
|
14
14
|
export declare const COLUMN_TYPES_BY_FAMILY: {
|
|
15
|
-
readonly numeric: readonly ["int", "integer", "tinyint", "smallint", "bigint", "float", "float4", "float8", "double", "double precision", "decimal", "numeric", "real"
|
|
15
|
+
readonly numeric: readonly ["int", "integer", "tinyint", "smallint", "bigint", "float", "float4", "float8", "double", "double precision", "decimal", "numeric", "real"];
|
|
16
16
|
readonly string: readonly ["char", "varchar", "text", "uuid"];
|
|
17
17
|
readonly date: readonly ["date", "time", "datetime", "timestamp", "timestamptz"];
|
|
18
18
|
readonly json: readonly ["json", "jsonb"];
|
|
@@ -33,6 +33,11 @@ export declare function columnFamily(type: unknown): ColumnFamily | undefined;
|
|
|
33
33
|
*/
|
|
34
34
|
export declare function isSoleIdField<E>(meta: EntityMeta<E>, field: FieldOptions): boolean;
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
36
|
+
* Whether the database generates this column's value.
|
|
37
|
+
*
|
|
38
|
+
* The only answer: the schema AST asked it separately and disagreed on three counts - it ignored
|
|
39
|
+
* `onInsert`, so a key the application generates was still emitted `AUTO_INCREMENT`, and it ignored
|
|
40
|
+
* `columnType`, where this one used to let *any* declared width suppress the whole inference. A key
|
|
41
|
+
* that states its width is still a generated key; one that states how it is filled is not.
|
|
37
42
|
*/
|
|
38
43
|
export declare function isAutoIncrement(field: FieldOptions, isPrimaryKey: boolean): boolean;
|
package/dist/util/field.util.js
CHANGED
|
@@ -20,9 +20,6 @@ export const COLUMN_TYPES_BY_FAMILY = {
|
|
|
20
20
|
'decimal',
|
|
21
21
|
'numeric',
|
|
22
22
|
'real',
|
|
23
|
-
'serial',
|
|
24
|
-
'smallserial',
|
|
25
|
-
'bigserial',
|
|
26
23
|
],
|
|
27
24
|
string: ['char', 'varchar', 'text', 'uuid'],
|
|
28
25
|
date: ['date', 'time', 'datetime', 'timestamp', 'timestamptz'],
|
|
@@ -62,15 +59,15 @@ export function isSoleIdField(meta, field) {
|
|
|
62
59
|
return field.isId === true && meta.ids.length === 1;
|
|
63
60
|
}
|
|
64
61
|
/**
|
|
65
|
-
*
|
|
62
|
+
* Whether the database generates this column's value.
|
|
63
|
+
*
|
|
64
|
+
* The only answer: the schema AST asked it separately and disagreed on three counts - it ignored
|
|
65
|
+
* `onInsert`, so a key the application generates was still emitted `AUTO_INCREMENT`, and it ignored
|
|
66
|
+
* `columnType`, where this one used to let *any* declared width suppress the whole inference. A key
|
|
67
|
+
* that states its width is still a generated key; one that states how it is filled is not.
|
|
66
68
|
*/
|
|
67
69
|
export function isAutoIncrement(field, isPrimaryKey) {
|
|
68
|
-
if (field.autoIncrement
|
|
69
|
-
return
|
|
70
|
-
|
|
71
|
-
return true;
|
|
72
|
-
const colType = field.columnType?.toLowerCase();
|
|
73
|
-
if (colType === 'serial' || colType === 'smallserial' || colType === 'bigserial')
|
|
74
|
-
return true;
|
|
75
|
-
return isPrimaryKey && columnFamily(field.type) === 'numeric' && !field.onInsert && !field.columnType;
|
|
70
|
+
if (field.autoIncrement !== undefined)
|
|
71
|
+
return field.autoIncrement;
|
|
72
|
+
return isPrimaryKey && columnFamily(field.type) === 'numeric' && !field.onInsert;
|
|
76
73
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uql-orm",
|
|
3
3
|
"homepage": "https://uql-orm.dev",
|
|
4
|
-
"description": "JSON-native TypeScript ORM for
|
|
4
|
+
"description": "The JSON-native TypeScript ORM for Bun, Browsers, Edge, Deno, Node, Workers. Supports PostgreSQL, PGlite, MySQL, MariaDB, SQLite, CockroachDB, Turso, Neon, Cloudflare D1 and MongoDB. Queries are plain JSON, typed to the leaf.",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.45.1",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"engines": {
|
|
9
9
|
"node": ">=24"
|