@mikro-orm/knex 6.1.13-dev.3 → 6.1.13-dev.30

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 (50) hide show
  1. package/AbstractSqlConnection.js +7 -1
  2. package/AbstractSqlDriver.d.ts +5 -5
  3. package/AbstractSqlDriver.js +18 -9
  4. package/MonkeyPatchable.d.ts +5 -0
  5. package/MonkeyPatchable.js +15 -0
  6. package/README.md +17 -11
  7. package/SqlEntityManager.d.ts +1 -1
  8. package/SqlEntityManager.js +1 -2
  9. package/dialects/index.d.ts +2 -0
  10. package/dialects/index.js +18 -0
  11. package/dialects/mssql/MsSqlColumnCompiler.d.ts +4 -0
  12. package/dialects/mssql/MsSqlColumnCompiler.js +10 -0
  13. package/dialects/mssql/MsSqlKnexDialect.d.ts +6 -0
  14. package/dialects/mssql/MsSqlKnexDialect.js +22 -0
  15. package/dialects/mssql/MsSqlQueryCompiler.d.ts +12 -0
  16. package/dialects/mssql/MsSqlQueryCompiler.js +94 -0
  17. package/dialects/mssql/MsSqlTableCompiler.d.ts +9 -0
  18. package/dialects/mssql/MsSqlTableCompiler.js +40 -0
  19. package/dialects/mssql/index.d.ts +1 -0
  20. package/dialects/mssql/index.js +17 -0
  21. package/dialects/sqlite/BaseSqliteConnection.d.ts +10 -0
  22. package/dialects/sqlite/BaseSqliteConnection.js +56 -0
  23. package/dialects/sqlite/BaseSqlitePlatform.d.ts +49 -0
  24. package/dialects/sqlite/BaseSqlitePlatform.js +78 -0
  25. package/dialects/sqlite/BaseSqliteSchemaHelper.d.ts +27 -0
  26. package/dialects/sqlite/BaseSqliteSchemaHelper.js +172 -0
  27. package/dialects/sqlite/BetterSqliteKnexDialect.d.ts +5 -0
  28. package/dialects/sqlite/BetterSqliteKnexDialect.js +15 -0
  29. package/dialects/sqlite/LibSqlKnexDialect.d.ts +6 -0
  30. package/dialects/sqlite/LibSqlKnexDialect.js +18 -0
  31. package/dialects/sqlite/SqliteKnexDialect.d.ts +8 -0
  32. package/dialects/sqlite/SqliteKnexDialect.js +67 -0
  33. package/dialects/sqlite/SqliteTableCompiler.d.ts +5 -0
  34. package/dialects/sqlite/SqliteTableCompiler.js +45 -0
  35. package/dialects/sqlite/index.d.ts +7 -0
  36. package/dialects/sqlite/index.js +23 -0
  37. package/index.d.ts +1 -0
  38. package/index.js +1 -0
  39. package/index.mjs +10 -0
  40. package/package.json +2 -2
  41. package/query/QueryBuilder.d.ts +57 -39
  42. package/query/QueryBuilder.js +64 -26
  43. package/query/QueryBuilderHelper.js +2 -1
  44. package/schema/DatabaseTable.js +20 -2
  45. package/schema/SchemaComparator.js +15 -2
  46. package/schema/SchemaHelper.d.ts +5 -1
  47. package/schema/SchemaHelper.js +20 -2
  48. package/schema/SqlSchemaGenerator.d.ts +12 -4
  49. package/schema/SqlSchemaGenerator.js +53 -28
  50. package/typings.d.ts +2 -1
@@ -1,7 +1,7 @@
1
1
  /// <reference types="node" />
2
2
  import { inspect } from 'util';
3
3
  import type { Knex } from 'knex';
4
- import { type AnyEntity, type ConnectionType, type Dictionary, type EntityData, type EntityName, type EntityProperty, type FlushMode, type GroupOperator, LockMode, type LoggingOptions, type MetadataStorage, type ObjectQuery, PopulateHint, type PopulateOptions, type QBFilterQuery, type QBQueryOrderMap, QueryFlag, type QueryResult, type RequiredEntityData } from '@mikro-orm/core';
4
+ import { type AnyEntity, type ConnectionType, type Dictionary, type EntityData, type EntityName, type EntityProperty, type FlushMode, type GroupOperator, LockMode, type LoggingOptions, type MetadataStorage, type ObjectQuery, PopulateHint, type PopulateOptions, type QBFilterQuery, type QBQueryOrderMap, QueryFlag, type QueryOrderMap, type QueryResult, type RequiredEntityData } from '@mikro-orm/core';
5
5
  import { JoinType, QueryType } from './enums';
