@mikro-orm/knex 6.0.0-dev.21 → 6.0.0-dev.211
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/AbstractSqlConnection.d.ts +22 -4
- package/AbstractSqlConnection.js +48 -18
- package/AbstractSqlDriver.d.ts +33 -25
- package/AbstractSqlDriver.js +575 -268
- package/AbstractSqlPlatform.d.ts +5 -4
- package/AbstractSqlPlatform.js +34 -5
- package/MonkeyPatchable.d.ts +1 -0
- package/MonkeyPatchable.js +3 -0
- package/PivotCollectionPersister.d.ts +17 -0
- package/PivotCollectionPersister.js +128 -0
- package/README.md +64 -107
- package/SqlEntityManager.d.ts +2 -6
- package/SqlEntityManager.js +4 -8
- package/SqlEntityRepository.d.ts +7 -6
- package/SqlEntityRepository.js +11 -8
- package/index.mjs +24 -5
- package/package.json +8 -8
- package/query/ArrayCriteriaNode.d.ts +5 -4
- package/query/ArrayCriteriaNode.js +7 -2
- package/query/CriteriaNode.d.ts +12 -9
- package/query/CriteriaNode.js +27 -11
- package/query/CriteriaNodeFactory.d.ts +6 -6
- package/query/CriteriaNodeFactory.js +32 -15
- package/query/ObjectCriteriaNode.d.ts +5 -4
- package/query/ObjectCriteriaNode.js +64 -24
- package/query/QueryBuilder.d.ts +58 -26
- package/query/QueryBuilder.js +444 -148
- package/query/QueryBuilderHelper.d.ts +13 -18
- package/query/QueryBuilderHelper.js +194 -167
- package/query/ScalarCriteriaNode.d.ts +4 -3
- package/query/ScalarCriteriaNode.js +12 -8
- package/query/enums.d.ts +7 -0
- package/query/enums.js +10 -2
- package/schema/DatabaseSchema.d.ts +5 -2
- package/schema/DatabaseSchema.js +28 -10
- package/schema/DatabaseTable.d.ts +13 -5
- package/schema/DatabaseTable.js +385 -60
- package/schema/SchemaComparator.d.ts +5 -5
- package/schema/SchemaComparator.js +59 -35
- package/schema/SchemaHelper.d.ts +7 -5
- package/schema/SchemaHelper.js +25 -11
- package/schema/SqlSchemaGenerator.d.ts +3 -6
- package/schema/SqlSchemaGenerator.js +71 -36
- package/typings.d.ts +30 -11
|
@@ -7,6 +7,9 @@ const core_1 = require("@mikro-orm/core");
|
|
|
7
7
|
* Compares two Schemas and return an instance of SchemaDifference.
|
|
8
8
|
*/
|
|
9
9
|
class SchemaComparator {
|
|
10
|
+
platform;
|
|
11
|
+
helper;
|
|
12
|
+
logger;
|
|
10
13
|
constructor(platform) {
|
|
11
14
|
this.platform = platform;
|
|
12
15
|
this.helper = this.platform.getSchemaHelper();
|
|
@@ -19,7 +22,7 @@ class SchemaComparator {
|
|
|
19
22
|
* operations to change the schema stored in fromSchema to the schema that is
|
|
20
23
|
* stored in toSchema.
|
|
21
24
|
*/
|
|
22
|
-
compare(fromSchema, toSchema) {
|
|
25
|
+
compare(fromSchema, toSchema, inverseDiff) {
|
|
23
26
|
const diff = { newTables: {}, removedTables: {}, changedTables: {}, orphanedForeignKeys: [], newNamespaces: new Set(), removedNamespaces: new Set(), fromSchema };
|
|
24
27
|
const foreignKeysToTable = {};
|
|
25
28
|
for (const namespace of toSchema.getNamespaces()) {
|
|
@@ -40,7 +43,7 @@ class SchemaComparator {
|
|
|
40
43
|
diff.newTables[tableName] = toSchema.getTable(tableName);
|
|
41
44
|
}
|
|
42
45
|
else {
|
|
43
|
-
const tableDifferences = this.diffTable(fromSchema.getTable(tableName), toSchema.getTable(tableName));
|
|
46
|
+
const tableDifferences = this.diffTable(fromSchema.getTable(tableName), toSchema.getTable(tableName), inverseDiff?.changedTables[tableName]);
|
|
44
47
|
if (tableDifferences !== false) {
|
|
45
48
|
diff.changedTables[tableName] = tableDifferences;
|
|
46
49
|
}
|
|
@@ -88,7 +91,7 @@ class SchemaComparator {
|
|
|
88
91
|
* Returns the difference between the tables fromTable and toTable.
|
|
89
92
|
* If there are no differences this method returns the boolean false.
|
|
90
93
|
*/
|
|
91
|
-
diffTable(fromTable, toTable) {
|
|
94
|
+
diffTable(fromTable, toTable, inverseTableDiff) {
|
|
92
95
|
let changes = 0;
|
|
93
96
|
const tableDifferences = {
|
|
94
97
|
name: fromTable.getShortestName(),
|
|
@@ -139,6 +142,12 @@ class SchemaComparator {
|
|
|
139
142
|
if (changedProperties.size === 0) {
|
|
140
143
|
continue;
|
|
141
144
|
}
|
|
145
|
+
if (changedProperties.size === 1 && changedProperties.has('generated')) {
|
|
146
|
+
tableDifferences.addedColumns[column.name] = toTable.getColumn(column.name);
|
|
147
|
+
tableDifferences.removedColumns[column.name] = column;
|
|
148
|
+
changes++;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
142
151
|
tableDifferences.changedColumns[column.name] = {
|
|
143
152
|
oldColumnName: column.name,
|
|
144
153
|
fromColumn: column,
|
|
@@ -148,7 +157,7 @@ class SchemaComparator {
|
|
|
148
157
|
this.log(`column ${tableDifferences.name}.${column.name} changed`, { changedProperties });
|
|
149
158
|
changes++;
|
|
150
159
|
}
|
|
151
|
-
this.detectColumnRenamings(tableDifferences);
|
|
160
|
+
this.detectColumnRenamings(tableDifferences, inverseTableDiff);
|
|
152
161
|
const fromTableIndexes = fromTable.getIndexes();
|
|
153
162
|
const toTableIndexes = toTable.getIndexes();
|
|
154
163
|
// See if all the indexes in "from" table exist in "to" table
|
|
@@ -200,7 +209,7 @@ class SchemaComparator {
|
|
|
200
209
|
}
|
|
201
210
|
// See if index has changed in "to" table
|
|
202
211
|
const toTableCheck = toTable.getCheck(check.name);
|
|
203
|
-
if (!this.
|
|
212
|
+
if (!this.diffExpression(check.expression, toTableCheck.expression)) {
|
|
204
213
|
continue;
|
|
205
214
|
}
|
|
206
215
|
this.log(`check constraint ${check.name} changed in table ${tableDifferences.name}`, { fromTableCheck: check, toTableCheck });
|
|
@@ -240,7 +249,7 @@ class SchemaComparator {
|
|
|
240
249
|
* Try to find columns that only changed their name, rename operations maybe cheaper than add/drop
|
|
241
250
|
* however ambiguities between different possibilities should not lead to renaming at all.
|
|
242
251
|
*/
|
|
243
|
-
detectColumnRenamings(tableDifferences) {
|
|
252
|
+
detectColumnRenamings(tableDifferences, inverseTableDiff) {
|
|
244
253
|
const renameCandidates = {};
|
|
245
254
|
for (const addedColumn of Object.values(tableDifferences.addedColumns)) {
|
|
246
255
|
for (const removedColumn of Object.values(tableDifferences.removedColumns)) {
|
|
@@ -248,6 +257,10 @@ class SchemaComparator {
|
|
|
248
257
|
if (diff.size !== 0) {
|
|
249
258
|
continue;
|
|
250
259
|
}
|
|
260
|
+
const renamedColumn = inverseTableDiff?.renamedColumns[addedColumn.name];
|
|
261
|
+
if (renamedColumn && renamedColumn?.name !== removedColumn.name) {
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
251
264
|
renameCandidates[addedColumn.name] = renameCandidates[addedColumn.name] ?? [];
|
|
252
265
|
renameCandidates[addedColumn.name].push([removedColumn, addedColumn]);
|
|
253
266
|
}
|
|
@@ -324,12 +337,12 @@ class SchemaComparator {
|
|
|
324
337
|
* Returns the difference between the columns
|
|
325
338
|
* If there are differences this method returns field2, otherwise the boolean false.
|
|
326
339
|
*/
|
|
327
|
-
diffColumn(
|
|
340
|
+
diffColumn(fromColumn, toColumn, tableName) {
|
|
328
341
|
const changedProperties = new Set();
|
|
329
|
-
const
|
|
330
|
-
const
|
|
331
|
-
const
|
|
332
|
-
const
|
|
342
|
+
const fromProp = this.mapColumnToProperty({ ...fromColumn, autoincrement: false });
|
|
343
|
+
const toProp = this.mapColumnToProperty({ ...toColumn, autoincrement: false });
|
|
344
|
+
const fromColumnType = fromColumn.mappedType.getColumnType(fromProp, this.platform).toLowerCase();
|
|
345
|
+
const toColumnType = toColumn.mappedType.getColumnType(toProp, this.platform).toLowerCase();
|
|
333
346
|
const log = (msg, params) => {
|
|
334
347
|
if (tableName) {
|
|
335
348
|
const copy = core_1.Utils.copy(params);
|
|
@@ -337,40 +350,46 @@ class SchemaComparator {
|
|
|
337
350
|
this.log(msg, copy);
|
|
338
351
|
}
|
|
339
352
|
};
|
|
340
|
-
if (
|
|
341
|
-
!(
|
|
342
|
-
|
|
343
|
-
log(`'type' changed for column ${tableName}.${
|
|
353
|
+
if (fromColumnType !== toColumnType &&
|
|
354
|
+
!(fromColumn.ignoreSchemaChanges?.includes('type') ||
|
|
355
|
+
toColumn.ignoreSchemaChanges?.includes('type'))) {
|
|
356
|
+
log(`'type' changed for column ${tableName}.${fromColumn.name}`, { fromColumnType, toColumnType });
|
|
344
357
|
changedProperties.add('type');
|
|
345
358
|
}
|
|
346
|
-
if (
|
|
347
|
-
log(`'nullable' changed for column ${tableName}.${
|
|
359
|
+
if (fromColumn.nullable !== toColumn.nullable && !fromColumn.generated && !toColumn.generated) {
|
|
360
|
+
log(`'nullable' changed for column ${tableName}.${fromColumn.name}`, { fromColumn, toColumn });
|
|
348
361
|
changedProperties.add('nullable');
|
|
349
362
|
}
|
|
350
|
-
if (
|
|
351
|
-
log(`'
|
|
363
|
+
if (this.diffExpression(fromColumn.generated, toColumn.generated)) {
|
|
364
|
+
log(`'generated' changed for column ${tableName}.${fromColumn.name}`, { fromColumn, toColumn });
|
|
365
|
+
changedProperties.add('generated');
|
|
366
|
+
}
|
|
367
|
+
if (!!fromColumn.autoincrement !== !!toColumn.autoincrement) {
|
|
368
|
+
log(`'autoincrement' changed for column ${tableName}.${fromColumn.name}`, { fromColumn, toColumn });
|
|
352
369
|
changedProperties.add('autoincrement');
|
|
353
370
|
}
|
|
354
|
-
if (
|
|
355
|
-
log(`'unsigned' changed for column ${tableName}.${
|
|
371
|
+
if (fromColumn.unsigned !== toColumn.unsigned && this.platform.supportsUnsigned()) {
|
|
372
|
+
log(`'unsigned' changed for column ${tableName}.${fromColumn.name}`, { fromColumn, toColumn });
|
|
356
373
|
changedProperties.add('unsigned');
|
|
357
374
|
}
|
|
358
|
-
if (!this.hasSameDefaultValue(
|
|
359
|
-
log(`'default' changed for column ${tableName}.${
|
|
375
|
+
if (!this.hasSameDefaultValue(fromColumn, toColumn)) {
|
|
376
|
+
log(`'default' changed for column ${tableName}.${fromColumn.name}`, { fromColumn, toColumn });
|
|
360
377
|
changedProperties.add('default');
|
|
361
378
|
}
|
|
362
|
-
if (this.diffComment(
|
|
363
|
-
log(`'comment' changed for column ${tableName}.${
|
|
379
|
+
if (this.diffComment(fromColumn.comment, toColumn.comment)) {
|
|
380
|
+
log(`'comment' changed for column ${tableName}.${fromColumn.name}`, { fromColumn, toColumn });
|
|
364
381
|
changedProperties.add('comment');
|
|
365
382
|
}
|
|
366
|
-
if (
|
|
367
|
-
|
|
383
|
+
if (!(fromColumn.mappedType instanceof core_1.ArrayType) &&
|
|
384
|
+
!(toColumn.mappedType instanceof core_1.ArrayType) &&
|
|
385
|
+
this.diffEnumItems(fromColumn.enumItems, toColumn.enumItems)) {
|
|
386
|
+
log(`'enumItems' changed for column ${tableName}.${fromColumn.name}`, { fromColumn, toColumn });
|
|
368
387
|
changedProperties.add('enumItems');
|
|
369
388
|
}
|
|
370
|
-
if ((
|
|
371
|
-
!(
|
|
372
|
-
|
|
373
|
-
log(`'extra' changed for column ${tableName}.${
|
|
389
|
+
if ((fromColumn.extra || '').toLowerCase() !== (toColumn.extra || '').toLowerCase() &&
|
|
390
|
+
!(fromColumn.ignoreSchemaChanges?.includes('extra') ||
|
|
391
|
+
toColumn.ignoreSchemaChanges?.includes('extra'))) {
|
|
392
|
+
log(`'extra' changed for column ${tableName}.${fromColumn.name}`, { fromColumn, toColumn });
|
|
374
393
|
changedProperties.add('extra');
|
|
375
394
|
}
|
|
376
395
|
return changedProperties;
|
|
@@ -424,11 +443,11 @@ class SchemaComparator {
|
|
|
424
443
|
}
|
|
425
444
|
return index1.primary === index2.primary && index1.unique === index2.unique;
|
|
426
445
|
}
|
|
427
|
-
|
|
428
|
-
// check
|
|
446
|
+
diffExpression(expr1, expr2) {
|
|
447
|
+
// expressions like check constraints might be normalized by the driver,
|
|
429
448
|
// e.g. quotes might be added (https://github.com/mikro-orm/mikro-orm/issues/3827)
|
|
430
|
-
const simplify = (str) => str?.replace(/['"`()]
|
|
431
|
-
return simplify(
|
|
449
|
+
const simplify = (str) => str?.replace(/_\w+\\'(.*?)\\'/g, '$1').replace(/['"`()]|::\w+| +/g, '').toLowerCase();
|
|
450
|
+
return simplify(expr1) !== simplify(expr2);
|
|
432
451
|
}
|
|
433
452
|
hasSameDefaultValue(from, to) {
|
|
434
453
|
if (from.default == null || from.default.toString().toLowerCase() === 'null' || from.default.toString().startsWith('nextval(')) {
|
|
@@ -439,6 +458,11 @@ class SchemaComparator {
|
|
|
439
458
|
const defaultValueTo = !['0', 'false', 'f', 'n', 'no', 'off'].includes('' + to.default);
|
|
440
459
|
return defaultValueFrom === defaultValueTo;
|
|
441
460
|
}
|
|
461
|
+
if (to.mappedType instanceof core_1.JsonType) {
|
|
462
|
+
const defaultValueFrom = (0, core_1.parseJsonSafe)(from.default.replace(/^(_\w+\\)?'(.*?)\\?'$/, '$2'));
|
|
463
|
+
const defaultValueTo = (0, core_1.parseJsonSafe)(to.default?.replace(/^\(?'(.*?)'\)?$/, '$1'));
|
|
464
|
+
return core_1.Utils.equals(defaultValueFrom, defaultValueTo);
|
|
465
|
+
}
|
|
442
466
|
if (to.mappedType instanceof core_1.DateTimeType && from.default && to.default) {
|
|
443
467
|
// normalize now/current_timestamp defaults, also remove `()` from the end of default expression
|
|
444
468
|
const defaultValueFrom = from.default.toLowerCase().replace('current_timestamp', 'now').replace(/\(\)$/, '');
|
package/schema/SchemaHelper.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Knex } from 'knex';
|
|
2
|
-
import type
|
|
2
|
+
import { type Connection, type Dictionary } from '@mikro-orm/core';
|
|
3
3
|
import type { AbstractSqlConnection } from '../AbstractSqlConnection';
|
|
4
4
|
import type { AbstractSqlPlatform } from '../AbstractSqlPlatform';
|
|
5
5
|
import type { CheckDef, Column, IndexDef, Table, TableDifference } from '../typings';
|
|
@@ -18,10 +18,12 @@ export declare abstract class SchemaHelper {
|
|
|
18
18
|
getForeignKeys(connection: AbstractSqlConnection, tableName: string, schemaName?: string): Promise<Dictionary>;
|
|
19
19
|
protected getTableKey(t: Table): string;
|
|
20
20
|
getEnumDefinitions(connection: AbstractSqlConnection, checks: CheckDef[], tableName: string, schemaName?: string): Promise<Dictionary<string[]>>;
|
|
21
|
-
|
|
21
|
+
getDropNativeEnumSQL(name: string, schema?: string): string;
|
|
22
|
+
getAlterNativeEnumSQL(name: string, schema?: string, value?: string): string;
|
|
23
|
+
loadInformationSchema(schema: DatabaseSchema, connection: AbstractSqlConnection, tables: Table[], schemas?: string[]): Promise<void>;
|
|
22
24
|
getListTablesSQL(schemaName?: string): string;
|
|
23
25
|
getRenameColumnSQL(tableName: string, oldColumnName: string, to: Column, schemaName?: string): string;
|
|
24
|
-
getCreateIndexSQL(tableName: string, index: IndexDef): string;
|
|
26
|
+
getCreateIndexSQL(tableName: string, index: IndexDef, partialExpression?: boolean): string;
|
|
25
27
|
getDropIndexSQL(tableName: string, index: IndexDef): string;
|
|
26
28
|
getRenameIndexSQL(tableName: string, index: IndexDef, oldIndexName: string): string;
|
|
27
29
|
hasNonDefaultPrimaryKeyName(table: DatabaseTable): boolean;
|
|
@@ -29,8 +31,8 @@ export declare abstract class SchemaHelper {
|
|
|
29
31
|
configureColumn(column: Column, col: Knex.ColumnBuilder, knex: Knex, changedProperties?: Set<string>): Knex.ColumnBuilder;
|
|
30
32
|
configureColumnDefault(column: Column, col: Knex.ColumnBuilder, knex: Knex, changedProperties?: Set<string>): Knex.ColumnBuilder;
|
|
31
33
|
getPreAlterTable(tableDiff: TableDifference, safe: boolean): string;
|
|
32
|
-
getAlterColumnAutoincrement(tableName: string, column: Column): string;
|
|
33
|
-
getChangeColumnCommentSQL(tableName: string, to: Column): string;
|
|
34
|
+
getAlterColumnAutoincrement(tableName: string, column: Column, schemaName?: string): string;
|
|
35
|
+
getChangeColumnCommentSQL(tableName: string, to: Column, schemaName?: string): string;
|
|
34
36
|
getNamespaces(connection: AbstractSqlConnection): Promise<string[]>;
|
|
35
37
|
getColumns(connection: AbstractSqlConnection, tableName: string, schemaName?: string): Promise<Column[]>;
|
|
36
38
|
getIndexes(connection: AbstractSqlConnection, tableName: string, schemaName?: string): Promise<IndexDef[]>;
|
package/schema/SchemaHelper.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.SchemaHelper = void 0;
|
|
4
4
|
const core_1 = require("@mikro-orm/core");
|
|
5
5
|
class SchemaHelper {
|
|
6
|
+
platform;
|
|
6
7
|
constructor(platform) {
|
|
7
8
|
this.platform = platform;
|
|
8
9
|
}
|
|
@@ -46,7 +47,13 @@ class SchemaHelper {
|
|
|
46
47
|
async getEnumDefinitions(connection, checks, tableName, schemaName) {
|
|
47
48
|
return {};
|
|
48
49
|
}
|
|
49
|
-
|
|
50
|
+
getDropNativeEnumSQL(name, schema) {
|
|
51
|
+
throw new Error('Not supported by given driver');
|
|
52
|
+
}
|
|
53
|
+
getAlterNativeEnumSQL(name, schema, value) {
|
|
54
|
+
throw new Error('Not supported by given driver');
|
|
55
|
+
}
|
|
56
|
+
async loadInformationSchema(schema, connection, tables, schemas) {
|
|
50
57
|
for (const t of tables) {
|
|
51
58
|
const table = schema.addTable(t.table_name, t.schema_name);
|
|
52
59
|
table.comment = t.table_comment;
|
|
@@ -70,14 +77,17 @@ class SchemaHelper {
|
|
|
70
77
|
const tableReference = schemaReference + tableName;
|
|
71
78
|
return `alter table ${tableReference} rename column ${oldColumnName} to ${columnName}`;
|
|
72
79
|
}
|
|
73
|
-
getCreateIndexSQL(tableName, index) {
|
|
74
|
-
|
|
75
|
-
if (index.expression) {
|
|
80
|
+
getCreateIndexSQL(tableName, index, partialExpression = false) {
|
|
81
|
+
if (index.expression && !partialExpression) {
|
|
76
82
|
return index.expression;
|
|
77
83
|
}
|
|
78
84
|
tableName = this.platform.quoteIdentifier(tableName);
|
|
79
85
|
const keyName = this.platform.quoteIdentifier(index.keyName);
|
|
80
|
-
|
|
86
|
+
const sql = `create ${index.unique ? 'unique ' : ''}index ${keyName} on ${tableName} `;
|
|
87
|
+
if (index.expression && partialExpression) {
|
|
88
|
+
return `${sql}(${index.expression})`;
|
|
89
|
+
}
|
|
90
|
+
return `${sql}(${index.columnNames.map(c => this.platform.quoteIdentifier(c)).join(', ')})`;
|
|
81
91
|
}
|
|
82
92
|
getDropIndexSQL(tableName, index) {
|
|
83
93
|
return `drop index ${this.platform.quoteIdentifier(index.keyName)}`;
|
|
@@ -95,7 +105,7 @@ class SchemaHelper {
|
|
|
95
105
|
}
|
|
96
106
|
createTableColumn(table, column, fromTable, changedProperties) {
|
|
97
107
|
const compositePK = fromTable.getPrimaryKey()?.composite;
|
|
98
|
-
if (column.autoincrement && !compositePK && (!changedProperties || changedProperties.has('autoincrement') || changedProperties.has('type'))) {
|
|
108
|
+
if (column.autoincrement && !column.generated && !compositePK && (!changedProperties || changedProperties.has('autoincrement') || changedProperties.has('type'))) {
|
|
99
109
|
const primaryKey = !changedProperties && !this.hasNonDefaultPrimaryKeyName(fromTable);
|
|
100
110
|
if (column.mappedType instanceof core_1.BigIntType) {
|
|
101
111
|
return table.bigIncrements(column.name, { primaryKey });
|
|
@@ -105,12 +115,16 @@ class SchemaHelper {
|
|
|
105
115
|
if (column.mappedType instanceof core_1.EnumType && column.enumItems?.every(item => core_1.Utils.isString(item))) {
|
|
106
116
|
return table.enum(column.name, column.enumItems);
|
|
107
117
|
}
|
|
108
|
-
|
|
118
|
+
let columnType = column.type;
|
|
119
|
+
if (column.generated) {
|
|
120
|
+
columnType += ` generated always as ${column.generated}`;
|
|
121
|
+
}
|
|
122
|
+
return table.specificType(column.name, columnType);
|
|
109
123
|
}
|
|
110
124
|
configureColumn(column, col, knex, changedProperties) {
|
|
111
125
|
const guard = (key) => !changedProperties || changedProperties.has(key);
|
|
112
126
|
core_1.Utils.runIfNotEmpty(() => col.nullable(), column.nullable && guard('nullable'));
|
|
113
|
-
core_1.Utils.runIfNotEmpty(() => col.notNullable(), !column.nullable);
|
|
127
|
+
core_1.Utils.runIfNotEmpty(() => col.notNullable(), !column.nullable && !column.generated);
|
|
114
128
|
core_1.Utils.runIfNotEmpty(() => col.unsigned(), column.unsigned);
|
|
115
129
|
core_1.Utils.runIfNotEmpty(() => col.comment(column.comment), column.comment);
|
|
116
130
|
this.configureColumnDefault(column, col, knex, changedProperties);
|
|
@@ -122,17 +136,17 @@ class SchemaHelper {
|
|
|
122
136
|
core_1.Utils.runIfNotEmpty(() => col.defaultTo(column.default == null ? null : knex.raw(column.default)), guard('default'));
|
|
123
137
|
}
|
|
124
138
|
else {
|
|
125
|
-
core_1.Utils.runIfNotEmpty(() => col.defaultTo(
|
|
139
|
+
core_1.Utils.runIfNotEmpty(() => col.defaultTo(knex.raw(column.default)), column.default != null && column.default !== 'null');
|
|
126
140
|
}
|
|
127
141
|
return col;
|
|
128
142
|
}
|
|
129
143
|
getPreAlterTable(tableDiff, safe) {
|
|
130
144
|
return '';
|
|
131
145
|
}
|
|
132
|
-
getAlterColumnAutoincrement(tableName, column) {
|
|
146
|
+
getAlterColumnAutoincrement(tableName, column, schemaName) {
|
|
133
147
|
return '';
|
|
134
148
|
}
|
|
135
|
-
getChangeColumnCommentSQL(tableName, to) {
|
|
149
|
+
getChangeColumnCommentSQL(tableName, to, schemaName) {
|
|
136
150
|
return '';
|
|
137
151
|
}
|
|
138
152
|
async getNamespaces(connection) {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import { AbstractSchemaGenerator } from '@mikro-orm/core';
|
|
1
|
+
import { AbstractSchemaGenerator, type MikroORM, type ISchemaGenerator } from '@mikro-orm/core';
|
|
3
2
|
import type { SchemaDifference } from '../typings';
|
|
4
3
|
import { DatabaseSchema } from './DatabaseSchema';
|
|
5
4
|
import type { AbstractSqlDriver } from '../AbstractSqlDriver';
|
|
@@ -8,8 +7,6 @@ export declare class SqlSchemaGenerator extends AbstractSchemaGenerator<Abstract
|
|
|
8
7
|
private readonly options;
|
|
9
8
|
protected lastEnsuredDatabase?: string;
|
|
10
9
|
static register(orm: MikroORM): void;
|
|
11
|
-
/** @deprecated use `dropSchema` and `createSchema` commands respectively */
|
|
12
|
-
generate(): Promise<string>;
|
|
13
10
|
createSchema(options?: {
|
|
14
11
|
wrap?: boolean;
|
|
15
12
|
schema?: string;
|
|
@@ -82,10 +79,10 @@ export declare class SqlSchemaGenerator extends AbstractSchemaGenerator<Abstract
|
|
|
82
79
|
* creates new database and connects to it
|
|
83
80
|
*/
|
|
84
81
|
createDatabase(name: string): Promise<void>;
|
|
85
|
-
dropDatabase(name
|
|
82
|
+
dropDatabase(name?: string): Promise<void>;
|
|
86
83
|
execute(sql: string, options?: {
|
|
87
84
|
wrap?: boolean;
|
|
88
|
-
}): Promise<
|
|
85
|
+
}): Promise<void>;
|
|
89
86
|
private wrapSchema;
|
|
90
87
|
private createSchemaBuilder;
|
|
91
88
|
private createTable;
|
|
@@ -5,22 +5,12 @@ const core_1 = require("@mikro-orm/core");
|
|
|
5
5
|
const DatabaseSchema_1 = require("./DatabaseSchema");
|
|
6
6
|
const SchemaComparator_1 = require("./SchemaComparator");
|
|
7
7
|
class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
this.options = this.config.get('schemaGenerator');
|
|
12
|
-
}
|
|
8
|
+
helper = this.platform.getSchemaHelper();
|
|
9
|
+
options = this.config.get('schemaGenerator');
|
|
10
|
+
lastEnsuredDatabase;
|
|
13
11
|
static register(orm) {
|
|
14
12
|
orm.config.registerExtension('@mikro-orm/schema-generator', () => new SqlSchemaGenerator(orm.em));
|
|
15
13
|
}
|
|
16
|
-
/** @deprecated use `dropSchema` and `createSchema` commands respectively */
|
|
17
|
-
async generate() {
|
|
18
|
-
const [dropSchema, createSchema] = await Promise.all([
|
|
19
|
-
this.getDropSchemaSQL({ wrap: false }),
|
|
20
|
-
this.getCreateSchemaSQL({ wrap: false }),
|
|
21
|
-
]);
|
|
22
|
-
return this.wrapSchema(dropSchema + createSchema);
|
|
23
|
-
}
|
|
24
14
|
async createSchema(options) {
|
|
25
15
|
await this.ensureDatabase();
|
|
26
16
|
const sql = await this.getCreateSchemaSQL(options);
|
|
@@ -101,7 +91,8 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
|
|
|
101
91
|
await this.ensureDatabase();
|
|
102
92
|
const wrap = options.wrap ?? this.options.disableForeignKeys;
|
|
103
93
|
const metadata = this.getOrderedMetadata(options.schema).reverse();
|
|
104
|
-
const
|
|
94
|
+
const schemas = this.getTargetSchema(options.schema).getNamespaces();
|
|
95
|
+
const schema = await DatabaseSchema_1.DatabaseSchema.create(this.connection, this.platform, this.config, options.schema, schemas);
|
|
105
96
|
let ret = '';
|
|
106
97
|
// remove FKs explicitly if we can't use cascading statement and we don't disable FK checks (we need this for circular relations)
|
|
107
98
|
for (const meta of metadata) {
|
|
@@ -118,6 +109,12 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
|
|
|
118
109
|
for (const meta of metadata) {
|
|
119
110
|
ret += await this.dump(this.dropTable(meta.collection, this.getSchemaName(meta, options)), '\n');
|
|
120
111
|
}
|
|
112
|
+
if (this.platform.supportsNativeEnums()) {
|
|
113
|
+
for (const columnName of Object.keys(schema.getNativeEnums())) {
|
|
114
|
+
const sql = this.helper.getDropNativeEnumSQL(columnName, options.schema ?? this.config.get('schema'));
|
|
115
|
+
ret += await this.dump(this.knex.schema.raw(sql), '\n');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
121
118
|
if (options.dropMigrationsTable) {
|
|
122
119
|
ret += await this.dump(this.dropTable(this.config.get('migrations').tableName, this.config.get('schema')), '\n');
|
|
123
120
|
}
|
|
@@ -140,25 +137,29 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
|
|
|
140
137
|
return this.diffToSQL(diffUp, options);
|
|
141
138
|
}
|
|
142
139
|
async getUpdateSchemaMigrationSQL(options = {}) {
|
|
143
|
-
|
|
140
|
+
if (!options.fromSchema) {
|
|
141
|
+
await this.ensureDatabase();
|
|
142
|
+
}
|
|
144
143
|
const { fromSchema, toSchema } = await this.prepareSchemaForComparison(options);
|
|
145
144
|
const comparator = new SchemaComparator_1.SchemaComparator(this.platform);
|
|
146
145
|
const diffUp = comparator.compare(fromSchema, toSchema);
|
|
147
|
-
const diffDown = comparator.compare(toSchema, fromSchema);
|
|
146
|
+
const diffDown = comparator.compare(toSchema, fromSchema, diffUp);
|
|
148
147
|
return {
|
|
149
148
|
up: await this.diffToSQL(diffUp, options),
|
|
150
149
|
down: this.platform.supportsDownMigrations() ? await this.diffToSQL(diffDown, options) : '',
|
|
151
150
|
};
|
|
152
151
|
}
|
|
153
152
|
async prepareSchemaForComparison(options) {
|
|
154
|
-
options.wrap
|
|
155
|
-
options.safe
|
|
156
|
-
options.dropTables
|
|
153
|
+
options.wrap ??= this.options.disableForeignKeys;
|
|
154
|
+
options.safe ??= false;
|
|
155
|
+
options.dropTables ??= true;
|
|
157
156
|
const toSchema = this.getTargetSchema(options.schema);
|
|
158
|
-
const
|
|
157
|
+
const schemas = toSchema.getNamespaces();
|
|
158
|
+
const fromSchema = options.fromSchema ?? await DatabaseSchema_1.DatabaseSchema.create(this.connection, this.platform, this.config, options.schema, schemas);
|
|
159
159
|
const wildcardSchemaTables = Object.values(this.metadata.getAll()).filter(meta => meta.schema === '*').map(meta => meta.tableName);
|
|
160
160
|
fromSchema.prune(options.schema, wildcardSchemaTables);
|
|
161
161
|
toSchema.prune(options.schema, wildcardSchemaTables);
|
|
162
|
+
toSchema.setNativeEnums(fromSchema.getNativeEnums());
|
|
162
163
|
return { fromSchema, toSchema };
|
|
163
164
|
}
|
|
164
165
|
async diffToSQL(schemaDiff, options) {
|
|
@@ -214,7 +215,7 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
|
|
|
214
215
|
if (schema && schemaName === '*') {
|
|
215
216
|
return `${schema}.${referencedTableName.replace(/^\*\./, '')}`;
|
|
216
217
|
}
|
|
217
|
-
if (schemaName === this.platform.getDefaultSchemaName()) {
|
|
218
|
+
if (!schemaName || schemaName === this.platform.getDefaultSchemaName()) {
|
|
218
219
|
return tableName;
|
|
219
220
|
}
|
|
220
221
|
return `${schemaName}.${tableName}`;
|
|
@@ -262,6 +263,12 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
|
|
|
262
263
|
alterTable(diff, safe) {
|
|
263
264
|
const ret = [];
|
|
264
265
|
const [schemaName, tableName] = this.splitTableName(diff.name);
|
|
266
|
+
const changedNativeEnums = [];
|
|
267
|
+
for (const { column, changedProperties } of Object.values(diff.changedColumns)) {
|
|
268
|
+
if (column.nativeEnumName && changedProperties.has('enumItems') && column.nativeEnumName in diff.fromTable.nativeEnums) {
|
|
269
|
+
changedNativeEnums.push([column.nativeEnumName, column.enumItems, diff.fromTable.getColumn(column.name).enumItems]);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
265
272
|
ret.push(this.createSchemaBuilder(schemaName).alterTable(tableName, table => {
|
|
266
273
|
for (const index of Object.values(diff.removedIndexes)) {
|
|
267
274
|
this.dropIndex(table, index);
|
|
@@ -275,6 +282,14 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
|
|
|
275
282
|
for (const check of Object.values(diff.changedChecks)) {
|
|
276
283
|
this.dropCheck(table, check);
|
|
277
284
|
}
|
|
285
|
+
/* istanbul ignore else */
|
|
286
|
+
if (!safe) {
|
|
287
|
+
for (const column of Object.values(diff.removedColumns)) {
|
|
288
|
+
this.helper.pushTableQuery(table, `alter table ${this.platform.quoteIdentifier(tableName)} drop column ${this.platform.quoteIdentifier(column.name)}`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}));
|
|
292
|
+
ret.push(this.createSchemaBuilder(schemaName).alterTable(tableName, table => {
|
|
278
293
|
for (const column of Object.values(diff.addedColumns)) {
|
|
279
294
|
const col = this.helper.createTableColumn(table, column, diff.fromTable);
|
|
280
295
|
this.helper.configureColumn(column, col, this.knex);
|
|
@@ -288,27 +303,24 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
|
|
|
288
303
|
.onDelete(foreignKey.deleteRule);
|
|
289
304
|
}
|
|
290
305
|
}
|
|
291
|
-
/* istanbul ignore else */
|
|
292
|
-
if (!safe) {
|
|
293
|
-
for (const column of Object.values(diff.removedColumns)) {
|
|
294
|
-
table.dropColumn(column.name);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
306
|
for (const { column, changedProperties } of Object.values(diff.changedColumns)) {
|
|
298
307
|
if (changedProperties.size === 1 && changedProperties.has('comment')) {
|
|
299
308
|
continue;
|
|
300
309
|
}
|
|
310
|
+
if (changedProperties.size === 1 && changedProperties.has('enumItems') && column.nativeEnumName) {
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
301
313
|
const col = this.helper.createTableColumn(table, column, diff.fromTable, changedProperties).alter();
|
|
302
314
|
this.helper.configureColumn(column, col, this.knex, changedProperties);
|
|
303
315
|
}
|
|
304
316
|
for (const { column } of Object.values(diff.changedColumns).filter(diff => diff.changedProperties.has('autoincrement'))) {
|
|
305
|
-
this.helper.pushTableQuery(table, this.helper.getAlterColumnAutoincrement(tableName, column));
|
|
317
|
+
this.helper.pushTableQuery(table, this.helper.getAlterColumnAutoincrement(tableName, column, schemaName));
|
|
306
318
|
}
|
|
307
319
|
for (const { column, changedProperties } of Object.values(diff.changedColumns).filter(diff => diff.changedProperties.has('comment'))) {
|
|
308
320
|
if (['type', 'nullable', 'autoincrement', 'unsigned', 'default', 'enumItems'].some(t => changedProperties.has(t))) {
|
|
309
321
|
continue; // will be handled via knex
|
|
310
322
|
}
|
|
311
|
-
this.helper.pushTableQuery(table, this.helper.getChangeColumnCommentSQL(tableName, column));
|
|
323
|
+
this.helper.pushTableQuery(table, this.helper.getChangeColumnCommentSQL(tableName, column, schemaName));
|
|
312
324
|
}
|
|
313
325
|
for (const [oldColumnName, column] of Object.entries(diff.renamedColumns)) {
|
|
314
326
|
this.helper.pushTableQuery(table, this.helper.getRenameColumnSQL(tableName, oldColumnName, column, schemaName));
|
|
@@ -345,6 +357,13 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
|
|
|
345
357
|
table.comment(comment);
|
|
346
358
|
}
|
|
347
359
|
}));
|
|
360
|
+
if (this.platform.supportsNativeEnums()) {
|
|
361
|
+
changedNativeEnums.forEach(([enumName, itemsNew, itemsOld]) => {
|
|
362
|
+
// postgres allows only adding new items
|
|
363
|
+
const newItems = itemsNew.filter(val => !itemsOld.includes(val));
|
|
364
|
+
ret.push(...newItems.map(val => this.knex.schema.raw(this.helper.getAlterNativeEnumSQL(enumName, schemaName, val))));
|
|
365
|
+
});
|
|
366
|
+
}
|
|
348
367
|
return ret;
|
|
349
368
|
}
|
|
350
369
|
/**
|
|
@@ -356,24 +375,26 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
|
|
|
356
375
|
await this.driver.reconnect();
|
|
357
376
|
}
|
|
358
377
|
async dropDatabase(name) {
|
|
378
|
+
name ??= this.config.get('dbName');
|
|
359
379
|
this.config.set('dbName', this.helper.getManagementDbName());
|
|
360
380
|
await this.driver.reconnect();
|
|
361
381
|
await this.driver.execute(this.helper.getDropDatabaseSQL('' + this.knex.ref(name)));
|
|
362
382
|
}
|
|
363
383
|
async execute(sql, options = {}) {
|
|
364
|
-
options.wrap
|
|
384
|
+
options.wrap ??= false;
|
|
365
385
|
const lines = this.wrapSchema(sql, options).split('\n').filter(i => i.trim());
|
|
366
386
|
if (lines.length === 0) {
|
|
367
387
|
return;
|
|
368
388
|
}
|
|
369
389
|
if (this.platform.supportsMultipleStatements()) {
|
|
370
390
|
const query = lines.join('\n');
|
|
371
|
-
|
|
391
|
+
await this.driver.execute(query);
|
|
392
|
+
return;
|
|
372
393
|
}
|
|
373
394
|
await core_1.Utils.runSerial(lines, line => this.driver.execute(line));
|
|
374
395
|
}
|
|
375
|
-
wrapSchema(sql, options
|
|
376
|
-
options.wrap
|
|
396
|
+
wrapSchema(sql, options) {
|
|
397
|
+
options.wrap ??= this.options.disableForeignKeys;
|
|
377
398
|
if (!options.wrap || sql.trim() === '') {
|
|
378
399
|
return sql;
|
|
379
400
|
}
|
|
@@ -423,7 +444,14 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
|
|
|
423
444
|
table.primary(index.columnNames, keyName);
|
|
424
445
|
}
|
|
425
446
|
else if (index.unique) {
|
|
426
|
-
|
|
447
|
+
// JSON columns can have unique index but not unique constraint, and we need to distinguish those, so we can properly drop them
|
|
448
|
+
if (index.columnNames.some(column => column.includes('.'))) {
|
|
449
|
+
const columns = this.platform.getJsonIndexDefinition(index);
|
|
450
|
+
table.index(columns.map(column => this.knex.raw(`(${column})`)), index.keyName, { indexType: 'unique' });
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
table.unique(index.columnNames, { indexName: index.keyName });
|
|
454
|
+
}
|
|
427
455
|
}
|
|
428
456
|
else if (index.expression) {
|
|
429
457
|
this.helper.pushTableQuery(table, index.expression);
|
|
@@ -435,14 +463,21 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
|
|
|
435
463
|
}
|
|
436
464
|
}
|
|
437
465
|
else {
|
|
438
|
-
|
|
466
|
+
// JSON columns can have unique index but not unique constraint, and we need to distinguish those, so we can properly drop them
|
|
467
|
+
if (index.columnNames.some(column => column.includes('.'))) {
|
|
468
|
+
const columns = this.platform.getJsonIndexDefinition(index);
|
|
469
|
+
table.index(columns.map(column => this.knex.raw(`(${column})`)), index.keyName, index.type);
|
|
470
|
+
}
|
|
471
|
+
else {
|
|
472
|
+
table.index(index.columnNames, index.keyName, index.type);
|
|
473
|
+
}
|
|
439
474
|
}
|
|
440
475
|
}
|
|
441
476
|
dropIndex(table, index, oldIndexName = index.keyName) {
|
|
442
477
|
if (index.primary) {
|
|
443
478
|
table.dropPrimary(oldIndexName);
|
|
444
479
|
}
|
|
445
|
-
else if (index.unique) {
|
|
480
|
+
else if (index.unique && index.constraint) {
|
|
446
481
|
table.dropUnique(index.columnNames, oldIndexName);
|
|
447
482
|
}
|
|
448
483
|
else {
|