@mikro-orm/knex 6.0.0-dev.32 → 6.0.0-dev.320

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 +45 -20
  3. package/AbstractSqlDriver.d.ts +30 -24
  4. package/AbstractSqlDriver.js +606 -268
  5. package/AbstractSqlPlatform.d.ts +9 -5
  6. package/AbstractSqlPlatform.js +25 -29
  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 -5
  15. package/SqlEntityRepository.js +11 -7
  16. package/index.mjs +24 -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 +69 -36
  27. package/query/QueryBuilder.js +469 -146
  28. package/query/QueryBuilderHelper.d.ts +13 -18
  29. package/query/QueryBuilderHelper.js +199 -171
  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 +370 -42
  38. package/schema/SchemaComparator.d.ts +7 -5
  39. package/schema/SchemaComparator.js +69 -35
  40. package/schema/SchemaHelper.d.ts +7 -5
  41. package/schema/SchemaHelper.js +30 -11
  42. package/schema/SqlSchemaGenerator.d.ts +11 -48
  43. package/schema/SqlSchemaGenerator.js +84 -30
  44. package/typings.d.ts +27 -8
@@ -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
  }
@@ -21,21 +19,30 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
21
19
  /**
22
20
  * Returns true if the database was created.
23
21
  */
24
- async ensureDatabase() {
22
+ async ensureDatabase(options) {
25
23
  const dbName = this.config.get('dbName');
26
- if (this.lastEnsuredDatabase === dbName) {
24
+ if (this.lastEnsuredDatabase === dbName && !options?.forceCheck) {
27
25
  return true;
28
26
  }
29
27
  const exists = await this.helper.databaseExists(this.connection, dbName);
30
28
  this.lastEnsuredDatabase = dbName;
31
29
  if (!exists) {
32
- this.config.set('dbName', this.helper.getManagementDbName());
33
- await this.driver.reconnect();
34
- await this.createDatabase(dbName);
35
- this.config.set('dbName', dbName);
36
- await this.driver.reconnect();
30
+ const managementDbName = this.helper.getManagementDbName();
31
+ if (managementDbName) {
32
+ this.config.set('dbName', managementDbName);
33
+ await this.driver.reconnect();
34
+ await this.createDatabase(dbName);
35
+ this.config.set('dbName', dbName);
36
+ await this.driver.reconnect();
37
+ }
38
+ if (options?.create) {
39
+ await this.createSchema(options);
40
+ }
37
41
  return true;
38
42
  }
43
+ if (options?.clear) {
44
+ await this.clearDatabase(options);
45
+ }
39
46
  return false;
40
47
  }
41
48
  getTargetSchema(schema) {
@@ -93,7 +100,8 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
93
100
  await this.ensureDatabase();
94
101
  const wrap = options.wrap ?? this.options.disableForeignKeys;
95
102
  const metadata = this.getOrderedMetadata(options.schema).reverse();
96
- const schema = await DatabaseSchema_1.DatabaseSchema.create(this.connection, this.platform, this.config, options.schema);
103
+ const schemas = this.getTargetSchema(options.schema).getNamespaces();
104
+ const schema = await DatabaseSchema_1.DatabaseSchema.create(this.connection, this.platform, this.config, options.schema, schemas);
97
105
  let ret = '';
98
106
  // remove FKs explicitly if we can't use cascading statement and we don't disable FK checks (we need this for circular relations)
99
107
  for (const meta of metadata) {
@@ -110,6 +118,12 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
110
118
  for (const meta of metadata) {
111
119
  ret += await this.dump(this.dropTable(meta.collection, this.getSchemaName(meta, options)), '\n');
112
120
  }
121
+ if (this.platform.supportsNativeEnums()) {
122
+ for (const columnName of Object.keys(schema.getNativeEnums())) {
123
+ const sql = this.helper.getDropNativeEnumSQL(columnName, options.schema ?? this.config.get('schema'));
124
+ ret += await this.dump(this.knex.schema.raw(sql), '\n');
125
+ }
126
+ }
113
127
  if (options.dropMigrationsTable) {
114
128
  ret += await this.dump(this.dropTable(this.config.get('migrations').tableName, this.config.get('schema')), '\n');
115
129
  }
@@ -132,11 +146,13 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
132
146
  return this.diffToSQL(diffUp, options);
133
147
  }
134
148
  async getUpdateSchemaMigrationSQL(options = {}) {
135
- await this.ensureDatabase();
149
+ if (!options.fromSchema) {
150
+ await this.ensureDatabase();
151
+ }
136
152
  const { fromSchema, toSchema } = await this.prepareSchemaForComparison(options);
137
153
  const comparator = new SchemaComparator_1.SchemaComparator(this.platform);
138
154
  const diffUp = comparator.compare(fromSchema, toSchema);
139
- const diffDown = comparator.compare(toSchema, fromSchema);
155
+ const diffDown = comparator.compare(toSchema, fromSchema, diffUp);
140
156
  return {
141
157
  up: await this.diffToSQL(diffUp, options),
142
158
  down: this.platform.supportsDownMigrations() ? await this.diffToSQL(diffDown, options) : '',
@@ -147,10 +163,12 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
147
163
  options.safe ??= false;
148
164
  options.dropTables ??= true;
149
165
  const toSchema = this.getTargetSchema(options.schema);
150
- const fromSchema = options.fromSchema ?? await DatabaseSchema_1.DatabaseSchema.create(this.connection, this.platform, this.config, options.schema);
166
+ const schemas = toSchema.getNamespaces();
167
+ const fromSchema = options.fromSchema ?? await DatabaseSchema_1.DatabaseSchema.create(this.connection, this.platform, this.config, options.schema, schemas);
151
168
  const wildcardSchemaTables = Object.values(this.metadata.getAll()).filter(meta => meta.schema === '*').map(meta => meta.tableName);
152
169
  fromSchema.prune(options.schema, wildcardSchemaTables);
153
170
  toSchema.prune(options.schema, wildcardSchemaTables);
171
+ toSchema.setNativeEnums(fromSchema.getNativeEnums());
154
172
  return { fromSchema, toSchema };
155
173
  }
156
174
  async diffToSQL(schemaDiff, options) {
@@ -206,7 +224,7 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
206
224
  if (schema && schemaName === '*') {
207
225
  return `${schema}.${referencedTableName.replace(/^\*\./, '')}`;
208
226
  }
209
- if (schemaName === this.platform.getDefaultSchemaName()) {
227
+ if (!schemaName || schemaName === this.platform.getDefaultSchemaName()) {
210
228
  return tableName;
211
229
  }
212
230
  return `${schemaName}.${tableName}`;
@@ -254,6 +272,12 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
254
272
  alterTable(diff, safe) {
255
273
  const ret = [];
256
274
  const [schemaName, tableName] = this.splitTableName(diff.name);
275
+ const changedNativeEnums = [];
276
+ for (const { column, changedProperties } of Object.values(diff.changedColumns)) {
277
+ if (column.nativeEnumName && changedProperties.has('enumItems') && column.nativeEnumName in diff.fromTable.nativeEnums) {
278
+ changedNativeEnums.push([column.nativeEnumName, column.enumItems, diff.fromTable.getColumn(column.name).enumItems]);
279
+ }
280
+ }
257
281
  ret.push(this.createSchemaBuilder(schemaName).alterTable(tableName, table => {
258
282
  for (const index of Object.values(diff.removedIndexes)) {
259
283
  this.dropIndex(table, index);
@@ -267,6 +291,14 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
267
291
  for (const check of Object.values(diff.changedChecks)) {
268
292
  this.dropCheck(table, check);
269
293
  }
294
+ /* istanbul ignore else */
295
+ if (!safe) {
296
+ for (const column of Object.values(diff.removedColumns)) {
297
+ this.helper.pushTableQuery(table, `alter table ${this.platform.quoteIdentifier(tableName)} drop column ${this.platform.quoteIdentifier(column.name)}`);
298
+ }
299
+ }
300
+ }));
301
+ ret.push(this.createSchemaBuilder(schemaName).alterTable(tableName, table => {
270
302
  for (const column of Object.values(diff.addedColumns)) {
271
303
  const col = this.helper.createTableColumn(table, column, diff.fromTable);
272
304
  this.helper.configureColumn(column, col, this.knex);
@@ -280,27 +312,24 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
280
312
  .onDelete(foreignKey.deleteRule);
281
313
  }
282
314
  }
283
- /* istanbul ignore else */
284
- if (!safe) {
285
- for (const column of Object.values(diff.removedColumns)) {
286
- table.dropColumn(column.name);
287
- }
288
- }
289
315
  for (const { column, changedProperties } of Object.values(diff.changedColumns)) {
290
316
  if (changedProperties.size === 1 && changedProperties.has('comment')) {
291
317
  continue;
292
318
  }
319
+ if (changedProperties.size === 1 && changedProperties.has('enumItems') && column.nativeEnumName) {
320
+ continue;
321
+ }
293
322
  const col = this.helper.createTableColumn(table, column, diff.fromTable, changedProperties).alter();
294
323
  this.helper.configureColumn(column, col, this.knex, changedProperties);
295
324
  }
296
325
  for (const { column } of Object.values(diff.changedColumns).filter(diff => diff.changedProperties.has('autoincrement'))) {
297
- this.helper.pushTableQuery(table, this.helper.getAlterColumnAutoincrement(tableName, column));
326
+ this.helper.pushTableQuery(table, this.helper.getAlterColumnAutoincrement(tableName, column, schemaName));
298
327
  }
299
328
  for (const { column, changedProperties } of Object.values(diff.changedColumns).filter(diff => diff.changedProperties.has('comment'))) {
300
329
  if (['type', 'nullable', 'autoincrement', 'unsigned', 'default', 'enumItems'].some(t => changedProperties.has(t))) {
301
330
  continue; // will be handled via knex
302
331
  }
303
- this.helper.pushTableQuery(table, this.helper.getChangeColumnCommentSQL(tableName, column));
332
+ this.helper.pushTableQuery(table, this.helper.getChangeColumnCommentSQL(tableName, column, schemaName));
304
333
  }
305
334
  for (const [oldColumnName, column] of Object.entries(diff.renamedColumns)) {
306
335
  this.helper.pushTableQuery(table, this.helper.getRenameColumnSQL(tableName, oldColumnName, column, schemaName));
@@ -337,17 +366,28 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
337
366
  table.comment(comment);
338
367
  }
339
368
  }));
369
+ if (this.platform.supportsNativeEnums()) {
370
+ changedNativeEnums.forEach(([enumName, itemsNew, itemsOld]) => {
371
+ // postgres allows only adding new items
372
+ const newItems = itemsNew.filter(val => !itemsOld.includes(val));
373
+ ret.push(...newItems.map(val => this.knex.schema.raw(this.helper.getAlterNativeEnumSQL(enumName, schemaName, val))));
374
+ });
375
+ }
340
376
  return ret;
341
377
  }
342
378
  /**
343
379
  * creates new database and connects to it
344
380
  */
345
381
  async createDatabase(name) {
346
- await this.driver.execute(this.helper.getCreateDatabaseSQL('' + this.knex.ref(name)));
382
+ const sql = this.helper.getCreateDatabaseSQL('' + this.knex.ref(name));
383
+ if (sql) {
384
+ await this.driver.execute(sql);
385
+ }
347
386
  this.config.set('dbName', name);
348
387
  await this.driver.reconnect();
349
388
  }
350
389
  async dropDatabase(name) {
390
+ name ??= this.config.get('dbName');
351
391
  this.config.set('dbName', this.helper.getManagementDbName());
352
392
  await this.driver.reconnect();
353
393
  await this.driver.execute(this.helper.getDropDatabaseSQL('' + this.knex.ref(name)));
@@ -365,7 +405,7 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
365
405
  }
366
406
  await core_1.Utils.runSerial(lines, line => this.driver.execute(line));
367
407
  }
368
- wrapSchema(sql, options = {}) {
408
+ wrapSchema(sql, options) {
369
409
  options.wrap ??= this.options.disableForeignKeys;
370
410
  if (!options.wrap || sql.trim() === '') {
371
411
  return sql;
@@ -416,7 +456,14 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
416
456
  table.primary(index.columnNames, keyName);
417
457
  }
418
458
  else if (index.unique) {
419
- table.unique(index.columnNames, { indexName: index.keyName });
459
+ // JSON columns can have unique index but not unique constraint, and we need to distinguish those, so we can properly drop them
460
+ if (index.columnNames.some(column => column.includes('.'))) {
461
+ const columns = this.platform.getJsonIndexDefinition(index);
462
+ table.index(columns.map(column => this.knex.raw(`(${column})`)), index.keyName, { indexType: 'unique' });
463
+ }
464
+ else {
465
+ table.unique(index.columnNames, { indexName: index.keyName });
466
+ }
420
467
  }
421
468
  else if (index.expression) {
422
469
  this.helper.pushTableQuery(table, index.expression);
@@ -428,14 +475,21 @@ class SqlSchemaGenerator extends core_1.AbstractSchemaGenerator {
428
475
  }
429
476
  }
430
477
  else {
431
- table.index(index.columnNames, index.keyName, index.type);
478
+ // JSON columns can have unique index but not unique constraint, and we need to distinguish those, so we can properly drop them
479
+ if (index.columnNames.some(column => column.includes('.'))) {
480
+ const columns = this.platform.getJsonIndexDefinition(index);
481
+ table.index(columns.map(column => this.knex.raw(`(${column})`)), index.keyName, index.type);
482
+ }
483
+ else {
484
+ table.index(index.columnNames, index.keyName, index.type);
485
+ }
432
486
  }
433
487
  }
434
488
  dropIndex(table, index, oldIndexName = index.keyName) {
435
489
  if (index.primary) {
436
490
  table.dropPrimary(oldIndexName);
437
491
  }
438
- else if (index.unique) {
492
+ else if (index.unique && index.constraint) {
439
493
  table.dropUnique(index.columnNames, oldIndexName);
440
494
  }
441
495
  else {
package/typings.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { Knex } from 'knex';
2
- import type { CheckCallback, Dictionary, EntityProperty, GroupOperator, QBFilterQuery, QueryOrderMap, Type } from '@mikro-orm/core';
3
- import type { QueryType } from './query/enums';
2
+ import type { CheckCallback, Dictionary, EntityProperty, GroupOperator, RawQueryFragment, QBFilterQuery, QueryOrderMap, Type, QueryFlag } from '@mikro-orm/core';
3
+ import type { JoinType, QueryType } from './query/enums';
4
4
  import type { DatabaseSchema, DatabaseTable } from './schema';
5
5
  export interface Table {
6
6
  table_name: string;
@@ -11,11 +11,11 @@ export type KnexStringRef = Knex.Ref<string, {
11
11
  [alias: string]: string;
12
12
  }>;
13
13
  type AnyString = string & {};
14
- export type Field<T> = AnyString | keyof T | KnexStringRef | Knex.QueryBuilder;
14
+ export type Field<T> = AnyString | keyof T | RawQueryFragment | KnexStringRef | Knex.QueryBuilder;
15
15
  export interface JoinOptions {
16
16
  table: string;
17
17
  schema?: string;
18
- type: 'leftJoin' | 'innerJoin' | 'pivotJoin';
18
+ type: JoinType;
19
19
  alias: string;
20
20
  ownerAlias: string;
21
21
  inverseAlias?: string;
@@ -25,6 +25,8 @@ export interface JoinOptions {
25
25
  path?: string;
26
26
  prop: EntityProperty;
27
27
  cond: Dictionary;
28
+ cond_?: Dictionary;
29
+ subquery?: string;
28
30
  }
29
31
  export interface Column {
30
32
  name: string;
@@ -38,6 +40,8 @@ export interface Column {
38
40
  scale?: number;
39
41
  default?: string | null;
40
42
  comment?: string;
43
+ generated?: string;
44
+ nativeEnumName?: string;
41
45
  enumItems?: string[];
42
46
  primary?: boolean;
43
47
  unique?: boolean;
@@ -58,9 +62,11 @@ export interface IndexDef {
58
62
  columnNames: string[];
59
63
  keyName: string;
60
64
  unique: boolean;
65
+ constraint: boolean;
61
66
  primary: boolean;
62
67
  composite?: boolean;
63
68
  expression?: string;
69
+ options?: Dictionary;
64
70
  type?: string | Readonly<{
65
71
  indexType?: string;
66
72
  storageEngineIndexType?: 'hash' | 'btree';
@@ -119,10 +125,12 @@ export interface IQueryBuilder<T> {
119
125
  delete(cond?: QBFilterQuery): this;
120
126
  truncate(): this;
121
127
  count(field?: string | string[], distinct?: boolean): this;
122
- join(field: string, alias: string, cond?: QBFilterQuery, type?: 'leftJoin' | 'innerJoin' | 'pivotJoin', path?: string): this;
128
+ join(field: string, alias: string, cond?: QBFilterQuery, type?: JoinType, path?: string): this;
129
+ innerJoin(field: string, alias: string, cond?: QBFilterQuery): this;
123
130
  leftJoin(field: string, alias: string, cond?: QBFilterQuery): this;
124
131
  joinAndSelect(field: string, alias: string, cond?: QBFilterQuery): this;
125
- leftJoinAndSelect(field: string, alias: string, cond?: QBFilterQuery): this;
132
+ leftJoinAndSelect(field: string, alias: string, cond?: QBFilterQuery, fields?: string[]): this;
133
+ innerJoinAndSelect(field: string, alias: string, cond?: QBFilterQuery, fields?: string[]): this;
126
134
  withSubQuery(subQuery: Knex.QueryBuilder, alias: string): this;
127
135
  where(cond: QBFilterQuery<T>, operator?: keyof typeof GroupOperator): this;
128
136
  where(cond: string, params?: any[], operator?: keyof typeof GroupOperator): this;
@@ -133,8 +141,19 @@ export interface IQueryBuilder<T> {
133
141
  orderBy(orderBy: QueryOrderMap<T>): this;
134
142
  groupBy(fields: (string | keyof T) | (string | keyof T)[]): this;
135
143
  having(cond?: QBFilterQuery | string, params?: any[]): this;
136
- getAliasForJoinPath(path: string): string | undefined;
144
+ getAliasForJoinPath(path: string, options?: ICriteriaNodeProcessOptions): string | undefined;
145
+ getJoinForPath(path?: string, options?: ICriteriaNodeProcessOptions): JoinOptions | undefined;
137
146
  getNextAlias(entityName?: string): string;
147
+ clone(reset?: boolean): IQueryBuilder<T>;
148
+ setFlag(flag: QueryFlag): this;
149
+ unsetFlag(flag: QueryFlag): this;
150
+ hasFlag(flag: QueryFlag): boolean;
151
+ }
152
+ export interface ICriteriaNodeProcessOptions {
153
+ alias?: string;
154
+ matchPopulateJoins?: boolean;
155
+ ignoreBranching?: boolean;
156
+ preferNoBranch?: boolean;
138
157
  }
139
158
  export interface ICriteriaNode<T extends object> {
140
159
  readonly entityName: string;
@@ -143,7 +162,7 @@ export interface ICriteriaNode<T extends object> {
143
162
  payload: any;
144
163
  prop?: EntityProperty;
145
164
  index?: number;
146
- process(qb: IQueryBuilder<T>, alias?: string): any;
165
+ process(qb: IQueryBuilder<T>, options?: ICriteriaNodeProcessOptions): any;
147
166
  shouldInline(payload: any): boolean;
148
167
  willAutoJoin(qb: IQueryBuilder<T>, alias?: string): boolean;
149
168
  shouldRename(payload: any): boolean;