6
6
  import type { AbstractSqlDriver } from '../AbstractSqlDriver';
7
7
  import { type Alias, QueryBuilderHelper } from './QueryBuilderHelper';
@@ -32,13 +32,12 @@ export interface ExecuteOptions {
32
32
  * ```
33
33
  */
34
34
  export declare class QueryBuilder<T extends object = AnyEntity> {
35
- #private;
36
- private readonly metadata;
37
- private readonly driver;
38
- private readonly context?;
39
- private connectionType?;
40
- private readonly em?;
41
- private readonly loggerContext?;
35
+ protected readonly metadata: MetadataStorage;
36
+ protected readonly driver: AbstractSqlDriver;
37
+ protected readonly context?: Knex.Transaction<any, any[]> | undefined;
38
+ protected connectionType?: ConnectionType | undefined;
39
+ protected em?: SqlEntityManager<AbstractSqlDriver<import("..").AbstractSqlConnection, AbstractSqlPlatform>> | undefined;
40
+ protected loggerContext?: (LoggingOptions & Dictionary) | undefined;
42
41
  get mainAlias(): Alias<T>;
43
42
  get alias(): string;
44
43
  get helper(): QueryBuilderHelper;
@@ -56,40 +55,51 @@ export declare class QueryBuilder<T extends object = AnyEntity> {
56
55
  _populateMap: Dictionary<string>;
57
56
  /** @internal */
58
57
  readonly rawFragments: Set<string>;
59
- private aliasCounter;
60
- private flags;
61
- private finalized;
62
- private _joins;
63
- private _explicitAlias;
64
- private _schema?;
65
- private _cond;
66
- private _data;
67
- private _orderBy;
68
- private _groupBy;
69
- private _having;
70
- private _returning?;
71
- private _onConflict?;
72
- private _limit?;
73
- private _offset?;
74
- private _distinctOn?;
75
- private _joinedProps;
76
- private _cache?;
77
- private _indexHint?;
78
- private _comments;
79
- private _hintComments;
80
- private flushMode?;
81
- private lockMode?;
82
- private lockTables?;
83
- private subQueries;
84
- private _mainAlias?;
85
- private _aliases;
86
- private _helper?;
87
- private readonly platform;
88
- private readonly knex;
58
+ protected aliasCounter: number;
59
+ protected flags: Set<QueryFlag>;
60
+ protected finalized: boolean;
61
+ protected _joins: Dictionary<JoinOptions>;
62
+ protected _explicitAlias: boolean;
63
+ protected _schema?: string;
64
+ protected _cond: Dictionary;
65
+ protected _data: Dictionary;
66
+ protected _orderBy: QueryOrderMap<T>[];
67
+ protected _groupBy: Field<T>[];
68
+ protected _having: Dictionary;
69
+ protected _returning?: Field<T>[];
70
+ protected _onConflict?: {
71
+ fields: string[];
72
+ ignore?: boolean;
73
+ merge?: EntityData<T> | Field<T>[];
74
+ where?: QBFilterQuery<T>;
75
+ }[];
76
+ protected _limit?: number;
77
+ protected _offset?: number;
78
+ protected _distinctOn?: string[];
79
+ protected _joinedProps: Map<string, PopulateOptions<any>>;
80
+ protected _cache?: boolean | number | [string, number];
81
+ protected _indexHint?: string;
82
+ protected _comments: string[];
83
+ protected _hintComments: string[];
84
+ protected flushMode?: FlushMode;
85
+ protected lockMode?: LockMode;
86
+ protected lockTables?: string[];
87
+ protected subQueries: Dictionary<string>;
88
+ protected _mainAlias?: Alias<T>;
89
+ protected _aliases: Dictionary<Alias<any>>;
90
+ protected _helper?: QueryBuilderHelper;
91
+ protected _query?: {
92
+ sql?: string;
93
+ _sql?: Knex.Sql;
94
+ params?: readonly unknown[];
95
+ qb: Knex.QueryBuilder<T>;
96
+ };
97
+ protected readonly platform: AbstractSqlPlatform;
98
+ protected readonly knex: Knex;
89
99
  /**
90
100
  * @internal
91
101
  */
92
- constructor(entityName: EntityName<T> | QueryBuilder<T>, metadata: MetadataStorage, driver: AbstractSqlDriver, context?: Knex.Transaction<any, any[]> | undefined, alias?: string, connectionType?: ConnectionType | undefined, em?: SqlEntityManager<AbstractSqlDriver<import("..").AbstractSqlConnection, AbstractSqlPlatform>> | undefined, loggerContext?: LoggingOptions | undefined);
102
+ constructor(entityName: EntityName<T> | QueryBuilder<T>, metadata: MetadataStorage, driver: AbstractSqlDriver, context?: Knex.Transaction<any, any[]> | undefined, alias?: string, connectionType?: ConnectionType | undefined, em?: SqlEntityManager<AbstractSqlDriver<import("..").AbstractSqlConnection, AbstractSqlPlatform>> | undefined, loggerContext?: (LoggingOptions & Dictionary) | undefined);
93
103
  select(fields: Field<T> | Field<T>[], distinct?: boolean): SelectQueryBuilder<T>;
94
104
  addSelect(fields: Field<T> | Field<T>[]): SelectQueryBuilder<T>;
95
105
  distinct(): SelectQueryBuilder<T>;
@@ -232,6 +242,14 @@ export declare class QueryBuilder<T extends object = AnyEntity> {
232
242
  as(alias: string): Knex.QueryBuilder;
233
243
  clone(reset?: boolean | string[]): QueryBuilder<T>;
234
244
  getKnex(processVirtualEntity?: boolean): Knex.QueryBuilder;
245
+ /**
246
+ * Sets logger context for this query builder.
247
+ */
248
+ setLoggerContext(context: LoggingOptions & Dictionary): void;
249
+ /**
250
+ * Gets logger context for this query builder.
251
+ */
252
+ getLoggerContext<T extends Dictionary & LoggingOptions = Dictionary>(): T;
235
253
  private fromVirtual;
236
254
  private joinReference;
237
255
  private prepareFields;
@@ -85,6 +85,7 @@ class QueryBuilder {
85
85
  _mainAlias;
86
86
  _aliases = {};
87
87
  _helper;
88
+ _query;
88
89
  platform;
89
90
  knex;
90
91
  /**
@@ -459,10 +460,10 @@ class QueryBuilder {
459
460
  return this;
460
461
  }
461
462
  getKnexQuery(processVirtualEntity = true) {
462
- if (this.#query) {
463
- return this.#query.qb;
463
+ if (this._query?.qb) {
464
+ return this._query.qb;
464
465
  }
465
- this.#query = {};
466
+ this._query = {};
466
467
  this.finalize();
467
468
  const qb = this.getQueryBase(processVirtualEntity);
468
469
  const type = this.type ?? enums_1.QueryType.SELECT;
@@ -484,14 +485,14 @@ class QueryBuilder {
484
485
  core_1.Utils.runIfNotEmpty(() => this._hintComments.forEach(comment => qb.hintComment(comment)), this._hintComments);
485
486
  core_1.Utils.runIfNotEmpty(() => this.helper.appendOnConflictClause(type, this._onConflict, qb), this._onConflict);
486
487
  if (this.type === enums_1.QueryType.TRUNCATE && this.platform.usesCascadeStatement()) {
487
- return this.#query.qb = this.knex.raw(qb.toSQL().toNative().sql + ' cascade');
488
+ return this._query.qb = this.knex.raw(qb.toSQL().toNative().sql + ' cascade');
488
489
  }
489
490
  if (this.lockMode) {
490
491
  this.helper.getLockSQL(qb, this.lockMode, this.lockTables);
491
492
  }
492
493
  this.helper.finalize(type, qb, this.mainAlias.metadata, this._data, this._returning);
493
494
  this.clearRawFragmentsCache();
494
- return this.#query.qb = qb;
495
+ return this._query.qb = qb;
495
496
  }
496
497
  /**
497
498
  * @internal
@@ -506,17 +507,16 @@ class QueryBuilder {
506
507
  getQuery() {
507
508
  return this.toQuery().sql;
508
509
  }
509
- #query;
510
510
  toQuery() {
511
- if (this.#query?.sql) {
512
- return { sql: this.#query.sql, _sql: this.#query._sql, params: this.#query.params };
511
+ if (this._query?.sql) {
512
+ return { sql: this._query.sql, _sql: this._query._sql, params: this._query.params };
513
513
  }
514
514
  const sql = this.getKnexQuery().toSQL();
515
515
  const query = sql.toNative();
516
- this.#query.sql = query.sql;
517
- this.#query._sql = sql;
518
- this.#query.params = query.bindings ?? [];
519
- return { sql: this.#query.sql, _sql: this.#query._sql, params: this.#query.params };
516
+ this._query.sql = query.sql;
517
+ this._query._sql = sql;
518
+ this._query.params = query.bindings ?? [];
519
+ return { sql: this._query.sql, _sql: this._query._sql, params: this._query.params };
520
520
  }
521
521
  /**
522
522
  * Returns the list of all parameters for this query.
@@ -598,17 +598,18 @@ class QueryBuilder {
598
598
  if (!this.connectionType && method !== 'run' && [enums_1.QueryType.INSERT, enums_1.QueryType.UPDATE, enums_1.QueryType.DELETE, enums_1.QueryType.TRUNCATE].includes(this.type ?? enums_1.QueryType.SELECT)) {
599
599
  this.connectionType = 'write';
600
600
  }
601
+ if (!this.finalized && method === 'get' && this.type === enums_1.QueryType.SELECT) {
602
+ this.limit(1);
603
+ }
601
604
  const query = this.toQuery()._sql;
602
605
  const cached = await this.em?.tryCache(this.mainAlias.entityName, this._cache, ['qb.execute', query.sql, query.bindings, method]);
603
606
  if (cached?.data) {
604
607
  return cached.data;
605
608
  }
606
- if (!this.finalized && method === 'get') {
607
- this.limit(1);
608
- }
609
609
  const write = method === 'run' || !this.platform.getConfig().get('preferReadReplicas');
610
610
  const type = this.connectionType || (write ? 'write' : 'read');
611
- const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context, this.loggerContext);
611
+ const loggerContext = { id: this.em?.id, ...this.loggerContext };
612
+ const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context, loggerContext);
612
613
  const meta = this.mainAlias.metadata;
613
614
  if (!options.mapResults || !meta) {
614
615
  await this.em?.storeCache(this._cache, cached, res);
@@ -652,8 +653,8 @@ class QueryBuilder {
652
653
  (0, core_1.helper)(entity).__serializationContext.populate ??= hint;
653
654
  hint.forEach(hint => {
654
655
  const [propName] = hint.field.split(':', 2);
655
- const value = entity[propName];
656
- if (core_1.Utils.isEntity(value, true)) {
656
+ const value = core_1.Reference.unwrapReference(entity[propName]);
657
+ if (core_1.Utils.isEntity(value)) {
657
658
  (0, core_1.helper)(value).populated();
658
659
  propagatePopulateHint(value, hint.children ?? []);
659
660
  }
@@ -794,6 +795,19 @@ class QueryBuilder {
794
795
  }
795
796
  return qb;
796
797
  }
798
+ /**
799
+ * Sets logger context for this query builder.
800
+ */
801
+ setLoggerContext(context) {
802
+ this.loggerContext = context;
803
+ }
804
+ /**
805
+ * Gets logger context for this query builder.
806
+ */
807
+ getLoggerContext() {
808
+ this.loggerContext ??= {};
809
+ return this.loggerContext;
810
+ }
797
811
  fromVirtual(meta) {
798
812
  if (typeof meta.expression === 'string') {
799
813
  return `(${meta.expression}) as ${this.platform.quoteIdentifier(this.alias)}`;
@@ -954,15 +968,19 @@ class QueryBuilder {
954
968
  /* istanbul ignore next */
955
969
  const requiresSQLConversion = meta?.props.filter(p => p.hasConvertToJSValueSQL && p.persist !== false) ?? [];
956
970
  if (this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES) && (fields.includes('*') || fields.includes(`${this.mainAlias.aliasName}.*`)) && requiresSQLConversion.length > 0) {
957
- requiresSQLConversion.forEach(p => ret.push(this.helper.mapper(p.name, this.type)));
971
+ for (const p of requiresSQLConversion) {
972
+ ret.push(this.helper.mapper(p.name, this.type));
973
+ }
958
974
  }
959
- Object.keys(this._populateMap).forEach(f => {
960
- if (!fields.includes(f.replace(/#\w+$/, '')) && type === 'where') {
975
+ for (const f of Object.keys(this._populateMap)) {
976
+ if (type === 'where' && this._joins[f]) {
961
977
  const cols = this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[f]);
962
- ret.push(...cols);
978
+ for (const col of cols) {
979
+ ret.push(col);
980
+ }
963
981
  }
964
- });
965
- return ret;
982
+ }
983
+ return core_1.Utils.unique(ret);
966
984
  }
967
985
  init(type, data, cond) {
968
986
  this.ensureNotFinalized();
@@ -1204,8 +1222,7 @@ class QueryBuilder {
1204
1222
  // not perfect, but should work most of the time, ideally we should check only the alias (`... as alias`)
1205
1223
  return field.sql.includes(prop);
1206
1224
  }
1207
- // not perfect, but should work most of the time, ideally we should check only the alias (`... as alias`)
1208
- return field.toString().includes(prop);
1225
+ return false;
1209
1226
  });
1210
1227
  if (field instanceof core_1.RawQueryFragment) {
1211
1228
  knexQuery.select(this.platform.formatQuery(field.sql, field.params));
@@ -1221,6 +1238,27 @@ class QueryBuilder {
1221
1238
  subSubQuery.__raw = true; // tag it as there is now way to check via `instanceof`
1222
1239
  this._limit = undefined;
1223
1240
  this._offset = undefined;
1241
+ // remove joins that are not used for population or ordering to improve performance
1242
+ const populate = new Set();
1243
+ const orderByAliases = this._orderBy
1244
+ .flatMap(hint => Object.keys(hint))
1245
+ .map(k => k.split('.')[0]);
1246
+ function addPath(hints, prefix = '') {
1247
+ for (const hint of hints) {
1248
+ const field = hint.field.split(':')[0];
1249
+ populate.add((prefix ? prefix + '.' : '') + field);
1250
+ if (hint.children) {
1251
+ addPath(hint.children, (prefix ? prefix + '.' : '') + field);
1252
+ }
1253
+ }
1254
+ }
1255
+ addPath(this._populate);
1256
+ for (const [key, join] of Object.entries(this._joins)) {
1257
+ const path = join.path?.replace(/\[populate]|\[pivot]|:ref/g, '').replace(new RegExp(`^${meta.className}.`), '');
1258
+ if (!populate.has(path ?? '') && !orderByAliases.includes(join.alias)) {
1259
+ delete this._joins[key];
1260
+ }
1261
+ }
1224
1262
  this.select(this._fields).where({ [core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: { $in: subSubQuery } });
1225
1263
  }
1226
1264
  wrapModifySubQuery(meta) {
@@ -518,7 +518,8 @@ class QueryBuilderHelper {
518
518
  return ret;
519
519
  }
520
520
  finalize(type, qb, meta, data, returning) {
521
- if (!meta || !data || !this.platform.usesReturningStatement()) {
521
+ const usesReturningStatement = this.platform.usesReturningStatement() || this.platform.usesOutputStatement();
522
+ if (!meta || !data || !usesReturningStatement) {
522
523
  return;
523
524
  }
524
525
  // always respect explicit returning hint
@@ -79,6 +79,9 @@ class DatabaseTable {
79
79
  prop.length ??= this.platform.getDefaultDateTimeLength();
80
80
  }
81
81
  }
82
+ if (prop.length == null && prop.columnTypes[idx]) {
83
+ prop.length = this.platform.getSchemaHelper().inferLengthFromColumnType(prop.columnTypes[idx]);
84
+ }
82
85
  const primary = !meta.compositePK && !!prop.primary && prop.kind === core_1.ReferenceKind.SCALAR && this.platform.isNumericColumn(mappedType);
83
86
  this.columns[field] = {
84
87
  name: prop.fieldNames[idx],
@@ -120,9 +123,22 @@ class DatabaseTable {
120
123
  if (prop.deleteRule || cascade || prop.nullable) {
121
124
  this.foreignKeys[constraintName].deleteRule = prop.deleteRule || (cascade ? 'cascade' : 'set null');
122
125
  }
123
- if (prop.updateRule || prop.cascade.includes(core_1.Cascade.PERSIST) || prop.cascade.includes(core_1.Cascade.ALL)) {
126
+ if (prop.updateRule) {
124
127
  this.foreignKeys[constraintName].updateRule = prop.updateRule || 'cascade';
125
128
  }
129
+ if ((prop.cascade.includes(core_1.Cascade.PERSIST) || prop.cascade.includes(core_1.Cascade.ALL))) {
130
+ const hasCascadePath = Object.values(this.foreignKeys).some(fk => {
131
+ return fk.constraintName !== constraintName
132
+ && ((fk.updateRule && fk.updateRule !== 'no action') || (fk.deleteRule && fk.deleteRule !== 'no action'))
133
+ && fk.referencedTableName === this.foreignKeys[constraintName].referencedTableName;
134
+ });
135
+ if (!hasCascadePath || this.platform.supportsMultipleCascadePaths()) {
136
+ this.foreignKeys[constraintName].updateRule ??= 'cascade';
137
+ }
138
+ }
139
+ if (prop.deferMode) {
140
+ this.foreignKeys[constraintName].deferMode = prop.deferMode;
141
+ }
126
142
  }
127
143
  if (prop.index) {
128
144
  this.indexes.push({
@@ -473,6 +489,7 @@ class DatabaseTable {
473
489
  fkOptions.referencedColumnNames = fk.referencedColumnNames;
474
490
  fkOptions.updateRule = fk.updateRule?.toLowerCase();
475
491
  fkOptions.deleteRule = fk.deleteRule?.toLowerCase();
492
+ fkOptions.deferMode = fk.deferMode;
476
493
  fkOptions.columnTypes = fk.columnNames.map(c => this.getColumn(c).type);
477
494
  const columnOptions = {};
478
495
  if (fk.columnNames.length === 1) {
@@ -520,6 +537,7 @@ class DatabaseTable {
520
537
  fkOptions.referencedColumnNames = fk.referencedColumnNames;
521
538
  fkOptions.updateRule = fk.updateRule?.toLowerCase();
522
539
  fkOptions.deleteRule = fk.deleteRule?.toLowerCase();
540
+ fkOptions.deferMode = fk.deferMode;
523
541
  }
524
542
  return {
525
543
  name: prop,
@@ -584,7 +602,7 @@ class DatabaseTable {
584
602
  // The enum name will be a concatenation of the table name and the column name.
585
603
  return namingStrategy.getClassName(this.name + '_' + column.name, '_');
586
604
  }
587
- return column.mappedType?.compareAsType() ?? 'unknown';
605
+ return column.mappedType?.runtimeType ?? 'unknown';
588
606
  }
589
607
  getPropertyDefaultValue(schemaHelper, column, propType, raw = false) {
590
608
  const empty = raw ? 'null' : undefined;
@@ -273,12 +273,19 @@ class SchemaComparator {
273
273
  */
274
274
  detectColumnRenamings(tableDifferences, inverseTableDiff) {
275
275
  const renameCandidates = {};
276
+ const oldFKs = Object.values(tableDifferences.fromTable.getForeignKeys());
277
+ const newFKs = Object.values(tableDifferences.toTable.getForeignKeys());
276
278
  for (const addedColumn of Object.values(tableDifferences.addedColumns)) {
277
279
  for (const removedColumn of Object.values(tableDifferences.removedColumns)) {
278
280
  const diff = this.diffColumn(addedColumn, removedColumn);
279
281
  if (diff.size !== 0) {
280
282
  continue;
281
283
  }
284
+ const wasFK = oldFKs.some(fk => fk.columnNames.includes(removedColumn.name));
285
+ const isFK = newFKs.some(fk => fk.columnNames.includes(addedColumn.name));
286
+ if (wasFK !== isFK) {
287
+ continue;
288
+ }
282
289
  const renamedColumn = inverseTableDiff?.renamedColumns[addedColumn.name];
283
290
  if (renamedColumn && renamedColumn?.name !== removedColumn.name) {
284
291
  continue;
@@ -348,6 +355,12 @@ class SchemaComparator {
348
355
  if (key1.referencedTableName !== key2.referencedTableName) {
349
356
  return true;
350
357
  }
358
+ if (key1.deferMode !== key2.deferMode) {
359
+ return true;
360
+ }
361
+ if (key1.localTableName === key1.referencedTableName && !this.platform.supportsMultipleCascadePaths()) {
362
+ return false;
363
+ }
351
364
  const defaultRule = ['restrict', 'no action'];
352
365
  const rule = (key, method) => {
353
366
  return (key[method] ?? defaultRule[0]).toLowerCase().replace(defaultRule[1], defaultRule[0]);
@@ -373,8 +386,8 @@ class SchemaComparator {
373
386
  }
374
387
  };
375
388
  if (fromColumnType !== toColumnType &&
376
- !(fromColumn.ignoreSchemaChanges?.includes('type') ||
377
- toColumn.ignoreSchemaChanges?.includes('type'))) {
389
+ !(fromColumn.ignoreSchemaChanges?.includes('type') || toColumn.ignoreSchemaChanges?.includes('type')) &&
390
+ !fromColumn.generated && !toColumn.generated) {
378
391
  log(`'type' changed for column ${tableName}.${fromColumn.name}`, { fromColumnType, toColumnType });
379
392
  changedProperties.add('type');
380
393
  }
@@ -15,6 +15,7 @@ export declare abstract class SchemaHelper {
15
15
  finalizeTable(table: Knex.TableBuilder, charset: string, collate?: string): void;
16
16
  supportsSchemaConstraints(): boolean;
17
17
  getPrimaryKeys(connection: AbstractSqlConnection, indexes: IndexDef[] | undefined, tableName: string, schemaName?: string): Promise<string[]>;
18
+ inferLengthFromColumnType(type: string): number | undefined;
18
19
  getForeignKeys(connection: AbstractSqlConnection, tableName: string, schemaName?: string): Promise<Dictionary>;
19
20
  protected getTableKey(t: Table): string;
20
21
  getEnumDefinitions(connection: AbstractSqlConnection, checks: CheckDef[], tableName: string, schemaName?: string): Promise<Dictionary<string[]>>;
@@ -27,8 +28,9 @@ export declare abstract class SchemaHelper {
27
28
  getCreateIndexSQL(tableName: string, index: IndexDef, partialExpression?: boolean): string;
28
29
  getDropIndexSQL(tableName: string, index: IndexDef): string;
29
30
  getRenameIndexSQL(tableName: string, index: IndexDef, oldIndexName: string): string;
31
+ getDropColumnsSQL(tableName: string, columns: Column[], schemaName?: string): string;
30
32
  hasNonDefaultPrimaryKeyName(table: DatabaseTable): boolean;
31
- createTableColumn(table: Knex.TableBuilder, column: Column, fromTable: DatabaseTable, changedProperties?: Set<string>, alter?: boolean): Knex.ColumnBuilder;
33
+ createTableColumn(table: Knex.TableBuilder, column: Column, fromTable: DatabaseTable, changedProperties?: Set<string>, alter?: boolean): Knex.ColumnBuilder | undefined;
32
34
  configureColumn(column: Column, col: Knex.ColumnBuilder, knex: Knex, changedProperties?: Set<string>): Knex.ColumnBuilder;
33
35
  configureColumnDefault(column: Column, col: Knex.ColumnBuilder, knex: Knex, changedProperties?: Set<string>): Knex.ColumnBuilder;
34
36
  getPreAlterTable(tableDiff: TableDifference, safe: boolean): string;
@@ -45,6 +47,8 @@ export declare abstract class SchemaHelper {
45
47
  normalizeDefaultValue(defaultValue: string, length?: number, defaultValues?: Dictionary<string[]>): string | number;
46
48
  getCreateDatabaseSQL(name: string): string;
47
49
  getDropDatabaseSQL(name: string): string;
50
+ getCreateNamespaceSQL(name: string): string;
51
+ getDropNamespaceSQL(name: string): string;
48
52
  getDatabaseExistsSQL(name: string): string;
49
53
  getDatabaseNotExistsError(dbName: string): string;
50
54
  getManagementDbName(): string;
@@ -29,6 +29,9 @@ class SchemaHelper {
29
29
  const pks = indexes.filter(i => i.primary).map(pk => pk.columnNames);
30
30
  return core_1.Utils.flatten(pks);
31
31
  }
32
+ inferLengthFromColumnType(type) {
33
+ return undefined;
34
+ }
32
35
  async getForeignKeys(connection, tableName, schemaName) {
33
36
  const fks = await connection.execute(this.getForeignKeysSQL(tableName, schemaName));
34
37
  return this.mapForeignKeys(fks, tableName, schemaName);
@@ -99,6 +102,11 @@ class SchemaHelper {
99
102
  getRenameIndexSQL(tableName, index, oldIndexName) {
100
103
  return [this.getDropIndexSQL(tableName, { ...index, keyName: oldIndexName }), this.getCreateIndexSQL(tableName, index)].join(';\n');
101
104
  }
105
+ getDropColumnsSQL(tableName, columns, schemaName) {
106
+ const name = this.platform.quoteIdentifier((schemaName && schemaName !== this.platform.getDefaultSchemaName() ? schemaName + '.' : '') + tableName);
107
+ const drops = columns.map(column => `drop column ${this.platform.quoteIdentifier(column.name)}`).join(', ');
108
+ return `alter table ${name} ${drops}`;
109
+ }
102
110
  hasNonDefaultPrimaryKeyName(table) {
103
111
  const pkIndex = table.getPrimaryKey();
104
112
  if (!pkIndex || !this.platform.supportsCustomPrimaryKeyNames()) {
@@ -199,6 +207,7 @@ class SchemaHelper {
199
207
  referencedColumnNames: [fk.referenced_column_name],
200
208
  updateRule: fk.update_rule.toLowerCase(),
201
209
  deleteRule: fk.delete_rule.toLowerCase(),
210
+ deferMode: fk.defer_mode,
202
211
  };
203
212
  }
204
213
  return ret;
@@ -220,10 +229,19 @@ class SchemaHelper {
220
229
  return norm[0].replace('(?)', length != null ? `(${length})` : '');
221
230
  }
222
231
  getCreateDatabaseSQL(name) {
223
- return `create database ${name}`;
232
+ // two line breaks to force separate execution
233
+ return `create database ${name};\n\nuse ${name}`;
224
234
  }
225
235
  getDropDatabaseSQL(name) {
226
- return `drop database if exists ${name}`;
236
+ return `drop database if exists ${this.platform.quoteIdentifier(name)}`;
237
+ }
238
+ /* istanbul ignore next */
239
+ getCreateNamespaceSQL(name) {
240
+ return `create schema if not exists ${this.platform.quoteIdentifier(name)}`;
241
+ }
242
+ /* istanbul ignore next */
243
+ getDropNamespaceSQL(name) {
244
+ return `drop schema if exists ${this.platform.quoteIdentifier(name)}`;
227
245
  }
228
246
  getDatabaseExistsSQL(name) {
229
247
  return `select 1 from information_schema.schemata where schema_name = '${name}'`;
@@ -1,10 +1,15 @@
1
- import { AbstractSchemaGenerator, type MikroORM, type ISchemaGenerator, type ClearDatabaseOptions, type CreateSchemaOptions, type EnsureDatabaseOptions, type DropSchemaOptions, type UpdateSchemaOptions } from '@mikro-orm/core';
1
+ import { AbstractSchemaGenerator, type ClearDatabaseOptions, type CreateSchemaOptions, type DropSchemaOptions, type EnsureDatabaseOptions, type ISchemaGenerator, type MikroORM, type Transaction, type UpdateSchemaOptions } from '@mikro-orm/core';
2
2
  import type { SchemaDifference } from '../typings';
3
3
  import { DatabaseSchema } from './DatabaseSchema';
4
4
  import type { AbstractSqlDriver } from '../AbstractSqlDriver';
5
5
  export declare class SqlSchemaGenerator extends AbstractSchemaGenerator<AbstractSqlDriver> implements ISchemaGenerator {
6
- private readonly helper;
7
- private readonly options;
6
+ protected readonly helper: import("./SchemaHelper").SchemaHelper;
7
+ protected readonly options: {
8
+ disableForeignKeys?: boolean | undefined;
9
+ createForeignKeyConstraints?: boolean | undefined;
10
+ ignoreSchema?: string[] | undefined;
11
+ managementDbName?: string | undefined;
12
+ };
8
13
  protected lastEnsuredDatabase?: string;
9
14
  static register(orm: MikroORM): void;
10
15
  createSchema(options?: CreateSchemaOptions): Promise<void>;
@@ -15,6 +20,8 @@ export declare class SqlSchemaGenerator extends AbstractSchemaGenerator<Abstract
15
20
  getTargetSchema(schema?: string): DatabaseSchema;
16
21
  getCreateSchemaSQL(options?: CreateSchemaOptions): Promise<string>;
17
22
  dropSchema(options?: DropSchemaOptions): Promise<void>;
23
+ createNamespace(name: string): Promise<void>;
24
+ dropNamespace(name: string): Promise<void>;
18
25
  clearDatabase(options?: ClearDatabaseOptions): Promise<void>;
19
26
  getDropSchemaSQL(options?: Omit<DropSchemaOptions, 'dropDb'>): Promise<string>;
20
27
  private getSchemaName;
@@ -43,10 +50,11 @@ export declare class SqlSchemaGenerator extends AbstractSchemaGenerator<Abstract
43
50
  /**
44
51
  * creates new database and connects to it
45
52
  */
46
- createDatabase(name: string): Promise<void>;
53
+ createDatabase(name?: string): Promise<void>;
47
54
  dropDatabase(name?: string): Promise<void>;
48
55
  execute(sql: string, options?: {
49
56
  wrap?: boolean;
57
+ ctx?: Transaction;
50
58
  }): Promise<void>;
51
59
  private wrapSchema;
52
60
  private createSchemaBuilder;