@mikro-orm/sql 7.0.0-rc.2 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AbstractSqlConnection.d.ts +5 -4
- package/AbstractSqlConnection.js +20 -6
- package/AbstractSqlDriver.d.ts +19 -13
- package/AbstractSqlDriver.js +225 -47
- package/AbstractSqlPlatform.d.ts +35 -0
- package/AbstractSqlPlatform.js +51 -5
- package/PivotCollectionPersister.d.ts +2 -11
- package/PivotCollectionPersister.js +59 -59
- package/README.md +5 -4
- package/SqlEntityManager.d.ts +2 -2
- package/SqlEntityManager.js +5 -5
- package/dialects/index.d.ts +1 -0
- package/dialects/index.js +1 -0
- package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +2 -0
- package/dialects/mssql/MsSqlNativeQueryBuilder.js +8 -4
- package/dialects/mysql/BaseMySqlPlatform.d.ts +6 -0
- package/dialects/mysql/BaseMySqlPlatform.js +18 -2
- package/dialects/mysql/MySqlSchemaHelper.d.ts +1 -1
- package/dialects/mysql/MySqlSchemaHelper.js +25 -14
- package/dialects/oracledb/OracleDialect.d.ts +78 -0
- package/dialects/oracledb/OracleDialect.js +166 -0
- package/dialects/oracledb/OracleNativeQueryBuilder.d.ts +19 -0
- package/dialects/oracledb/OracleNativeQueryBuilder.js +249 -0
- package/dialects/oracledb/index.d.ts +2 -0
- package/dialects/oracledb/index.js +2 -0
- package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +6 -0
- package/dialects/postgresql/BasePostgreSqlPlatform.js +49 -37
- package/dialects/postgresql/PostgreSqlSchemaHelper.js +75 -59
- package/dialects/sqlite/BaseSqliteConnection.js +2 -2
- package/dialects/sqlite/NodeSqliteDialect.js +3 -1
- package/dialects/sqlite/SqlitePlatform.d.ts +1 -0
- package/dialects/sqlite/SqlitePlatform.js +7 -1
- package/dialects/sqlite/SqliteSchemaHelper.js +23 -17
- package/index.d.ts +1 -1
- package/index.js +0 -1
- package/package.json +30 -30
- package/plugin/index.d.ts +1 -14
- package/plugin/index.js +13 -13
- package/plugin/transformer.d.ts +6 -22
- package/plugin/transformer.js +91 -82
- package/query/ArrayCriteriaNode.d.ts +1 -1
- package/query/CriteriaNode.js +28 -10
- package/query/CriteriaNodeFactory.js +20 -4
- package/query/NativeQueryBuilder.d.ts +28 -3
- package/query/NativeQueryBuilder.js +65 -3
- package/query/ObjectCriteriaNode.js +75 -31
- package/query/QueryBuilder.d.ts +199 -100
- package/query/QueryBuilder.js +544 -358
- package/query/QueryBuilderHelper.d.ts +18 -14
- package/query/QueryBuilderHelper.js +364 -147
- package/query/ScalarCriteriaNode.js +17 -8
- package/query/enums.d.ts +2 -0
- package/query/enums.js +2 -0
- package/query/raw.js +1 -1
- package/schema/DatabaseSchema.d.ts +7 -5
- package/schema/DatabaseSchema.js +68 -45
- package/schema/DatabaseTable.d.ts +8 -6
- package/schema/DatabaseTable.js +191 -107
- package/schema/SchemaComparator.d.ts +1 -3
- package/schema/SchemaComparator.js +76 -50
- package/schema/SchemaHelper.d.ts +2 -13
- package/schema/SchemaHelper.js +30 -9
- package/schema/SqlSchemaGenerator.d.ts +4 -14
- package/schema/SqlSchemaGenerator.js +26 -12
- package/typings.d.ts +10 -5
- package/tsconfig.build.tsbuildinfo +0 -1
|
@@ -3,13 +3,13 @@ import { ArrayType, BooleanType, DateTimeType, inspect, JsonType, parseJsonSafe,
|
|
|
3
3
|
* Compares two Schemas and return an instance of SchemaDifference.
|
|
4
4
|
*/
|
|
5
5
|
export class SchemaComparator {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
#helper;
|
|
7
|
+
#logger;
|
|
8
|
+
#platform;
|
|
9
9
|
constructor(platform) {
|
|
10
|
-
this
|
|
11
|
-
this
|
|
12
|
-
this
|
|
10
|
+
this.#platform = platform;
|
|
11
|
+
this.#helper = this.#platform.getSchemaHelper();
|
|
12
|
+
this.#logger = this.#platform.getConfig().getLogger();
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
15
|
* Returns a SchemaDifference object containing the differences between the schemas fromSchema and toSchema.
|
|
@@ -35,13 +35,13 @@ export class SchemaComparator {
|
|
|
35
35
|
};
|
|
36
36
|
const foreignKeysToTable = {};
|
|
37
37
|
for (const namespace of toSchema.getNamespaces()) {
|
|
38
|
-
if (fromSchema.hasNamespace(namespace) || namespace === this
|
|
38
|
+
if (fromSchema.hasNamespace(namespace) || namespace === this.#platform.getDefaultSchemaName()) {
|
|
39
39
|
continue;
|
|
40
40
|
}
|
|
41
41
|
diff.newNamespaces.add(namespace);
|
|
42
42
|
}
|
|
43
43
|
for (const namespace of fromSchema.getNamespaces()) {
|
|
44
|
-
if (toSchema.hasNamespace(namespace) || namespace === this
|
|
44
|
+
if (toSchema.hasNamespace(namespace) || namespace === this.#platform.getDefaultSchemaName()) {
|
|
45
45
|
continue;
|
|
46
46
|
}
|
|
47
47
|
diff.removedNamespaces.add(namespace);
|
|
@@ -59,7 +59,9 @@ export class SchemaComparator {
|
|
|
59
59
|
if (toSchema.hasNativeEnum(key)) {
|
|
60
60
|
continue;
|
|
61
61
|
}
|
|
62
|
-
if (key.startsWith(`${fromSchema.name}.`) &&
|
|
62
|
+
if (key.startsWith(`${fromSchema.name}.`) &&
|
|
63
|
+
(fromSchema.name !== toSchema.name ||
|
|
64
|
+
toSchema.getNativeEnum(key.substring(fromSchema.name.length + 1))?.schema === '*')) {
|
|
63
65
|
continue;
|
|
64
66
|
}
|
|
65
67
|
diff.removedNativeEnums.push(nativeEnum);
|
|
@@ -164,7 +166,10 @@ export class SchemaComparator {
|
|
|
164
166
|
};
|
|
165
167
|
if (this.diffComment(fromTable.comment, toTable.comment)) {
|
|
166
168
|
tableDifferences.changedComment = toTable.comment;
|
|
167
|
-
this.log(`table comment changed for ${tableDifferences.name}`, {
|
|
169
|
+
this.log(`table comment changed for ${tableDifferences.name}`, {
|
|
170
|
+
fromTableComment: fromTable.comment,
|
|
171
|
+
toTableComment: toTable.comment,
|
|
172
|
+
});
|
|
168
173
|
changes++;
|
|
169
174
|
}
|
|
170
175
|
const fromTableColumns = fromTable.getColumns();
|
|
@@ -222,7 +227,7 @@ export class SchemaComparator {
|
|
|
222
227
|
// See if there are any removed indexes in "to" table
|
|
223
228
|
for (const index of fromTableIndexes) {
|
|
224
229
|
// See if index is removed in "to" table.
|
|
225
|
-
if ((index.primary && !toTable.hasPrimaryKey()) || !index.primary && !toTable.hasIndex(index.keyName)) {
|
|
230
|
+
if ((index.primary && !toTable.hasPrimaryKey()) || (!index.primary && !toTable.hasIndex(index.keyName))) {
|
|
226
231
|
tableDifferences.removedIndexes[index.keyName] = index;
|
|
227
232
|
this.log(`index ${index.keyName} removed from table ${tableDifferences.name}`);
|
|
228
233
|
changes++;
|
|
@@ -234,7 +239,10 @@ export class SchemaComparator {
|
|
|
234
239
|
continue;
|
|
235
240
|
}
|
|
236
241
|
tableDifferences.changedIndexes[index.keyName] = toTableIndex;
|
|
237
|
-
this.log(`index ${index.keyName} changed in table ${tableDifferences.name}`, {
|
|
242
|
+
this.log(`index ${index.keyName} changed in table ${tableDifferences.name}`, {
|
|
243
|
+
fromTableIndex: index,
|
|
244
|
+
toTableIndex,
|
|
245
|
+
});
|
|
238
246
|
changes++;
|
|
239
247
|
}
|
|
240
248
|
this.detectIndexRenamings(tableDifferences);
|
|
@@ -264,10 +272,15 @@ export class SchemaComparator {
|
|
|
264
272
|
if (!this.diffExpression(check.expression, toTableCheck.expression)) {
|
|
265
273
|
continue;
|
|
266
274
|
}
|
|
267
|
-
if (fromColumn?.enumItems &&
|
|
275
|
+
if (fromColumn?.enumItems &&
|
|
276
|
+
toColumn?.enumItems &&
|
|
277
|
+
!this.diffEnumItems(fromColumn.enumItems, toColumn.enumItems)) {
|
|
268
278
|
continue;
|
|
269
279
|
}
|
|
270
|
-
this.log(`check constraint ${check.name} changed in table ${tableDifferences.name}`, {
|
|
280
|
+
this.log(`check constraint ${check.name} changed in table ${tableDifferences.name}`, {
|
|
281
|
+
fromTableCheck: check,
|
|
282
|
+
toTableCheck,
|
|
283
|
+
});
|
|
271
284
|
tableDifferences.changedChecks[check.name] = toTableCheck;
|
|
272
285
|
changes++;
|
|
273
286
|
}
|
|
@@ -280,7 +293,10 @@ export class SchemaComparator {
|
|
|
280
293
|
delete toForeignKeys[toConstraint.constraintName];
|
|
281
294
|
}
|
|
282
295
|
else if (fromConstraint.constraintName.toLowerCase() === toConstraint.constraintName.toLowerCase()) {
|
|
283
|
-
this.log(`FK constraint ${fromConstraint.constraintName} changed in table ${tableDifferences.name}`, {
|
|
296
|
+
this.log(`FK constraint ${fromConstraint.constraintName} changed in table ${tableDifferences.name}`, {
|
|
297
|
+
fromConstraint,
|
|
298
|
+
toConstraint,
|
|
299
|
+
});
|
|
284
300
|
tableDifferences.changedForeignKeys[toConstraint.constraintName] = toConstraint;
|
|
285
301
|
changes++;
|
|
286
302
|
delete fromForeignKeys[fromConstraint.constraintName];
|
|
@@ -295,7 +311,9 @@ export class SchemaComparator {
|
|
|
295
311
|
}
|
|
296
312
|
for (const toConstraint of Object.values(toForeignKeys)) {
|
|
297
313
|
tableDifferences.addedForeignKeys[toConstraint.constraintName] = toConstraint;
|
|
298
|
-
this.log(`FK constraint ${toConstraint.constraintName} added to table ${tableDifferences.name}`, {
|
|
314
|
+
this.log(`FK constraint ${toConstraint.constraintName} added to table ${tableDifferences.name}`, {
|
|
315
|
+
constraint: toConstraint,
|
|
316
|
+
});
|
|
299
317
|
changes++;
|
|
300
318
|
}
|
|
301
319
|
return changes ? tableDifferences : false;
|
|
@@ -341,7 +359,10 @@ export class SchemaComparator {
|
|
|
341
359
|
tableDifferences.renamedColumns[removedColumnName] = addedColumn;
|
|
342
360
|
delete tableDifferences.addedColumns[addedColumnName];
|
|
343
361
|
delete tableDifferences.removedColumns[removedColumnName];
|
|
344
|
-
this.log(`renamed column detected in table ${tableDifferences.name}`, {
|
|
362
|
+
this.log(`renamed column detected in table ${tableDifferences.name}`, {
|
|
363
|
+
old: removedColumnName,
|
|
364
|
+
new: addedColumnName,
|
|
365
|
+
});
|
|
345
366
|
}
|
|
346
367
|
}
|
|
347
368
|
/**
|
|
@@ -375,7 +396,10 @@ export class SchemaComparator {
|
|
|
375
396
|
tableDifferences.renamedIndexes[removedIndexName] = addedIndex;
|
|
376
397
|
delete tableDifferences.addedIndexes[addedIndexName];
|
|
377
398
|
delete tableDifferences.removedIndexes[removedIndexName];
|
|
378
|
-
this.log(`renamed index detected in table ${tableDifferences.name}`, {
|
|
399
|
+
this.log(`renamed index detected in table ${tableDifferences.name}`, {
|
|
400
|
+
old: removedIndexName,
|
|
401
|
+
new: addedIndexName,
|
|
402
|
+
});
|
|
379
403
|
}
|
|
380
404
|
}
|
|
381
405
|
diffForeignKey(key1, key2, tableDifferences) {
|
|
@@ -394,7 +418,7 @@ export class SchemaComparator {
|
|
|
394
418
|
if (key1.deferMode !== key2.deferMode) {
|
|
395
419
|
return true;
|
|
396
420
|
}
|
|
397
|
-
if (key1.localTableName === key1.referencedTableName && !this
|
|
421
|
+
if (key1.localTableName === key1.referencedTableName && !this.#platform.supportsMultipleCascadePaths()) {
|
|
398
422
|
return false;
|
|
399
423
|
}
|
|
400
424
|
if (key1.columnNames.some(col => tableDifferences.changedColumns[col]?.changedProperties.has('type'))) {
|
|
@@ -402,13 +426,12 @@ export class SchemaComparator {
|
|
|
402
426
|
}
|
|
403
427
|
const defaultRule = ['restrict', 'no action'];
|
|
404
428
|
const rule = (key, method) => {
|
|
405
|
-
return (key[method] ?? defaultRule[0])
|
|
406
|
-
.toLowerCase()
|
|
407
|
-
.replace(defaultRule[1], defaultRule[0])
|
|
408
|
-
.replace(/"/g, '');
|
|
429
|
+
return (key[method] ?? defaultRule[0]).toLowerCase().replace(defaultRule[1], defaultRule[0]).replace(/"/g, '');
|
|
409
430
|
};
|
|
410
431
|
const compare = (method) => rule(key1, method) === rule(key2, method);
|
|
411
|
-
|
|
432
|
+
// Skip updateRule comparison for platforms that don't support ON UPDATE (e.g., Oracle)
|
|
433
|
+
const updateRuleDiffers = this.#platform.supportsOnUpdate() && !compare('updateRule');
|
|
434
|
+
return updateRuleDiffers || !compare('deleteRule');
|
|
412
435
|
}
|
|
413
436
|
/**
|
|
414
437
|
* Returns the difference between the columns
|
|
@@ -417,9 +440,10 @@ export class SchemaComparator {
|
|
|
417
440
|
const changedProperties = new Set();
|
|
418
441
|
const fromProp = this.mapColumnToProperty({ ...fromColumn, autoincrement: false });
|
|
419
442
|
const toProp = this.mapColumnToProperty({ ...toColumn, autoincrement: false });
|
|
420
|
-
const fromColumnType = this
|
|
421
|
-
const fromNativeEnum = fromTable.nativeEnums[fromColumnType] ??
|
|
422
|
-
|
|
443
|
+
const fromColumnType = this.#platform.normalizeColumnType(fromColumn.mappedType.getColumnType(fromProp, this.#platform).toLowerCase(), fromProp);
|
|
444
|
+
const fromNativeEnum = fromTable.nativeEnums[fromColumnType] ??
|
|
445
|
+
Object.values(fromTable.nativeEnums).find(e => e.name === fromColumnType && e.schema !== '*');
|
|
446
|
+
let toColumnType = this.#platform.normalizeColumnType(toColumn.mappedType.getColumnType(toProp, this.#platform).toLowerCase(), toProp);
|
|
423
447
|
const log = (msg, params) => {
|
|
424
448
|
if (logging) {
|
|
425
449
|
const copy = Utils.copy(params);
|
|
@@ -430,8 +454,11 @@ export class SchemaComparator {
|
|
|
430
454
|
if (fromColumnType !== toColumnType &&
|
|
431
455
|
(!fromNativeEnum || `${fromNativeEnum.schema}.${fromNativeEnum.name}` !== toColumnType) &&
|
|
432
456
|
!(fromColumn.ignoreSchemaChanges?.includes('type') || toColumn.ignoreSchemaChanges?.includes('type')) &&
|
|
433
|
-
!fromColumn.generated &&
|
|
434
|
-
|
|
457
|
+
!fromColumn.generated &&
|
|
458
|
+
!toColumn.generated) {
|
|
459
|
+
if (!toColumnType.includes('.') &&
|
|
460
|
+
fromTable.schema &&
|
|
461
|
+
fromTable.schema !== this.#platform.getDefaultSchemaName()) {
|
|
435
462
|
toColumnType = `${fromTable.schema}.${toColumnType}`;
|
|
436
463
|
}
|
|
437
464
|
if (fromColumnType !== toColumnType) {
|
|
@@ -439,7 +466,7 @@ export class SchemaComparator {
|
|
|
439
466
|
changedProperties.add('type');
|
|
440
467
|
}
|
|
441
468
|
}
|
|
442
|
-
if (fromColumn.nullable !== toColumn.nullable && !fromColumn.generated && !toColumn.generated) {
|
|
469
|
+
if (!!fromColumn.nullable !== !!toColumn.nullable && !fromColumn.generated && !toColumn.generated) {
|
|
443
470
|
log(`'nullable' changed for column ${fromTable.name}.${fromColumn.name}`, { fromColumn, toColumn });
|
|
444
471
|
changedProperties.add('nullable');
|
|
445
472
|
}
|
|
@@ -451,12 +478,12 @@ export class SchemaComparator {
|
|
|
451
478
|
log(`'autoincrement' changed for column ${fromTable.name}.${fromColumn.name}`, { fromColumn, toColumn });
|
|
452
479
|
changedProperties.add('autoincrement');
|
|
453
480
|
}
|
|
454
|
-
if (fromColumn.unsigned !== toColumn.unsigned && this
|
|
481
|
+
if (!!fromColumn.unsigned !== !!toColumn.unsigned && this.#platform.supportsUnsigned()) {
|
|
455
482
|
log(`'unsigned' changed for column ${fromTable.name}.${fromColumn.name}`, { fromColumn, toColumn });
|
|
456
483
|
changedProperties.add('unsigned');
|
|
457
484
|
}
|
|
458
|
-
if (!(fromColumn.ignoreSchemaChanges?.includes('default') ||
|
|
459
|
-
|
|
485
|
+
if (!(fromColumn.ignoreSchemaChanges?.includes('default') || toColumn.ignoreSchemaChanges?.includes('default')) &&
|
|
486
|
+
!this.hasSameDefaultValue(fromColumn, toColumn)) {
|
|
460
487
|
log(`'default' changed for column ${fromTable.name}.${fromColumn.name}`, { fromColumn, toColumn });
|
|
461
488
|
changedProperties.add('default');
|
|
462
489
|
}
|
|
@@ -471,8 +498,7 @@ export class SchemaComparator {
|
|
|
471
498
|
changedProperties.add('enumItems');
|
|
472
499
|
}
|
|
473
500
|
if ((fromColumn.extra || '').toLowerCase() !== (toColumn.extra || '').toLowerCase() &&
|
|
474
|
-
!(fromColumn.ignoreSchemaChanges?.includes('extra') ||
|
|
475
|
-
toColumn.ignoreSchemaChanges?.includes('extra'))) {
|
|
501
|
+
!(fromColumn.ignoreSchemaChanges?.includes('extra') || toColumn.ignoreSchemaChanges?.includes('extra'))) {
|
|
476
502
|
log(`'extra' changed for column ${fromTable.name}.${fromColumn.name}`, { fromColumn, toColumn });
|
|
477
503
|
changedProperties.add('extra');
|
|
478
504
|
}
|
|
@@ -549,7 +575,7 @@ export class SchemaComparator {
|
|
|
549
575
|
// index that has no constraints.
|
|
550
576
|
return true;
|
|
551
577
|
}
|
|
552
|
-
if (this
|
|
578
|
+
if (this.#platform.supportsDeferredUniqueConstraints() && index1.deferMode !== index2.deferMode) {
|
|
553
579
|
return false;
|
|
554
580
|
}
|
|
555
581
|
return index1.primary === index2.primary && index1.unique === index2.unique;
|
|
@@ -579,7 +605,7 @@ export class SchemaComparator {
|
|
|
579
605
|
if (sort1 !== sort2) {
|
|
580
606
|
return false;
|
|
581
607
|
}
|
|
582
|
-
const defaultNulls = (s) => s === 'DESC' ? 'FIRST' : 'LAST';
|
|
608
|
+
const defaultNulls = (s) => (s === 'DESC' ? 'FIRST' : 'LAST');
|
|
583
609
|
const nulls1 = c1.nulls?.toUpperCase() ?? defaultNulls(sort1);
|
|
584
610
|
const nulls2 = c2.nulls?.toUpperCase() ?? defaultNulls(sort2);
|
|
585
611
|
if (nulls1 !== nulls2) {
|
|
@@ -610,9 +636,9 @@ export class SchemaComparator {
|
|
|
610
636
|
// expressions like check constraints might be normalized by the driver,
|
|
611
637
|
// e.g. quotes might be added (https://github.com/mikro-orm/mikro-orm/issues/3827)
|
|
612
638
|
const simplify = (str) => {
|
|
613
|
-
return str
|
|
639
|
+
return (str
|
|
614
640
|
?.replace(/_\w+'(.*?)'/g, '$1')
|
|
615
|
-
.replace(/in\s*\((.*?)\)/
|
|
641
|
+
.replace(/in\s*\((.*?)\)/gi, '= any (array[$1])')
|
|
616
642
|
// MySQL normalizes count(*) to count(0)
|
|
617
643
|
.replace(/\bcount\s*\(\s*0\s*\)/gi, 'count(*)')
|
|
618
644
|
// Remove quotes first so we can process identifiers
|
|
@@ -628,7 +654,7 @@ export class SchemaComparator {
|
|
|
628
654
|
.replace(/\bas\b/gi, '')
|
|
629
655
|
// Remove remaining special chars, parentheses, type casts, asterisks, and normalize whitespace
|
|
630
656
|
.replace(/[()\n[\]*]|::\w+| +/g, '')
|
|
631
|
-
.replace(/anyarray\[(.*)]/
|
|
657
|
+
.replace(/anyarray\[(.*)]/gi, '$1')
|
|
632
658
|
.toLowerCase()
|
|
633
659
|
// PostgreSQL adds default aliases to aggregate functions (e.g., count(*) AS count)
|
|
634
660
|
// After removing AS and whitespace, this results in duplicate adjacent words
|
|
@@ -636,7 +662,7 @@ export class SchemaComparator {
|
|
|
636
662
|
// Use lookahead to match repeated patterns of 3+ chars (avoid false positives on short sequences)
|
|
637
663
|
.replace(/(\w{3,})\1/g, '$1')
|
|
638
664
|
// Remove trailing semicolon (PostgreSQL adds it to view definitions)
|
|
639
|
-
.replace(/;$/, '');
|
|
665
|
+
.replace(/;$/, ''));
|
|
640
666
|
};
|
|
641
667
|
return simplify(expr1) !== simplify(expr2);
|
|
642
668
|
}
|
|
@@ -645,13 +671,13 @@ export class SchemaComparator {
|
|
|
645
671
|
if (!defaultValue) {
|
|
646
672
|
return null;
|
|
647
673
|
}
|
|
648
|
-
const val = defaultValue
|
|
649
|
-
.replace(/^(_\w+\\)?'(.*?)\\?'$/, '$2')
|
|
650
|
-
.replace(/^\(?'(.*?)'\)?$/, '$1');
|
|
674
|
+
const val = defaultValue.replace(/^(_\w+\\)?'(.*?)\\?'$/, '$2').replace(/^\(?'(.*?)'\)?$/, '$1');
|
|
651
675
|
return parseJsonSafe(val);
|
|
652
676
|
}
|
|
653
677
|
hasSameDefaultValue(from, to) {
|
|
654
|
-
if (from.default == null ||
|
|
678
|
+
if (from.default == null ||
|
|
679
|
+
from.default.toString().toLowerCase() === 'null' ||
|
|
680
|
+
from.default.toString().startsWith('nextval(')) {
|
|
655
681
|
return to.default == null || to.default.toLowerCase() === 'null';
|
|
656
682
|
}
|
|
657
683
|
if (to.mappedType instanceof BooleanType) {
|
|
@@ -673,15 +699,15 @@ export class SchemaComparator {
|
|
|
673
699
|
if (from.default && to.default) {
|
|
674
700
|
return from.default.toString().toLowerCase() === to.default.toString().toLowerCase();
|
|
675
701
|
}
|
|
676
|
-
if (['', this
|
|
677
|
-
return ['', this
|
|
702
|
+
if (['', this.#helper.getDefaultEmptyString()].includes(to.default) && from.default != null) {
|
|
703
|
+
return ['', this.#helper.getDefaultEmptyString()].includes(from.default.toString());
|
|
678
704
|
}
|
|
679
705
|
// eslint-disable-next-line eqeqeq
|
|
680
706
|
return from.default == to.default; // == intentionally
|
|
681
707
|
}
|
|
682
708
|
mapColumnToProperty(column) {
|
|
683
|
-
const length =
|
|
684
|
-
const match =
|
|
709
|
+
const length = /\w+\((\d+)\)/.exec(column.type);
|
|
710
|
+
const match = /\w+\((\d+), ?(\d+)\)/.exec(column.type);
|
|
685
711
|
return {
|
|
686
712
|
fieldNames: [column.name],
|
|
687
713
|
columnTypes: [column.type],
|
|
@@ -696,6 +722,6 @@ export class SchemaComparator {
|
|
|
696
722
|
if (params) {
|
|
697
723
|
message += ' ' + inspect(params);
|
|
698
724
|
}
|
|
699
|
-
this
|
|
725
|
+
this.#logger.log('schema', message);
|
|
700
726
|
}
|
|
701
727
|
}
|
package/schema/SchemaHelper.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Connection, type Dictionary, RawQueryFragment } from '@mikro-orm/core';
|
|
1
|
+
import { type Connection, type Dictionary, type Options, RawQueryFragment } from '@mikro-orm/core';
|
|
2
2
|
import type { AbstractSqlConnection } from '../AbstractSqlConnection.js';
|
|
3
3
|
import type { AbstractSqlPlatform } from '../AbstractSqlPlatform.js';
|
|
4
4
|
import type { CheckDef, Column, ForeignKey, IndexDef, Table, TableDifference } from '../typings.js';
|
|
@@ -71,18 +71,7 @@ export declare abstract class SchemaHelper {
|
|
|
71
71
|
createCheck(table: DatabaseTable, check: CheckDef): string;
|
|
72
72
|
protected getTableName(table: string, schema?: string): string;
|
|
73
73
|
getTablesGroupedBySchemas(tables: Table[]): Map<string | undefined, Table[]>;
|
|
74
|
-
get options():
|
|
75
|
-
disableForeignKeys?: boolean;
|
|
76
|
-
disableForeignKeysForClear?: boolean;
|
|
77
|
-
createForeignKeyConstraints?: boolean;
|
|
78
|
-
ignoreSchema?: string[];
|
|
79
|
-
skipTables?: (string | RegExp)[];
|
|
80
|
-
skipViews?: (string | RegExp)[];
|
|
81
|
-
skipColumns?: Dictionary<(string | RegExp)[]>;
|
|
82
|
-
managementDbName?: string;
|
|
83
|
-
defaultUpdateRule?: "cascade" | "no action" | "set null" | "set default" | "restrict";
|
|
84
|
-
defaultDeleteRule?: "cascade" | "no action" | "set null" | "set default" | "restrict";
|
|
85
|
-
};
|
|
74
|
+
get options(): NonNullable<Options['schemaGenerator']>;
|
|
86
75
|
protected processComment(comment: string): string;
|
|
87
76
|
protected quote(...keys: (string | undefined)[]): string;
|
|
88
77
|
dropForeignKey(tableName: string, constraintName: string): string;
|
package/schema/SchemaHelper.js
CHANGED
|
@@ -36,7 +36,7 @@ export class SchemaHelper {
|
|
|
36
36
|
return Utils.flatten(pks);
|
|
37
37
|
}
|
|
38
38
|
inferLengthFromColumnType(type) {
|
|
39
|
-
const match =
|
|
39
|
+
const match = /^\w+\s*(?:\(\s*(\d+)\s*\)|$)/.exec(type);
|
|
40
40
|
if (!match) {
|
|
41
41
|
return;
|
|
42
42
|
}
|
|
@@ -78,7 +78,7 @@ export class SchemaHelper {
|
|
|
78
78
|
tableName = this.quote(tableName);
|
|
79
79
|
oldColumnName = this.quote(oldColumnName);
|
|
80
80
|
const columnName = this.quote(to.name);
|
|
81
|
-
const schemaReference =
|
|
81
|
+
const schemaReference = schemaName !== undefined && schemaName !== 'public' ? '"' + schemaName + '".' : '';
|
|
82
82
|
const tableReference = schemaReference + tableName;
|
|
83
83
|
return `alter table ${tableReference} rename column ${oldColumnName} to ${columnName}`;
|
|
84
84
|
}
|
|
@@ -124,7 +124,8 @@ export class SchemaHelper {
|
|
|
124
124
|
*/
|
|
125
125
|
getIndexColumns(index) {
|
|
126
126
|
if (index.columns?.length) {
|
|
127
|
-
return index.columns
|
|
127
|
+
return index.columns
|
|
128
|
+
.map(col => {
|
|
128
129
|
let colDef = this.quote(col.name);
|
|
129
130
|
// Collation comes after column name (SQLite syntax: column COLLATE name)
|
|
130
131
|
if (col.collation) {
|
|
@@ -139,7 +140,8 @@ export class SchemaHelper {
|
|
|
139
140
|
colDef += ` nulls ${col.nulls}`;
|
|
140
141
|
}
|
|
141
142
|
return colDef;
|
|
142
|
-
})
|
|
143
|
+
})
|
|
144
|
+
.join(', ');
|
|
143
145
|
}
|
|
144
146
|
return index.columnNames.map(c => this.quote(c)).join(', ');
|
|
145
147
|
}
|
|
@@ -256,9 +258,11 @@ export class SchemaHelper {
|
|
|
256
258
|
return ret;
|
|
257
259
|
}
|
|
258
260
|
getAddColumnsSQL(table, columns) {
|
|
259
|
-
const adds = columns
|
|
261
|
+
const adds = columns
|
|
262
|
+
.map(column => {
|
|
260
263
|
return `add ${this.createTableColumn(column, table)}`;
|
|
261
|
-
})
|
|
264
|
+
})
|
|
265
|
+
.join(', ');
|
|
262
266
|
return [`alter table ${table.getQuotedName()} ${adds}`];
|
|
263
267
|
}
|
|
264
268
|
getDropColumnsSQL(tableName, columns, schemaName) {
|
|
@@ -310,12 +314,26 @@ export class SchemaHelper {
|
|
|
310
314
|
Utils.runIfNotEmpty(() => col.push('not null'), !column.nullable && !column.generated);
|
|
311
315
|
Utils.runIfNotEmpty(() => col.push('auto_increment'), column.autoincrement);
|
|
312
316
|
Utils.runIfNotEmpty(() => col.push('unique'), column.autoincrement && !column.primary);
|
|
313
|
-
if (column.autoincrement &&
|
|
317
|
+
if (column.autoincrement &&
|
|
318
|
+
!column.generated &&
|
|
319
|
+
!compositePK &&
|
|
320
|
+
(!changedProperties || changedProperties.has('autoincrement') || changedProperties.has('type'))) {
|
|
314
321
|
Utils.runIfNotEmpty(() => col.push('primary key'), primaryKey && column.primary);
|
|
315
322
|
}
|
|
316
323
|
if (useDefault) {
|
|
317
324
|
// https://dev.mysql.com/doc/refman/9.0/en/data-type-defaults.html
|
|
318
|
-
const needsExpression = [
|
|
325
|
+
const needsExpression = [
|
|
326
|
+
'blob',
|
|
327
|
+
'text',
|
|
328
|
+
'json',
|
|
329
|
+
'point',
|
|
330
|
+
'linestring',
|
|
331
|
+
'polygon',
|
|
332
|
+
'multipoint',
|
|
333
|
+
'multilinestring',
|
|
334
|
+
'multipolygon',
|
|
335
|
+
'geometrycollection',
|
|
336
|
+
].some(type => column.type.toLowerCase().startsWith(type));
|
|
319
337
|
const defaultSql = needsExpression && !column.default.startsWith('(') ? `(${column.default})` : column.default;
|
|
320
338
|
col.push(`default ${defaultSql}`);
|
|
321
339
|
}
|
|
@@ -371,7 +389,9 @@ export class SchemaHelper {
|
|
|
371
389
|
columnNames: [fk.column_name],
|
|
372
390
|
constraintName: fk.constraint_name,
|
|
373
391
|
localTableName: schemaName ? `${schemaName}.${tableName}` : tableName,
|
|
374
|
-
referencedTableName: fk.referenced_schema_name
|
|
392
|
+
referencedTableName: fk.referenced_schema_name
|
|
393
|
+
? `${fk.referenced_schema_name}.${fk.referenced_table_name}`
|
|
394
|
+
: fk.referenced_table_name,
|
|
375
395
|
referencedColumnNames: [fk.referenced_column_name],
|
|
376
396
|
updateRule: fk.update_rule.toLowerCase(),
|
|
377
397
|
deleteRule: fk.delete_rule.toLowerCase(),
|
|
@@ -396,6 +416,7 @@ export class SchemaHelper {
|
|
|
396
416
|
return norm[0].replace('(?)', length != null ? `(${length})` : '');
|
|
397
417
|
}
|
|
398
418
|
getCreateDatabaseSQL(name) {
|
|
419
|
+
name = this.quote(name);
|
|
399
420
|
// two line breaks to force separate execution
|
|
400
421
|
return `create database ${name};\n\nuse ${name}`;
|
|
401
422
|
}
|
|
@@ -1,22 +1,12 @@
|
|
|
1
|
-
import { type ClearDatabaseOptions, type CreateSchemaOptions, type
|
|
1
|
+
import { type ClearDatabaseOptions, type CreateSchemaOptions, type DropSchemaOptions, type EnsureDatabaseOptions, type EntityMetadata, type ISchemaGenerator, type MikroORM, type Options, type Transaction, type UpdateSchemaOptions } from '@mikro-orm/core';
|
|
2
2
|
import { AbstractSchemaGenerator } from '@mikro-orm/core/schema';
|
|
3
3
|
import type { SchemaDifference } from '../typings.js';
|
|
4
4
|
import { DatabaseSchema } from './DatabaseSchema.js';
|
|
5
5
|
import type { AbstractSqlDriver } from '../AbstractSqlDriver.js';
|
|
6
|
+
import type { SchemaHelper } from './SchemaHelper.js';
|
|
6
7
|
export declare class SqlSchemaGenerator extends AbstractSchemaGenerator<AbstractSqlDriver> implements ISchemaGenerator {
|
|
7
|
-
protected readonly helper:
|
|
8
|
-
protected readonly options:
|
|
9
|
-
disableForeignKeys?: boolean;
|
|
10
|
-
disableForeignKeysForClear?: boolean;
|
|
11
|
-
createForeignKeyConstraints?: boolean;
|
|
12
|
-
ignoreSchema?: string[];
|
|
13
|
-
skipTables?: (string | RegExp)[];
|
|
14
|
-
skipViews?: (string | RegExp)[];
|
|
15
|
-
skipColumns?: Dictionary<(string | RegExp)[]>;
|
|
16
|
-
managementDbName?: string;
|
|
17
|
-
defaultUpdateRule?: "cascade" | "no action" | "set null" | "set default" | "restrict";
|
|
18
|
-
defaultDeleteRule?: "cascade" | "no action" | "set null" | "set default" | "restrict";
|
|
19
|
-
};
|
|
8
|
+
protected readonly helper: SchemaHelper;
|
|
9
|
+
protected readonly options: NonNullable<Options['schemaGenerator']>;
|
|
20
10
|
protected lastEnsuredDatabase?: string;
|
|
21
11
|
static register(orm: MikroORM): void;
|
|
22
12
|
create(options?: CreateSchemaOptions): Promise<void>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CommitOrderCalculator, Utils, } from '@mikro-orm/core';
|
|
1
|
+
import { CommitOrderCalculator, TableNotFoundException, Utils, } from '@mikro-orm/core';
|
|
2
2
|
import { AbstractSchemaGenerator } from '@mikro-orm/core/schema';
|
|
3
3
|
import { DatabaseSchema } from './DatabaseSchema.js';
|
|
4
4
|
import { SchemaComparator } from './SchemaComparator.js';
|
|
@@ -128,10 +128,19 @@ export class SqlSchemaGenerator extends AbstractSchemaGenerator {
|
|
|
128
128
|
}
|
|
129
129
|
const schema = options?.schema ?? this.config.get('schema', this.platform.getDefaultSchemaName());
|
|
130
130
|
for (const meta of this.getOrderedMetadata(schema).reverse()) {
|
|
131
|
-
|
|
132
|
-
.
|
|
133
|
-
|
|
134
|
-
|
|
131
|
+
try {
|
|
132
|
+
await this.driver
|
|
133
|
+
.createQueryBuilder(meta.class, this.em?.getTransactionContext(), 'write', false)
|
|
134
|
+
.withSchema(schema)
|
|
135
|
+
.truncate()
|
|
136
|
+
.execute();
|
|
137
|
+
}
|
|
138
|
+
catch (e) {
|
|
139
|
+
if (this.platform.getExceptionConverter().convertException(e) instanceof TableNotFoundException) {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
throw e;
|
|
143
|
+
}
|
|
135
144
|
}
|
|
136
145
|
if (this.options.disableForeignKeysForClear) {
|
|
137
146
|
await this.execute(this.helper.enableForeignKeysSQL());
|
|
@@ -224,8 +233,11 @@ export class SqlSchemaGenerator extends AbstractSchemaGenerator {
|
|
|
224
233
|
options.dropTables ??= true;
|
|
225
234
|
const toSchema = this.getTargetSchema(options.schema);
|
|
226
235
|
const schemas = toSchema.getNamespaces();
|
|
227
|
-
const fromSchema = options.fromSchema ??
|
|
228
|
-
|
|
236
|
+
const fromSchema = options.fromSchema ??
|
|
237
|
+
(await DatabaseSchema.create(this.connection, this.platform, this.config, options.schema, schemas, undefined, this.options.skipTables, this.options.skipViews));
|
|
238
|
+
const wildcardSchemaTables = [...this.metadata.getAll().values()]
|
|
239
|
+
.filter(meta => meta.schema === '*')
|
|
240
|
+
.map(meta => meta.tableName);
|
|
229
241
|
fromSchema.prune(options.schema, wildcardSchemaTables);
|
|
230
242
|
toSchema.prune(options.schema, wildcardSchemaTables);
|
|
231
243
|
return { fromSchema, toSchema };
|
|
@@ -368,7 +380,7 @@ export class SqlSchemaGenerator extends AbstractSchemaGenerator {
|
|
|
368
380
|
*/
|
|
369
381
|
async createDatabase(name, options) {
|
|
370
382
|
name ??= this.config.get('dbName');
|
|
371
|
-
const sql = this.helper.getCreateDatabaseSQL(
|
|
383
|
+
const sql = this.helper.getCreateDatabaseSQL(name);
|
|
372
384
|
if (sql) {
|
|
373
385
|
await this.execute(sql);
|
|
374
386
|
}
|
|
@@ -408,7 +420,11 @@ export class SqlSchemaGenerator extends AbstractSchemaGenerator {
|
|
|
408
420
|
return;
|
|
409
421
|
}
|
|
410
422
|
const statements = groups.flatMap(group => {
|
|
411
|
-
return group
|
|
423
|
+
return group
|
|
424
|
+
.join('\n')
|
|
425
|
+
.split(';\n')
|
|
426
|
+
.map(s => s.trim())
|
|
427
|
+
.filter(s => s);
|
|
412
428
|
});
|
|
413
429
|
await Utils.runSerial(statements, stmt => this.driver.execute(stmt));
|
|
414
430
|
}
|
|
@@ -476,9 +492,7 @@ export class SqlSchemaGenerator extends AbstractSchemaGenerator {
|
|
|
476
492
|
}
|
|
477
493
|
// Check if the definition references the other view's name
|
|
478
494
|
// Use word boundary matching to avoid false positives
|
|
479
|
-
const patterns = [
|
|
480
|
-
new RegExp(`\\b${this.escapeRegExp(otherView.name.toLowerCase())}\\b`),
|
|
481
|
-
];
|
|
495
|
+
const patterns = [new RegExp(`\\b${this.escapeRegExp(otherView.name.toLowerCase())}\\b`)];
|
|
482
496
|
if (otherView.schema) {
|
|
483
497
|
patterns.push(new RegExp(`\\b${this.escapeRegExp(`${otherView.schema}.${otherView.name}`.toLowerCase())}\\b`));
|
|
484
498
|
}
|
package/typings.d.ts
CHANGED
|
@@ -3,8 +3,8 @@ import type { CheckCallback, DeferMode, Dictionary, EntityName, EntityProperty,
|
|
|
3
3
|
import type { JoinType, QueryType } from './query/enums.js';
|
|
4
4
|
import type { DatabaseSchema } from './schema/DatabaseSchema.js';
|
|
5
5
|
import type { DatabaseTable } from './schema/DatabaseTable.js';
|
|
6
|
-
import type {
|
|
7
|
-
import type { NativeQueryBuilder } from './query/NativeQueryBuilder.js';
|
|
6
|
+
import type { AnyQueryBuilder } from './query/QueryBuilder.js';
|
|
7
|
+
import type { CteOptions, NativeQueryBuilder } from './query/NativeQueryBuilder.js';
|
|
8
8
|
import type { MikroKyselyPluginOptions } from './plugin/index.js';
|
|
9
9
|
export interface Table {
|
|
10
10
|
table_name: string;
|
|
@@ -12,7 +12,7 @@ export interface Table {
|
|
|
12
12
|
table_comment?: string;
|
|
13
13
|
}
|
|
14
14
|
/** @internal */
|
|
15
|
-
export type InternalField<T> = string | RawQueryFragment |
|
|
15
|
+
export type InternalField<T> = string | RawQueryFragment | AnyQueryBuilder | NativeQueryBuilder;
|
|
16
16
|
export interface JoinOptions {
|
|
17
17
|
table: string;
|
|
18
18
|
schema?: string;
|
|
@@ -171,7 +171,10 @@ export interface IQueryBuilder<T> {
|
|
|
171
171
|
readonly alias: string;
|
|
172
172
|
readonly type: QueryType;
|
|
173
173
|
/** @internal */
|
|
174
|
-
|
|
174
|
+
state: {
|
|
175
|
+
fields?: InternalField<T>[];
|
|
176
|
+
[key: string]: any;
|
|
177
|
+
};
|
|
175
178
|
/** @internal */
|
|
176
179
|
helper: any;
|
|
177
180
|
select(fields: string | RawQueryFragment | (string | RawQueryFragment)[], distinct?: boolean): this;
|
|
@@ -202,6 +205,8 @@ export interface IQueryBuilder<T> {
|
|
|
202
205
|
setFlag(flag: QueryFlag): this;
|
|
203
206
|
unsetFlag(flag: QueryFlag): this;
|
|
204
207
|
hasFlag(flag: QueryFlag): boolean;
|
|
208
|
+
with(name: string, query: AnyQueryBuilder | NativeQueryBuilder | RawQueryFragment, options?: CteOptions): this;
|
|
209
|
+
withRecursive(name: string, query: AnyQueryBuilder | NativeQueryBuilder | RawQueryFragment, options?: CteOptions): this;
|
|
205
210
|
scheduleFilterCheck(path: string): void;
|
|
206
211
|
withSchema(schema: string): this;
|
|
207
212
|
}
|
|
@@ -309,7 +314,7 @@ type MaybeNever<TValue, TOptions> = TOptions extends {
|
|
|
309
314
|
type ExcludeNever<TMap extends Record<string, any>> = {
|
|
310
315
|
[K in keyof TMap as TMap[K] extends never ? never : K]: TMap[K];
|
|
311
316
|
};
|
|
312
|
-
export type InferClassEntityDB<TEntities, TOptions extends MikroKyselyPluginOptions = {}> = ClassEntityDBMap<TEntities, TOptions> extends infer R ? [keyof R] extends [never] ? unknown : R : never;
|
|
317
|
+
export type InferClassEntityDB<TEntities, TOptions extends MikroKyselyPluginOptions = {}> = ClassEntityDBMap<TEntities, TOptions> extends infer R ? ([keyof R] extends [never] ? unknown : R) : never;
|
|
313
318
|
type ClassEntityDBMap<TEntities, TOptions extends MikroKyselyPluginOptions = {}> = {
|
|
314
319
|
[T in TEntities as ClassEntityTableName<T, TOptions>]: ClassEntityColumns<T>;
|
|
315
320
|
};
|