@mikro-orm/knex 6.0.0-dev.29 → 6.0.0-dev.291

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 (44) hide show
  1. package/AbstractSqlConnection.d.ts +22 -4
  2. package/AbstractSqlConnection.js +48 -18
  3. package/AbstractSqlDriver.d.ts +30 -24
  4. package/AbstractSqlDriver.js +596 -274
  5. package/AbstractSqlPlatform.d.ts +9 -4
  6. package/AbstractSqlPlatform.js +40 -5
  7. package/MonkeyPatchable.d.ts +1 -0
  8. package/MonkeyPatchable.js +3 -0
  9. package/PivotCollectionPersister.d.ts +17 -0
  10. package/PivotCollectionPersister.js +128 -0
  11. package/README.md +64 -107
  12. package/SqlEntityManager.d.ts +4 -8
  13. package/SqlEntityManager.js +8 -12
  14. package/SqlEntityRepository.d.ts +7 -6
  15. package/SqlEntityRepository.js +11 -8
  16. package/index.mjs +22 -3
  17. package/package.json +8 -8
  18. package/query/ArrayCriteriaNode.d.ts +3 -2
  19. package/query/ArrayCriteriaNode.js +7 -2
  20. package/query/CriteriaNode.d.ts +6 -4
  21. package/query/CriteriaNode.js +22 -9
  22. package/query/CriteriaNodeFactory.d.ts +1 -1
  23. package/query/CriteriaNodeFactory.js +27 -11
  24. package/query/ObjectCriteriaNode.d.ts +3 -2
  25. package/query/ObjectCriteriaNode.js +72 -28
  26. package/query/QueryBuilder.d.ts +58 -26
  27. package/query/QueryBuilder.js +438 -142
  28. package/query/QueryBuilderHelper.d.ts +13 -18
  29. package/query/QueryBuilderHelper.js +190 -163
  30. package/query/ScalarCriteriaNode.d.ts +3 -2
  31. package/query/ScalarCriteriaNode.js +14 -4
  32. package/query/enums.d.ts +7 -0
  33. package/query/enums.js +10 -2
  34. package/schema/DatabaseSchema.d.ts +5 -2
  35. package/schema/DatabaseSchema.js +26 -8
  36. package/schema/DatabaseTable.d.ts +12 -4
  37. package/schema/DatabaseTable.js +369 -44
  38. package/schema/SchemaComparator.d.ts +7 -5
  39. package/schema/SchemaComparator.js +68 -35
  40. package/schema/SchemaHelper.d.ts +7 -5
  41. package/schema/SchemaHelper.js +25 -11
  42. package/schema/SqlSchemaGenerator.d.ts +3 -4
  43. package/schema/SqlSchemaGenerator.js +71 -28
  44. package/typings.d.ts +27 -8
@@ -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.diffCheck(check, toTableCheck)) {
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(column1, column2, tableName) {
340
+ diffColumn(fromColumn, toColumn, tableName) {
328
341
  const changedProperties = new Set();
329
- const prop1 = this.mapColumnToProperty({ ...column1, autoincrement: false });
330
- const prop2 = this.mapColumnToProperty({ ...column2, autoincrement: false });
331
- const columnType1 = column1.mappedType.getColumnType(prop1, this.platform).toLowerCase();
332
- const columnType2 = column2.mappedType.getColumnType(prop2, this.platform).toLowerCase();
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 (columnType1 !== columnType2 &&
341
- !(column1.ignoreSchemaChanges?.includes('type') ||
342
- column2.ignoreSchemaChanges?.includes('type'))) {
343
- log(`'type' changed for column ${tableName}.${column1.name}`, { columnType1, columnType2 });
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 (column1.nullable !== column2.nullable) {
347
- log(`'nullable' changed for column ${tableName}.${column1.name}`, { column1, column2 });
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 (!!column1.autoincrement !== !!column2.autoincrement) {
351
- log(`'autoincrement' changed for column ${tableName}.${column1.name}`, { column1, column2 });
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 (column1.unsigned !== column2.unsigned && this.platform.supportsUnsigned()) {
355
- log(`'unsigned' changed for column ${tableName}.${column1.name}`, { column1, column2 });
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(column1, column2)) {
359
- log(`'default' changed for column ${tableName}.${column1.name}`, { column1, column2 });
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(column1.comment, column2.comment)) {
363
- log(`'comment' changed for column ${tableName}.${column1.name}`, { column1, column2 });
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 (this.diffEnumItems(column1.enumItems, column2.enumItems)) {
367
- log(`'enumItems' changed for column ${tableName}.${column1.name}`, { column1, column2 });
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 ((column1.extra || '').toLowerCase() !== (column2.extra || '').toLowerCase() &&
371
- !(column1.ignoreSchemaChanges?.includes('extra') ||
372
- column2.ignoreSchemaChanges?.includes('extra'))) {
373
- log(`'extra' changed for column ${tableName}.${column1.name}`, { column1, column2 });
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
- diffCheck(check1, check2) {
428
- // check constraint definition might be normalized by the driver,
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(/['"`()]/g, '').toLowerCase();
431
- return simplify(check1.expression) !== simplify(check2.expression);
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(/\(\)$/, '');
@@ -1,5 +1,5 @@
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
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
- loadInformationSchema(schema: DatabaseSchema, connection: AbstractSqlConnection, tables: Table[]): Promise<void>;
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[]>;
@@ -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
- async loadInformationSchema(schema, connection, tables) {
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
- /* istanbul ignore if */
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
- 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(', ')})`;
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
- 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);
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(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');
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 { MikroORM, ISchemaGenerator } from '@mikro-orm/core';
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: string): Promise<void>;
82
+ dropDatabase(name?: string): Promise<void>;
84
83
  execute(sql: string, options?: {
85
84
  wrap?: boolean;
86
- }): Promise<undefined>;
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
- constructor() {
9
- super(...arguments);
10
- this.helper = this.platform.getSchemaHelper();
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 schema = await DatabaseSchema_1.DatabaseSchema.create(this.connection, this.platform, this.config, options.schema);
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
- await this.ensureDatabase();
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 ?? (options.wrap = this.options.disableForeignKeys);
147
- options.safe ?? (options.safe = false);
148
- options.dropTables ?? (options.dropTables = true);
153
+ options.wrap ??= this.options.disableForeignKeys;
154
+ options.safe ??= false;
155
+ options.dropTables ??= true;
149
156
  const toSchema = this.getTargetSchema(options.schema);
150
- const fromSchema = options.fromSchema ?? await DatabaseSchema_1.DatabaseSchema.create(this.connection, this.platform, this.config, options.schema);
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 ?? (options.wrap = false);
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
- return void await this.driver.execute(query);
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 ?? (options.wrap = this.options.disableForeignKeys);
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
- table.unique(index.columnNames, { indexName: index.keyName });
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
- table.index(index.columnNames, index.keyName, index.type);
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 {