@mikro-orm/knex 6.0.0-dev.2 → 6.0.0-dev.200

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/{dist/AbstractSqlConnection.d.ts → AbstractSqlConnection.d.ts} +8 -4
  2. package/{dist/AbstractSqlConnection.js → AbstractSqlConnection.js} +35 -18
  3. package/AbstractSqlDriver.d.ts +73 -0
  4. package/AbstractSqlDriver.js +1085 -0
  5. package/AbstractSqlPlatform.d.ts +22 -0
  6. package/{dist/AbstractSqlPlatform.js → AbstractSqlPlatform.js} +46 -19
  7. package/{dist/MonkeyPatchable.d.ts → MonkeyPatchable.d.ts} +1 -0
  8. package/{dist/MonkeyPatchable.js → MonkeyPatchable.js} +3 -0
  9. package/PivotCollectionPersister.d.ts +17 -0
  10. package/PivotCollectionPersister.js +128 -0
  11. package/{dist/README.md → README.md} +66 -109
  12. package/{dist/SqlEntityManager.d.ts → SqlEntityManager.d.ts} +6 -10
  13. package/{dist/SqlEntityManager.js → SqlEntityManager.js} +5 -10
  14. package/SqlEntityRepository.d.ts +24 -0
  15. package/{dist/SqlEntityRepository.js → SqlEntityRepository.js} +11 -8
  16. package/{dist/index.d.ts → index.d.ts} +1 -0
  17. package/{dist/index.js → index.js} +6 -1
  18. package/index.mjs +211 -0
  19. package/package.json +20 -49
  20. package/query/ArrayCriteriaNode.d.ts +10 -0
  21. package/{dist/query → query}/ArrayCriteriaNode.js +7 -2
  22. package/{dist/query → query}/CriteriaNode.d.ts +11 -9
  23. package/{dist/query → query}/CriteriaNode.js +31 -15
  24. package/query/CriteriaNodeFactory.d.ts +12 -0
  25. package/{dist/query → query}/CriteriaNodeFactory.js +32 -15
  26. package/query/ObjectCriteriaNode.d.ts +15 -0
  27. package/{dist/query → query}/ObjectCriteriaNode.js +68 -21
  28. package/query/QueryBuilder.d.ts +284 -0
  29. package/query/QueryBuilder.js +1218 -0
  30. package/{dist/query → query}/QueryBuilderHelper.d.ts +26 -23
  31. package/query/QueryBuilderHelper.js +661 -0
  32. package/query/ScalarCriteriaNode.d.ts +10 -0
  33. package/{dist/query → query}/ScalarCriteriaNode.js +12 -8
  34. package/query/enums.d.ts +15 -0
  35. package/query/enums.js +20 -0
  36. package/{dist/query → query}/index.js +5 -1
  37. package/{dist/schema → schema}/DatabaseSchema.d.ts +6 -3
  38. package/{dist/schema → schema}/DatabaseSchema.js +35 -25
  39. package/{dist/schema → schema}/DatabaseTable.d.ts +21 -13
  40. package/schema/DatabaseTable.js +645 -0
  41. package/{dist/schema → schema}/SchemaComparator.d.ts +9 -7
  42. package/{dist/schema → schema}/SchemaComparator.js +99 -25
  43. package/{dist/schema → schema}/SchemaHelper.d.ts +20 -13
  44. package/{dist/schema → schema}/SchemaHelper.js +70 -18
  45. package/{dist/schema/SchemaGenerator.d.ts → schema/SqlSchemaGenerator.d.ts} +11 -14
  46. package/{dist/schema/SchemaGenerator.js → schema/SqlSchemaGenerator.js} +149 -63
  47. package/{dist/schema → schema}/index.d.ts +1 -1
  48. package/{dist/schema → schema}/index.js +6 -2
  49. package/{dist/typings.d.ts → typings.d.ts} +45 -22
  50. package/CHANGELOG.md +0 -661
  51. package/dist/AbstractSqlDriver.d.ts +0 -55
  52. package/dist/AbstractSqlDriver.js +0 -644
  53. package/dist/AbstractSqlPlatform.d.ts +0 -21
  54. package/dist/LICENSE +0 -21
  55. package/dist/SqlEntityRepository.d.ts +0 -23
  56. package/dist/index.mjs +0 -26
  57. package/dist/package.json +0 -99
  58. package/dist/query/ArrayCriteriaNode.d.ts +0 -9
  59. package/dist/query/CriteriaNodeFactory.d.ts +0 -12
  60. package/dist/query/ObjectCriteriaNode.d.ts +0 -14
  61. package/dist/query/QueryBuilder.d.ts +0 -211
  62. package/dist/query/QueryBuilder.js +0 -688
  63. package/dist/query/QueryBuilderHelper.js +0 -523
  64. package/dist/query/ScalarCriteriaNode.d.ts +0 -9
  65. package/dist/query/enums.d.ts +0 -8
  66. package/dist/query/enums.js +0 -12
  67. package/dist/schema/DatabaseTable.js +0 -319
  68. /package/{dist/query → query}/index.d.ts +0 -0
  69. /package/{dist/typings.js → typings.js} +0 -0
