@mikro-orm/knex 6.0.0-dev.27 → 6.0.0-dev.270
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 +32 -24
- package/AbstractSqlDriver.js +578 -270
- package/AbstractSqlPlatform.d.ts +9 -4
- package/AbstractSqlPlatform.js +40 -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 +4 -8
- package/SqlEntityManager.js +8 -12
- package/SqlEntityRepository.d.ts +7 -6
- package/SqlEntityRepository.js +11 -8
- package/index.mjs +22 -3
- package/package.json +8 -8
- package/query/ArrayCriteriaNode.d.ts +5 -4
- package/query/ArrayCriteriaNode.js +7 -2
- package/query/CriteriaNode.d.ts +12 -10
- package/query/CriteriaNode.js +22 -9
- package/query/CriteriaNodeFactory.d.ts +6 -6
- package/query/CriteriaNodeFactory.js +32 -16
- package/query/ObjectCriteriaNode.d.ts +5 -4
- package/query/ObjectCriteriaNode.js +72 -28
- package/query/QueryBuilder.d.ts +58 -26
- package/query/QueryBuilder.js +438 -142
- package/query/QueryBuilderHelper.d.ts +13 -18
- package/query/QueryBuilderHelper.js +191 -164
- package/query/ScalarCriteriaNode.d.ts +4 -3
- package/query/ScalarCriteriaNode.js +14 -4
- package/query/enums.d.ts +7 -0
- package/query/enums.js +10 -2
- package/schema/DatabaseSchema.d.ts +5 -2
- package/schema/DatabaseSchema.js +26 -8
- package/schema/DatabaseTable.d.ts +12 -4
- package/schema/DatabaseTable.js +371 -46
- package/schema/SchemaComparator.d.ts +7 -5
- package/schema/SchemaComparator.js +68 -35
- package/schema/SchemaHelper.d.ts +7 -5
- package/schema/SchemaHelper.js +25 -11
- package/schema/SqlSchemaGenerator.d.ts +3 -4
- package/schema/SqlSchemaGenerator.js +71 -28
- 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,20 @@ 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);
|
|
451
|
+
}
|
|
452
|
+
parseJsonDefault(defaultValue) {
|
|
453
|
+
if (!defaultValue) {
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
const val = defaultValue
|
|
457
|
+
.replace(/^(_\w+\\)?'(.*?)\\?'$/, '$2')
|
|
458
|
+
.replace(/^\(?'(.*?)'\)?$/, '$1');
|
|
459
|
+
return (0, core_1.parseJsonSafe)(val);
|
|
432
460
|
}
|
|
433
461
|
hasSameDefaultValue(from, to) {
|
|
434
462
|
if (from.default == null || from.default.toString().toLowerCase() === 'null' || from.default.toString().startsWith('nextval(')) {
|
|
@@ -439,6 +467,11 @@ class SchemaComparator {
|
|
|
439
467
|
const defaultValueTo = !['0', 'false', 'f', 'n', 'no', 'off'].includes('' + to.default);
|
|
440
468
|
return defaultValueFrom === defaultValueTo;
|
|
441
469
|
}
|
|
470
|
+
if (to.mappedType instanceof core_1.JsonType) {
|
|
471
|
+
const defaultValueFrom = this.parseJsonDefault(from.default);
|
|
472
|
+
const defaultValueTo = this.parseJsonDefault(to.default);
|
|
473
|
+
return core_1.Utils.equals(defaultValueFrom, defaultValueTo);
|
|
474
|
+
}
|
|
442
475
|
if (to.mappedType instanceof core_1.DateTimeType && from.default && to.default) {
|
|
443
476
|
// normalize now/current_timestamp defaults, also remove `()` from the end of default expression
|
|
444
477
|
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';
|
|
@@ -80,10 +79,10 @@ export declare class SqlSchemaGenerator extends AbstractSchemaGenerator<Abstract
|
|
|
80
79
|
* creates new database and connects to it
|
|
81
80
|
*/
|
|
82
81
|
createDatabase(name: string): Promise<void>;
|
|
83
|
-
dropDatabase(name
|
|
82
|
+
dropDatabase(name?: string): Promise<void>;
|
|
84
83
|
execute(sql: string, options?: {
|
|
85
84
|
wrap?: boolean;
|
|
86
|
-
}): Promise<
|
|
85
|
+
}): Promise<void>;
|
|
87
86
|
private wrapSchema;
|
|
88
87
|
private createSchemaBuilder;
|
|
89
88
|
private createTable;
|
|
@@ -5,11 +5,9 @@ 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
|
}
|
|
@@ -93,7 +91,8 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
|
|
|
93
91
|
await this.ensureDatabase();
|
|
94
92
|
const wrap = options.wrap ?? this.options.disableForeignKeys;
|
|
95
93
|
const metadata = this.getOrderedMetadata(options.schema).reverse();
|
|
96
|
-
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);
|
|
97
96
|
let ret = '';
|
|
98
97
|
// remove FKs explicitly if we can't use cascading statement and we don't disable FK checks (we need this for circular relations)
|
|
99
98
|
for (const meta of metadata) {
|
|
@@ -110,6 +109,12 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
|
|
|
110
109
|
for (const meta of metadata) {
|
|
111
110
|
ret += await this.dump(this.dropTable(meta.collection, this.getSchemaName(meta, options)), '\n');
|
|
112
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
|
+
}
|
|
113
118
|
if (options.dropMigrationsTable) {
|
|
114
119
|
ret += await this.dump(this.dropTable(this.config.get('migrations').tableName, this.config.get('schema')), '\n');
|
|
115
120
|
}
|
|
@@ -132,25 +137,29 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
|
|
|
132
137
|
return this.diffToSQL(diffUp, options);
|
|
133
138
|
}
|
|
134
139
|
async getUpdateSchemaMigrationSQL(options = {}) {
|
|
135
|
-
|
|
140
|
+
if (!options.fromSchema) {
|
|
141
|
+
await this.ensureDatabase();
|
|
142
|
+
}
|
|
136
143
|
const { fromSchema, toSchema } = await this.prepareSchemaForComparison(options);
|
|
137
144
|
const comparator = new SchemaComparator_1.SchemaComparator(this.platform);
|
|
138
145
|
const diffUp = comparator.compare(fromSchema, toSchema);
|
|
139
|
-
const diffDown = comparator.compare(toSchema, fromSchema);
|
|
146
|
+
const diffDown = comparator.compare(toSchema, fromSchema, diffUp);
|
|
140
147
|
return {
|
|
141
148
|
up: await this.diffToSQL(diffUp, options),
|
|
142
149
|
down: this.platform.supportsDownMigrations() ? await this.diffToSQL(diffDown, options) : '',
|
|
143
150
|
};
|
|
144
151
|
}
|
|
145
152
|
async prepareSchemaForComparison(options) {
|
|
146
|
-
options.wrap
|
|
147
|
-
options.safe
|
|
148
|
-
options.dropTables
|
|
153
|
+
options.wrap ??= this.options.disableForeignKeys;
|
|
154
|
+
options.safe ??= false;
|
|
155
|
+
options.dropTables ??= true;
|
|
149
156
|
const toSchema = this.getTargetSchema(options.schema);
|
|
150
|
-
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);
|
|
151
159
|
const wildcardSchemaTables = Object.values(this.metadata.getAll()).filter(meta => meta.schema === '*').map(meta => meta.tableName);
|
|
152
160
|
fromSchema.prune(options.schema, wildcardSchemaTables);
|
|
153
161
|
toSchema.prune(options.schema, wildcardSchemaTables);
|
|
162
|
+
toSchema.setNativeEnums(fromSchema.getNativeEnums());
|
|
154
163
|
return { fromSchema, toSchema };
|
|
155
164
|
}
|
|
156
165
|
async diffToSQL(schemaDiff, options) {
|
|
@@ -206,7 +215,7 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
|
|
|
206
215
|
if (schema && schemaName === '*') {
|
|
207
216
|
return `${schema}.${referencedTableName.replace(/^\*\./, '')}`;
|
|
208
217
|
}
|
|
209
|
-
if (schemaName === this.platform.getDefaultSchemaName()) {
|
|
218
|
+
if (!schemaName || schemaName === this.platform.getDefaultSchemaName()) {
|
|
210
219
|
return tableName;
|
|
211
220
|
}
|
|
212
221
|
return `${schemaName}.${tableName}`;
|
|
@@ -254,6 +263,12 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
|
|
|
254
263
|
alterTable(diff, safe) {
|
|
255
264
|
const ret = [];
|
|
256
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
|
+
}
|
|
257
272
|
ret.push(this.createSchemaBuilder(schemaName).alterTable(tableName, table => {
|
|
258
273
|
for (const index of Object.values(diff.removedIndexes)) {
|
|
259
274
|
this.dropIndex(table, index);
|
|
@@ -267,6 +282,14 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
|
|
|
267
282
|
for (const check of Object.values(diff.changedChecks)) {
|
|
268
283
|
this.dropCheck(table, check);
|
|
269
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 => {
|
|
270
293
|
for (const column of Object.values(diff.addedColumns)) {
|
|
271
294
|
const col = this.helper.createTableColumn(table, column, diff.fromTable);
|
|
272
295
|
this.helper.configureColumn(column, col, this.knex);
|
|
@@ -280,27 +303,24 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
|
|
|
280
303
|
.onDelete(foreignKey.deleteRule);
|
|
281
304
|
}
|
|
282
305
|
}
|
|
283
|
-
/* istanbul ignore else */
|
|
284
|
-
if (!safe) {
|
|
285
|
-
for (const column of Object.values(diff.removedColumns)) {
|
|
286
|
-
table.dropColumn(column.name);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
306
|
for (const { column, changedProperties } of Object.values(diff.changedColumns)) {
|
|
290
307
|
if (changedProperties.size === 1 && changedProperties.has('comment')) {
|
|
291
308
|
continue;
|
|
292
309
|
}
|
|
310
|
+
if (changedProperties.size === 1 && changedProperties.has('enumItems') && column.nativeEnumName) {
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
293
313
|
const col = this.helper.createTableColumn(table, column, diff.fromTable, changedProperties).alter();
|
|
294
314
|
this.helper.configureColumn(column, col, this.knex, changedProperties);
|
|
295
315
|
}
|
|
296
316
|
for (const { column } of Object.values(diff.changedColumns).filter(diff => diff.changedProperties.has('autoincrement'))) {
|
|
297
|
-
this.helper.pushTableQuery(table, this.helper.getAlterColumnAutoincrement(tableName, column));
|
|
317
|
+
this.helper.pushTableQuery(table, this.helper.getAlterColumnAutoincrement(tableName, column, schemaName));
|
|
298
318
|
}
|
|
299
319
|
for (const { column, changedProperties } of Object.values(diff.changedColumns).filter(diff => diff.changedProperties.has('comment'))) {
|
|
300
320
|
if (['type', 'nullable', 'autoincrement', 'unsigned', 'default', 'enumItems'].some(t => changedProperties.has(t))) {
|
|
301
321
|
continue; // will be handled via knex
|
|
302
322
|
}
|
|
303
|
-
this.helper.pushTableQuery(table, this.helper.getChangeColumnCommentSQL(tableName, column));
|
|
323
|
+
this.helper.pushTableQuery(table, this.helper.getChangeColumnCommentSQL(tableName, column, schemaName));
|
|
304
324
|
}
|
|
305
325
|
for (const [oldColumnName, column] of Object.entries(diff.renamedColumns)) {
|
|
306
326
|
this.helper.pushTableQuery(table, this.helper.getRenameColumnSQL(tableName, oldColumnName, column, schemaName));
|
|
@@ -337,6 +357,13 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
|
|
|
337
357
|
table.comment(comment);
|
|
338
358
|
}
|
|
339
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
|
+
}
|
|
340
367
|
return ret;
|
|
341
368
|
}
|
|
342
369
|
/**
|
|
@@ -348,24 +375,26 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
|
|
|
348
375
|
await this.driver.reconnect();
|
|
349
376
|
}
|
|
350
377
|
async dropDatabase(name) {
|
|
378
|
+
name ??= this.config.get('dbName');
|
|
351
379
|
this.config.set('dbName', this.helper.getManagementDbName());
|
|
352
380
|
await this.driver.reconnect();
|
|
353
381
|
await this.driver.execute(this.helper.getDropDatabaseSQL('' + this.knex.ref(name)));
|
|
354
382
|
}
|
|
355
383
|
async execute(sql, options = {}) {
|
|
356
|
-
options.wrap
|
|
384
|
+
options.wrap ??= false;
|
|
357
385
|
const lines = this.wrapSchema(sql, options).split('\n').filter(i => i.trim());
|
|
358
386
|
if (lines.length === 0) {
|
|
359
387
|
return;
|
|
360
388
|
}
|
|
361
389
|
if (this.platform.supportsMultipleStatements()) {
|
|
362
390
|
const query = lines.join('\n');
|
|
363
|
-
|
|
391
|
+
await this.driver.execute(query);
|
|
392
|
+
return;
|
|
364
393
|
}
|
|
365
394
|
await core_1.Utils.runSerial(lines, line => this.driver.execute(line));
|
|
366
395
|
}
|
|
367
|
-
wrapSchema(sql, options
|
|
368
|
-
options.wrap
|
|
396
|
+
wrapSchema(sql, options) {
|
|
397
|
+
options.wrap ??= this.options.disableForeignKeys;
|
|
369
398
|
if (!options.wrap || sql.trim() === '') {
|
|
370
399
|
return sql;
|
|
371
400
|
}
|
|
@@ -415,7 +444,14 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
|
|
|
415
444
|
table.primary(index.columnNames, keyName);
|
|
416
445
|
}
|
|
417
446
|
else if (index.unique) {
|
|
418
|
-
|
|
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
|
+
}
|
|
419
455
|
}
|
|
420
456
|
else if (index.expression) {
|
|
421
457
|
this.helper.pushTableQuery(table, index.expression);
|
|
@@ -427,14 +463,21 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
|
|
|
427
463
|
}
|
|
428
464
|
}
|
|
429
465
|
else {
|
|
430
|
-
|
|
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
|
+
}
|
|
431
474
|
}
|
|
432
475
|
}
|
|
433
476
|
dropIndex(table, index, oldIndexName = index.keyName) {
|
|
434
477
|
if (index.primary) {
|
|
435
478
|
table.dropPrimary(oldIndexName);
|
|
436
479
|
}
|
|
437
|
-
else if (index.unique) {
|
|
480
|
+
else if (index.unique && index.constraint) {
|
|
438
481
|
table.dropUnique(index.columnNames, oldIndexName);
|
|
439
482
|
}
|
|
440
483
|
else {
|