@@ -1,4 +1,4 @@
1
- import type { Check, Column, ForeignKey, Index, SchemaDifference, TableDifference } from '../typings';
1
+ import type { Column, ForeignKey, IndexDef, SchemaDifference, TableDifference } from '../typings';
2
2
  import type { DatabaseSchema } from './DatabaseSchema';
3
3
  import type { DatabaseTable } from './DatabaseTable';
4
4
  import type { AbstractSqlPlatform } from '../AbstractSqlPlatform';
@@ -8,6 +8,7 @@ import type { AbstractSqlPlatform } from '../AbstractSqlPlatform';
8
8
  export declare class SchemaComparator {
9
9
  private readonly platform;
10
10
  private readonly helper;
11
+ private readonly logger;
11
12
  constructor(platform: AbstractSqlPlatform);
12
13
  /**
13
14
  * Returns a SchemaDifference object containing the differences between the schemas fromSchema and toSchema.
@@ -16,12 +17,12 @@ export declare class SchemaComparator {
16
17
  * operations to change the schema stored in fromSchema to the schema that is
17
18
  * stored in toSchema.
18
19
  */
19
- compare(fromSchema: DatabaseSchema, toSchema: DatabaseSchema): SchemaDifference;
20
+ compare(fromSchema: DatabaseSchema, toSchema: DatabaseSchema, inverseDiff?: SchemaDifference): SchemaDifference;
20
21
  /**
21
22
  * Returns the difference between the tables fromTable and toTable.
22
23
  * If there are no differences this method returns the boolean false.
23
24
  */
24
- diffTable(fromTable: DatabaseTable, toTable: DatabaseTable): TableDifference | false;
25
+ diffTable(fromTable: DatabaseTable, toTable: DatabaseTable, inverseTableDiff?: TableDifference): TableDifference | false;
25
26
  /**
26
27
  * Try to find columns that only changed their name, rename operations maybe cheaper than add/drop
27
28
  * however ambiguities between different possibilities should not lead to renaming at all.
@@ -37,19 +38,20 @@ export declare class SchemaComparator {
37
38
  * Returns the difference between the columns
38
39
  * If there are differences this method returns field2, otherwise the boolean false.
39
40
  */
40
- diffColumn(column1: Column, column2: Column): Set<string>;
41
+ diffColumn(fromColumn: Column, toColumn: Column, tableName?: string): Set<string>;
41
42
  diffEnumItems(items1?: string[], items2?: string[]): boolean;
42
43
  diffComment(comment1?: string, comment2?: string): boolean;
43
44
  /**
44
45
  * Finds the difference between the indexes index1 and index2.
45
46
  * Compares index1 with index2 and returns index2 if there are any differences or false in case there are no differences.
46
47
  */
47
- diffIndex(index1: Index, index2: Index): boolean;
48
+ diffIndex(index1: IndexDef, index2: IndexDef): boolean;
48
49
  /**
49
50
  * Checks if the other index already fulfills all the indexing and constraint needs of the current one.
50
51
  */
51
- isIndexFulfilledBy(index1: Index, index2: Index): boolean;
52
- diffCheck(check1: Check, check2: Check): boolean;
52
+ isIndexFulfilledBy(index1: IndexDef, index2: IndexDef): boolean;
53
+ diffExpression(expr1: string, expr2: string): boolean;
53
54
  hasSameDefaultValue(from: Column, to: Column): boolean;
54
55
  private mapColumnToProperty;
56
+ private log;
55
57
  }
@@ -1,14 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SchemaComparator = void 0;
4
+ const util_1 = require("util");
4
5
  const core_1 = require("@mikro-orm/core");
5
6
  /**
6
7
  * Compares two Schemas and return an instance of SchemaDifference.
7
8
  */
8
9
  class SchemaComparator {
10
+ platform;
11
+ helper;
12
+ logger;
9
13
  constructor(platform) {
10
14
  this.platform = platform;
11
15
  this.helper = this.platform.getSchemaHelper();
16
+ this.logger = this.platform.getConfig().getLogger();
12
17
  }
13
18
  /**
14
19
  * Returns a SchemaDifference object containing the differences between the schemas fromSchema and toSchema.
@@ -17,7 +22,7 @@ class SchemaComparator {
17
22
  * operations to change the schema stored in fromSchema to the schema that is
18
23
  * stored in toSchema.
19
24
  */
20
- compare(fromSchema, toSchema) {
25
+ compare(fromSchema, toSchema, inverseDiff) {
21
26
  const diff = { newTables: {}, removedTables: {}, changedTables: {}, orphanedForeignKeys: [], newNamespaces: new Set(), removedNamespaces: new Set(), fromSchema };
22
27
  const foreignKeysToTable = {};
23
28
  for (const namespace of toSchema.getNamespaces()) {
@@ -38,7 +43,7 @@ class SchemaComparator {
38
43
  diff.newTables[tableName] = toSchema.getTable(tableName);
39
44
  }
40
45
  else {
41
- const tableDifferences = this.diffTable(fromSchema.getTable(tableName), toSchema.getTable(tableName));
46
+ const tableDifferences = this.diffTable(fromSchema.getTable(tableName), toSchema.getTable(tableName), inverseDiff?.changedTables[tableName]);
42
47
  if (tableDifferences !== false) {
43
48
  diff.changedTables[tableName] = tableDifferences;
44
49
  }
@@ -86,7 +91,7 @@ class SchemaComparator {
86
91
  * Returns the difference between the tables fromTable and toTable.
87
92
  * If there are no differences this method returns the boolean false.
88
93
  */
89
- diffTable(fromTable, toTable) {
94
+ diffTable(fromTable, toTable, inverseTableDiff) {
90
95
  let changes = 0;
91
96
  const tableDifferences = {
92
97
  name: fromTable.getShortestName(),
@@ -105,18 +110,22 @@ class SchemaComparator {
105
110
  renamedColumns: {},
106
111
  renamedIndexes: {},
107
112
  fromTable,
113
+ toTable,
108
114
  };
109
115
  if (this.diffComment(fromTable.comment, toTable.comment)) {
110
116
  tableDifferences.changedComment = toTable.comment;
117
+ this.log(`table comment changed for ${tableDifferences.name}`, { fromTableComment: fromTable.comment, toTableComment: toTable.comment });
111
118
  }
112
119
  const fromTableColumns = fromTable.getColumns();
113
120
  const toTableColumns = toTable.getColumns();
121
+ const tableName = (toTable.schema ? toTable.schema + '.' : '') + toTable.name;
114
122
  // See if all the columns in "from" table exist in "to" table
115
123
  for (const column of toTableColumns) {
116
124
  if (fromTable.hasColumn(column.name)) {
117
125
  continue;
118
126
  }
119
127
  tableDifferences.addedColumns[column.name] = column;
128
+ this.log(`column ${tableDifferences.name}.${column.name} of type ${column.type} added`);
120
129
  changes++;
121
130
  }
122
131
  /* See if there are any removed columns in "to" table */
@@ -124,23 +133,31 @@ class SchemaComparator {
124
133
  // See if column is removed in "to" table.
125
134
  if (!toTable.hasColumn(column.name)) {
126
135
  tableDifferences.removedColumns[column.name] = column;
136
+ this.log(`column ${tableDifferences.name}.${column.name} removed`);
127
137
  changes++;
128
138
  continue;
129
139
  }
130
140
  // See if column has changed properties in "to" table.
131
- const changedProperties = this.diffColumn(column, toTable.getColumn(column.name));
141
+ const changedProperties = this.diffColumn(column, toTable.getColumn(column.name), tableName);
132
142
  if (changedProperties.size === 0) {
133
143
  continue;
134
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
+ }
135
151
  tableDifferences.changedColumns[column.name] = {
136
152
  oldColumnName: column.name,
137
153
  fromColumn: column,
138
154
  column: toTable.getColumn(column.name),
139
155
  changedProperties,
140
156
  };
157
+ this.log(`column ${tableDifferences.name}.${column.name} changed`, { changedProperties });
141
158
  changes++;
142
159
  }
143
- this.detectColumnRenamings(tableDifferences);
160
+ this.detectColumnRenamings(tableDifferences, inverseTableDiff);
144
161
  const fromTableIndexes = fromTable.getIndexes();
145
162
  const toTableIndexes = toTable.getIndexes();
146
163
  // See if all the indexes in "from" table exist in "to" table
@@ -149,6 +166,7 @@ class SchemaComparator {
149
166
  continue;
150
167
  }
151
168
  tableDifferences.addedIndexes[index.keyName] = index;
169
+ this.log(`index ${index.keyName} added to table ${tableDifferences.name}`, { index });
152
170
  changes++;
153
171
  }
154
172
  // See if there are any removed indexes in "to" table
@@ -156,6 +174,7 @@ class SchemaComparator {
156
174
  // See if index is removed in "to" table.
157
175
  if ((index.primary && !toTable.hasPrimaryKey()) || !index.primary && !toTable.hasIndex(index.keyName)) {
158
176
  tableDifferences.removedIndexes[index.keyName] = index;
177
+ this.log(`index ${index.keyName} removed from table ${tableDifferences.name}`);
159
178
  changes++;
160
179
  continue;
161
180
  }
@@ -165,6 +184,7 @@ class SchemaComparator {
165
184
  continue;
166
185
  }
167
186
  tableDifferences.changedIndexes[index.keyName] = toTableIndex;
187
+ this.log(`index ${index.keyName} changed in table ${tableDifferences.name}`, { fromTableIndex: index, toTableIndex });
168
188
  changes++;
169
189
  }
170
190
  this.detectIndexRenamings(tableDifferences);
@@ -176,20 +196,23 @@ class SchemaComparator {
176
196
  continue;
177
197
  }
178
198
  tableDifferences.addedChecks[check.name] = check;
199
+ this.log(`check constraint ${check.name} added to table ${tableDifferences.name}`, { check });
179
200
  changes++;
180
201
  }
181
202
  // See if there are any removed checks in "to" table
182
203
  for (const check of fromTableChecks) {
183
204
  if (!toTable.hasCheck(check.name)) {
184
205
  tableDifferences.removedChecks[check.name] = check;
206
+ this.log(`check constraint ${check.name} removed from table ${tableDifferences.name}`);
185
207
  changes++;
186
208
  continue;
187
209
  }
188
210
  // See if index has changed in "to" table
189
211
  const toTableCheck = toTable.getCheck(check.name);
190
- if (!this.diffCheck(check, toTableCheck)) {
212
+ if (!this.diffExpression(check.expression, toTableCheck.expression)) {
191
213
  continue;
192
214
  }
215
+ this.log(`check constraint ${check.name} changed in table ${tableDifferences.name}`, { fromTableCheck: check, toTableCheck });
193
216
  tableDifferences.changedChecks[check.name] = toTableCheck;
194
217
  changes++;
195
218
  }
@@ -202,6 +225,7 @@ class SchemaComparator {
202
225
  delete toForeignKeys[toConstraint.constraintName];
203
226
  }
204
227
  else if (fromConstraint.constraintName.toLowerCase() === toConstraint.constraintName.toLowerCase()) {
228
+ this.log(`FK constraint ${fromConstraint.constraintName} changed in table ${tableDifferences.name}`, { fromConstraint, toConstraint });
205
229
  tableDifferences.changedForeignKeys[toConstraint.constraintName] = toConstraint;
206
230
  changes++;
207
231
  delete fromForeignKeys[fromConstraint.constraintName];
@@ -211,10 +235,12 @@ class SchemaComparator {
211
235
  }
212
236
  for (const fromConstraint of Object.values(fromForeignKeys)) {
213
237
  tableDifferences.removedForeignKeys[fromConstraint.constraintName] = fromConstraint;
238
+ this.log(`FK constraint ${fromConstraint.constraintName} removed from table ${tableDifferences.name}`);
214
239
  changes++;
215
240
  }
216
241
  for (const toConstraint of Object.values(toForeignKeys)) {
217
242
  tableDifferences.addedForeignKeys[toConstraint.constraintName] = toConstraint;
243
+ this.log(`FK constraint ${toConstraint.constraintName} added from table ${tableDifferences.name}`, { constraint: toConstraint });
218
244
  changes++;
219
245
  }
220
246
  return changes ? tableDifferences : false;
@@ -223,7 +249,7 @@ class SchemaComparator {
223
249
  * Try to find columns that only changed their name, rename operations maybe cheaper than add/drop
224
250
  * however ambiguities between different possibilities should not lead to renaming at all.
225
251
  */
226
- detectColumnRenamings(tableDifferences) {
252
+ detectColumnRenamings(tableDifferences, inverseTableDiff) {
227
253
  const renameCandidates = {};
228
254
  for (const addedColumn of Object.values(tableDifferences.addedColumns)) {
229
255
  for (const removedColumn of Object.values(tableDifferences.removedColumns)) {
@@ -231,6 +257,10 @@ class SchemaComparator {
231
257
  if (diff.size !== 0) {
232
258
  continue;
233
259
  }
260
+ const renamedColumn = inverseTableDiff?.renamedColumns[addedColumn.name];
261
+ if (renamedColumn && renamedColumn?.name !== removedColumn.name) {
262
+ continue;
263
+ }
234
264
  renameCandidates[addedColumn.name] = renameCandidates[addedColumn.name] ?? [];
235
265
  renameCandidates[addedColumn.name].push([removedColumn, addedColumn]);
236
266
  }
@@ -249,6 +279,7 @@ class SchemaComparator {
249
279
  tableDifferences.renamedColumns[removedColumnName] = addedColumn;
250
280
  delete tableDifferences.addedColumns[addedColumnName];
251
281
  delete tableDifferences.removedColumns[removedColumnName];
282
+ this.log(`renamed column detected in table ${tableDifferences.name}`, { old: removedColumnName, new: addedColumnName });
252
283
  }
253
284
  }
254
285
  /**
@@ -282,6 +313,7 @@ class SchemaComparator {
282
313
  tableDifferences.renamedIndexes[removedIndexName] = addedIndex;
283
314
  delete tableDifferences.addedIndexes[addedIndexName];
284
315
  delete tableDifferences.removedIndexes[removedIndexName];
316
+ this.log(`renamed index detected in table ${tableDifferences.name}`, { old: removedIndexName, new: addedIndexName });
285
317
  }
286
318
  }
287
319
  diffForeignKey(key1, key2) {
@@ -294,7 +326,10 @@ class SchemaComparator {
294
326
  if (key1.referencedTableName !== key2.referencedTableName) {
295
327
  return true;
296
328
  }
297
- const rule = (key, method) => (key[method] ?? this.platform.getDefaultIntegrityRule()).toLowerCase();
329
+ const defaultRule = ['restrict', 'no action'];
330
+ const rule = (key, method) => {
331
+ return (key[method] ?? defaultRule[0]).toLowerCase().replace(defaultRule[1], defaultRule[0]);
332
+ };
298
333
  const compare = (method) => rule(key1, method) === rule(key2, method);
299
334
  return !compare('updateRule') || !compare('deleteRule');
300
335
  }
@@ -302,34 +337,59 @@ class SchemaComparator {
302
337
  * Returns the difference between the columns
303
338
  * If there are differences this method returns field2, otherwise the boolean false.
304
339
  */
305
- diffColumn(column1, column2) {
340
+ diffColumn(fromColumn, toColumn, tableName) {
306
341
  const changedProperties = new Set();
307
- const prop1 = this.mapColumnToProperty({ ...column1, autoincrement: false });
308
- const prop2 = this.mapColumnToProperty({ ...column2, autoincrement: false });
309
- const columnType1 = column1.mappedType.getColumnType(prop1, this.platform);
310
- const columnType2 = column2.mappedType.getColumnType(prop2, this.platform);
311
- if (columnType1 !== columnType2) {
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();
346
+ const log = (msg, params) => {
347
+ if (tableName) {
348
+ const copy = core_1.Utils.copy(params);
349
+ core_1.Utils.dropUndefinedProperties(copy);
350
+ this.log(msg, copy);
351
+ }
352
+ };
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 });
312
357
  changedProperties.add('type');
313
358
  }
314
- if (column1.nullable !== column2.nullable) {
359
+ if (fromColumn.nullable !== toColumn.nullable && !fromColumn.generated && !toColumn.generated) {
360
+ log(`'nullable' changed for column ${tableName}.${fromColumn.name}`, { fromColumn, toColumn });
315
361
  changedProperties.add('nullable');
316
362
  }
317
- if (!!column1.autoincrement !== !!column2.autoincrement) {
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 });
318
369
  changedProperties.add('autoincrement');
319
370
  }
320
- if (column1.unsigned !== column2.unsigned && this.platform.supportsUnsigned()) {
371
+ if (fromColumn.unsigned !== toColumn.unsigned && this.platform.supportsUnsigned()) {
372
+ log(`'unsigned' changed for column ${tableName}.${fromColumn.name}`, { fromColumn, toColumn });
321
373
  changedProperties.add('unsigned');
322
374
  }
323
- if (!this.hasSameDefaultValue(column1, column2)) {
375
+ if (!this.hasSameDefaultValue(fromColumn, toColumn)) {
376
+ log(`'default' changed for column ${tableName}.${fromColumn.name}`, { fromColumn, toColumn });
324
377
  changedProperties.add('default');
325
378
  }
326
- if (this.diffComment(column1.comment, column2.comment)) {
379
+ if (this.diffComment(fromColumn.comment, toColumn.comment)) {
380
+ log(`'comment' changed for column ${tableName}.${fromColumn.name}`, { fromColumn, toColumn });
327
381
  changedProperties.add('comment');
328
382
  }
329
- if (this.diffEnumItems(column1.enumItems, column2.enumItems)) {
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 });
330
387
  changedProperties.add('enumItems');
331
388
  }
332
- if ((column1.extra || '').toLowerCase() !== (column2.extra || '').toLowerCase()) {
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 });
333
393
  changedProperties.add('extra');
334
394
  }
335
395
  return changedProperties;
@@ -347,8 +407,8 @@ class SchemaComparator {
347
407
  * Compares index1 with index2 and returns index2 if there are any differences or false in case there are no differences.
348
408
  */
349
409
  diffIndex(index1, index2) {
350
- // if one of them is a custom expression, compare only by name
351
- if (index1.expression || index2.expression) {
410
+ // if one of them is a custom expression or full text index, compare only by name
411
+ if (index1.expression || index2.expression || index1.type === 'fulltext' || index2.type === 'fulltext') {
352
412
  return index1.keyName !== index2.keyName;
353
413
  }
354
414
  return !this.isIndexFulfilledBy(index1, index2) || !this.isIndexFulfilledBy(index2, index1);
@@ -383,8 +443,11 @@ class SchemaComparator {
383
443
  }
384
444
  return index1.primary === index2.primary && index1.unique === index2.unique;
385
445
  }
386
- diffCheck(check1, check2) {
387
- return check1.expression !== check2.expression;
446
+ diffExpression(expr1, expr2) {
447
+ // expressions like check constraints might be normalized by the driver,
448
+ // e.g. quotes might be added (https://github.com/mikro-orm/mikro-orm/issues/3827)
449
+ const simplify = (str) => str?.replace(/_\w+\\'(.*?)\\'/g, '$1').replace(/['"`()]|::\w+| +/g, '').toLowerCase();
450
+ return simplify(expr1) !== simplify(expr2);
388
451
  }
389
452
  hasSameDefaultValue(from, to) {
390
453
  if (from.default == null || from.default.toString().toLowerCase() === 'null' || from.default.toString().startsWith('nextval(')) {
@@ -395,6 +458,11 @@ class SchemaComparator {
395
458
  const defaultValueTo = !['0', 'false', 'f', 'n', 'no', 'off'].includes('' + to.default);
396
459
  return defaultValueFrom === defaultValueTo;
397
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
+ }
398
466
  if (to.mappedType instanceof core_1.DateTimeType && from.default && to.default) {
399
467
  // normalize now/current_timestamp defaults, also remove `()` from the end of default expression
400
468
  const defaultValueFrom = from.default.toLowerCase().replace('current_timestamp', 'now').replace(/\(\)$/, '');
@@ -423,5 +491,11 @@ class SchemaComparator {
423
491
  scale: match ? +match[2] : column.scale,
424
492
  };
425
493
  }
494
+ log(message, params) {
495
+ if (params) {
496
+ message += ' ' + (0, util_1.inspect)(params);
497
+ }
498
+ this.logger.log('schema', message);
499
+ }
426
500
  }
427
501
  exports.SchemaComparator = SchemaComparator;
@@ -1,9 +1,10 @@
1
1
  import type { Knex } from 'knex';
2
- import type { Connection, Dictionary } from '@mikro-orm/core';
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
- import type { Check, Column, Index, TableDifference } from '../typings';
5
+ import type { CheckDef, Column, IndexDef, Table, TableDifference } from '../typings';
6
6
  import type { DatabaseTable } from './DatabaseTable';
7
+ import type { DatabaseSchema } from './DatabaseSchema';
7
8
  export declare abstract class SchemaHelper {
8
9
  protected readonly platform: AbstractSqlPlatform;
9
10
  constructor(platform: AbstractSqlPlatform);
@@ -13,24 +14,30 @@ export declare abstract class SchemaHelper {
13
14
  getSchemaEnd(): string;
14
15
  finalizeTable(table: Knex.TableBuilder, charset: string, collate?: string): void;
15
16
  supportsSchemaConstraints(): boolean;
16
- getPrimaryKeys(connection: AbstractSqlConnection, indexes: Index[], tableName: string, schemaName?: string): Promise<string[]>;
17
+ getPrimaryKeys(connection: AbstractSqlConnection, indexes: IndexDef[] | undefined, tableName: string, schemaName?: string): Promise<string[]>;
17
18
  getForeignKeys(connection: AbstractSqlConnection, tableName: string, schemaName?: string): Promise<Dictionary>;
18
- getEnumDefinitions(connection: AbstractSqlConnection, checks: Check[], tableName: string, schemaName?: string): Promise<Dictionary<string[]>>;
19
+ protected getTableKey(t: Table): string;
20
+ getEnumDefinitions(connection: AbstractSqlConnection, checks: CheckDef[], tableName: string, schemaName?: string): Promise<Dictionary<string[]>>;
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>;
19
24
  getListTablesSQL(schemaName?: string): string;
20
- getRenameColumnSQL(tableName: string, oldColumnName: string, to: Column): string;
21
- getCreateIndexSQL(tableName: string, index: Index): string;
22
- getDropIndexSQL(tableName: string, index: Index): string;
23
- getRenameIndexSQL(tableName: string, index: Index, oldIndexName: string): string;
25
+ getRenameColumnSQL(tableName: string, oldColumnName: string, to: Column, schemaName?: string): string;
26
+ getCreateIndexSQL(tableName: string, index: IndexDef, partialExpression?: boolean): string;
27
+ getDropIndexSQL(tableName: string, index: IndexDef): string;
28
+ getRenameIndexSQL(tableName: string, index: IndexDef, oldIndexName: string): string;
29
+ hasNonDefaultPrimaryKeyName(table: DatabaseTable): boolean;
24
30
  createTableColumn(table: Knex.TableBuilder, column: Column, fromTable: DatabaseTable, changedProperties?: Set<string>): Knex.ColumnBuilder;
25
31
  configureColumn(column: Column, col: Knex.ColumnBuilder, knex: Knex, changedProperties?: Set<string>): Knex.ColumnBuilder;
26
32
  configureColumnDefault(column: Column, col: Knex.ColumnBuilder, knex: Knex, changedProperties?: Set<string>): Knex.ColumnBuilder;
27
33
  getPreAlterTable(tableDiff: TableDifference, safe: boolean): string;
28
- getAlterColumnAutoincrement(tableName: string, column: Column): string;
29
- getChangeColumnCommentSQL(tableName: string, to: Column): string;
34
+ getAlterColumnAutoincrement(tableName: string, column: Column, schemaName?: string): string;
35
+ getChangeColumnCommentSQL(tableName: string, to: Column, schemaName?: string): string;
36
+ getNamespaces(connection: AbstractSqlConnection): Promise<string[]>;
30
37
  getColumns(connection: AbstractSqlConnection, tableName: string, schemaName?: string): Promise<Column[]>;
31
- getIndexes(connection: AbstractSqlConnection, tableName: string, schemaName?: string): Promise<Index[]>;
32
- getChecks(connection: AbstractSqlConnection, tableName: string, schemaName?: string): Promise<Check[]>;
33
- protected mapIndexes(indexes: Index[]): Promise<Index[]>;
38
+ getIndexes(connection: AbstractSqlConnection, tableName: string, schemaName?: string): Promise<IndexDef[]>;
39
+ getChecks(connection: AbstractSqlConnection, tableName: string, schemaName?: string, columns?: Column[]): Promise<CheckDef[]>;
40
+ protected mapIndexes(indexes: IndexDef[]): Promise<IndexDef[]>;
34
41
  getForeignKeysSQL(tableName: string, schemaName?: string): string;
35
42
  mapForeignKeys(fks: any[], tableName: string, schemaName?: string): Dictionary;
36
43
  normalizeDefaultValue(defaultValue: string, length?: number, defaultValues?: Dictionary<string[]>): string | number;
@@ -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
  }
@@ -24,34 +25,69 @@ class SchemaHelper {
24
25
  supportsSchemaConstraints() {
25
26
  return true;
26
27
  }
27
- async getPrimaryKeys(connection, indexes, tableName, schemaName) {
28
- const pk = indexes.find(i => i.primary);
29
- return pk ? pk.columnNames : [];
28
+ async getPrimaryKeys(connection, indexes = [], tableName, schemaName) {
29
+ const pks = indexes.filter(i => i.primary).map(pk => pk.columnNames);
30
+ return core_1.Utils.flatten(pks);
30
31
  }
31
32
  async getForeignKeys(connection, tableName, schemaName) {
32
33
  const fks = await connection.execute(this.getForeignKeysSQL(tableName, schemaName));
33
34
  return this.mapForeignKeys(fks, tableName, schemaName);
34
35
  }
36
+ getTableKey(t) {
37
+ const unquote = (str) => str.replace(/['"`]/g, '');
38
+ const parts = t.table_name.split('.');
39
+ if (parts.length > 1) {
40
+ return `${unquote(parts[0])}.${unquote(parts[1])}`;
41
+ }
42
+ if (t.schema_name) {
43
+ return `${unquote(t.schema_name)}.${unquote(t.table_name)}`;
44
+ }
45
+ return unquote(t.table_name);
46
+ }
35
47
  async getEnumDefinitions(connection, checks, tableName, schemaName) {
36
48
  return {};
37
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) {
57
+ for (const t of tables) {
58
+ const table = schema.addTable(t.table_name, t.schema_name);
59
+ table.comment = t.table_comment;
60
+ const cols = await this.getColumns(connection, table.name, table.schema);
61
+ const indexes = await this.getIndexes(connection, table.name, table.schema);
62
+ const checks = await this.getChecks(connection, table.name, table.schema, cols);
63
+ const pks = await this.getPrimaryKeys(connection, indexes, table.name, table.schema);
64
+ const fks = await this.getForeignKeys(connection, table.name, table.schema);
65
+ const enums = await this.getEnumDefinitions(connection, checks, table.name, table.schema);
66
+ table.init(cols, indexes, checks, pks, fks, enums);
67
+ }
68
+ }
38
69
  getListTablesSQL(schemaName) {
39
70
  throw new Error('Not supported by given driver');
40
71
  }
41
- getRenameColumnSQL(tableName, oldColumnName, to) {
72
+ getRenameColumnSQL(tableName, oldColumnName, to, schemaName) {
42
73
  tableName = this.platform.quoteIdentifier(tableName);
43
74
  oldColumnName = this.platform.quoteIdentifier(oldColumnName);
44
75
  const columnName = this.platform.quoteIdentifier(to.name);
45
- return `alter table ${tableName} rename column ${oldColumnName} to ${columnName}`;
76
+ const schemaReference = (schemaName !== undefined && schemaName !== 'public') ? ('"' + schemaName + '".') : '';
77
+ const tableReference = schemaReference + tableName;
78
+ return `alter table ${tableReference} rename column ${oldColumnName} to ${columnName}`;
46
79
  }
47
- getCreateIndexSQL(tableName, index) {
48
- /* istanbul ignore if */
49
- if (index.expression) {
80
+ getCreateIndexSQL(tableName, index, partialExpression = false) {
81
+ if (index.expression && !partialExpression) {
50
82
  return index.expression;
51
83
  }
52
84
  tableName = this.platform.quoteIdentifier(tableName);
53
85
  const keyName = this.platform.quoteIdentifier(index.keyName);
54
- return `create index ${keyName} on ${tableName} (${index.columnNames.map(c => this.platform.quoteIdentifier(c)).join(', ')})`;
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(', ')})`;
55
91
  }
56
92
  getDropIndexSQL(tableName, index) {
57
93
  return `drop index ${this.platform.quoteIdentifier(index.keyName)}`;
@@ -59,23 +95,36 @@ class SchemaHelper {
59
95
  getRenameIndexSQL(tableName, index, oldIndexName) {
60
96
  return [this.getDropIndexSQL(tableName, { ...index, keyName: oldIndexName }), this.getCreateIndexSQL(tableName, index)].join(';\n');
61
97
  }
98
+ hasNonDefaultPrimaryKeyName(table) {
99
+ const pkIndex = table.getPrimaryKey();
100
+ if (!pkIndex || !this.platform.supportsCustomPrimaryKeyNames()) {
101
+ return false;
102
+ }
103
+ const defaultName = this.platform.getDefaultPrimaryName(table.name, pkIndex.columnNames);
104
+ return pkIndex?.keyName !== defaultName;
105
+ }
62
106
  createTableColumn(table, column, fromTable, changedProperties) {
63
107
  const compositePK = fromTable.getPrimaryKey()?.composite;
64
- 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'))) {
109
+ const primaryKey = !changedProperties && !this.hasNonDefaultPrimaryKeyName(fromTable);
65
110
  if (column.mappedType instanceof core_1.BigIntType) {
66
- return table.bigIncrements(column.name, { primaryKey: !changedProperties });
111
+ return table.bigIncrements(column.name, { primaryKey });
67
112
  }
68
- return table.increments(column.name, { primaryKey: !changedProperties });
113
+ return table.increments(column.name, { primaryKey });
69
114
  }
70
115
  if (column.mappedType instanceof core_1.EnumType && column.enumItems?.every(item => core_1.Utils.isString(item))) {
71
116
  return table.enum(column.name, column.enumItems);
72
117
  }
73
- return table.specificType(column.name, column.type);
118
+ let columnType = column.type;
119
+ if (column.generated) {
120
+ columnType += ` generated always as ${column.generated}`;
121
+ }
122
+ return table.specificType(column.name, columnType);
74
123
  }
75
124
  configureColumn(column, col, knex, changedProperties) {
76
125
  const guard = (key) => !changedProperties || changedProperties.has(key);
77
126
  core_1.Utils.runIfNotEmpty(() => col.nullable(), column.nullable && guard('nullable'));
78
- core_1.Utils.runIfNotEmpty(() => col.notNullable(), !column.nullable);
127
+ core_1.Utils.runIfNotEmpty(() => col.notNullable(), !column.nullable && !column.generated);
79
128
  core_1.Utils.runIfNotEmpty(() => col.unsigned(), column.unsigned);
80
129
  core_1.Utils.runIfNotEmpty(() => col.comment(column.comment), column.comment);
81
130
  this.configureColumnDefault(column, col, knex, changedProperties);
@@ -87,26 +136,29 @@ class SchemaHelper {
87
136
  core_1.Utils.runIfNotEmpty(() => col.defaultTo(column.default == null ? null : knex.raw(column.default)), guard('default'));
88
137
  }
89
138
  else {
90
- core_1.Utils.runIfNotEmpty(() => col.defaultTo(column.default == null ? null : knex.raw(column.default)), column.default !== undefined);
139
+ core_1.Utils.runIfNotEmpty(() => col.defaultTo(knex.raw(column.default)), column.default != null && column.default !== 'null');
91
140
  }
92
141
  return col;
93
142
  }
94
143
  getPreAlterTable(tableDiff, safe) {
95
144
  return '';
96
145
  }
97
- getAlterColumnAutoincrement(tableName, column) {
146
+ getAlterColumnAutoincrement(tableName, column, schemaName) {
98
147
  return '';
99
148
  }
100
- getChangeColumnCommentSQL(tableName, to) {
149
+ getChangeColumnCommentSQL(tableName, to, schemaName) {
101
150
  return '';
102
151
  }
152
+ async getNamespaces(connection) {
153
+ return [];
154
+ }
103
155
  async getColumns(connection, tableName, schemaName) {
104
156
  throw new Error('Not supported by given driver');
105
157
  }
106
158
  async getIndexes(connection, tableName, schemaName) {
107
159
  throw new Error('Not supported by given driver');
108
160
  }
109
- async getChecks(connection, tableName, schemaName) {
161
+ async getChecks(connection, tableName, schemaName, columns) {
110
162
  throw new Error('Not supported by given driver');
111
163
  }
112
164
  async mapIndexes(indexes) {