@mikro-orm/knex 6.0.0-dev.5 → 6.0.0-dev.50

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 (40) hide show
  1. package/AbstractSqlConnection.d.ts +3 -0
  2. package/AbstractSqlConnection.js +16 -8
  3. package/AbstractSqlDriver.d.ts +11 -11
  4. package/AbstractSqlDriver.js +100 -40
  5. package/AbstractSqlPlatform.d.ts +2 -4
  6. package/AbstractSqlPlatform.js +6 -16
  7. package/README.md +63 -106
  8. package/SqlEntityManager.d.ts +0 -4
  9. package/SqlEntityManager.js +1 -9
  10. package/SqlEntityRepository.d.ts +0 -1
  11. package/SqlEntityRepository.js +0 -1
  12. package/index.d.ts +1 -0
  13. package/index.js +1 -0
  14. package/index.mjs +173 -0
  15. package/package.json +5 -39
  16. package/query/ArrayCriteriaNode.d.ts +3 -3
  17. package/query/CriteriaNode.d.ts +8 -8
  18. package/query/CriteriaNode.js +8 -8
  19. package/query/CriteriaNodeFactory.d.ts +6 -6
  20. package/query/CriteriaNodeFactory.js +11 -11
  21. package/query/ObjectCriteriaNode.d.ts +3 -3
  22. package/query/ObjectCriteriaNode.js +5 -5
  23. package/query/QueryBuilder.d.ts +11 -4
  24. package/query/QueryBuilder.js +141 -36
  25. package/query/QueryBuilderHelper.d.ts +3 -3
  26. package/query/QueryBuilderHelper.js +47 -19
  27. package/query/ScalarCriteriaNode.d.ts +3 -2
  28. package/query/ScalarCriteriaNode.js +9 -6
  29. package/schema/DatabaseSchema.js +3 -3
  30. package/schema/DatabaseTable.d.ts +9 -9
  31. package/schema/DatabaseTable.js +21 -22
  32. package/schema/SchemaComparator.d.ts +4 -4
  33. package/schema/SchemaComparator.js +4 -2
  34. package/schema/SchemaHelper.d.ts +11 -11
  35. package/schema/SchemaHelper.js +3 -3
  36. package/schema/{SchemaGenerator.d.ts → SqlSchemaGenerator.d.ts} +4 -8
  37. package/schema/{SchemaGenerator.js → SqlSchemaGenerator.js} +17 -24
  38. package/schema/index.d.ts +1 -1
  39. package/schema/index.js +1 -1
  40. package/typings.d.ts +14 -13
@@ -7,6 +7,9 @@ export declare abstract class AbstractSqlConnection extends Connection {
7
7
  protected platform: AbstractSqlPlatform;
8
8
  protected client: Knex;
9
9
  constructor(config: Configuration, options?: ConnectionOptions, type?: 'read' | 'write');
10
+ abstract createKnex(): void;
11
+ /** @inheritDoc */
12
+ connect(): void | Promise<void>;
10
13
  getKnex(): Knex;
11
14
  close(force?: boolean): Promise<void>;
12
15
  isConnected(): Promise<boolean>;
@@ -14,16 +14,23 @@ class AbstractSqlConnection extends core_1.Connection {
14
14
  super(config, options, type);
15
15
  this.patchKnexClient();
16
16
  }
17
+ /** @inheritDoc */
18
+ connect() {
19
+ this.createKnex();
20
+ }
17
21
  getKnex() {
22
+ if (!this.client) {
23
+ this.createKnex();
24
+ }
18
25
  return this.client;
19
26
  }
20
27
  async close(force) {
21
28
  await super.close(force);
22
- await this.client.destroy();
29
+ await this.getKnex().destroy();
23
30
  }
24
31
  async isConnected() {
25
32
  try {
26
- await this.client.raw('select 1');
33
+ await this.getKnex().raw('select 1');
27
34
  return true;
28
35
  }
29
36
  catch {
@@ -46,7 +53,7 @@ class AbstractSqlConnection extends core_1.Connection {
46
53
  if (!options.ctx) {
47
54
  await options.eventBroadcaster?.dispatchEvent(core_1.EventType.beforeTransactionStart);
48
55
  }
49
- const trx = await (options.ctx || this.client).transaction(null, { isolationLevel: options.isolationLevel });
56
+ const trx = await (options.ctx || this.getKnex()).transaction(null, { isolationLevel: options.isolationLevel });
50
57
  if (!options.ctx) {
51
58
  await options.eventBroadcaster?.dispatchEvent(core_1.EventType.afterTransactionStart, trx);
52
59
  }
@@ -77,8 +84,9 @@ class AbstractSqlConnection extends core_1.Connection {
77
84
  }
78
85
  }
79
86
  async execute(queryOrKnex, params = [], method = 'all', ctx) {
87
+ await this.ensureConnection();
80
88
  if (core_1.Utils.isObject(queryOrKnex)) {
81
- ctx ?? (ctx = queryOrKnex.client.transacting ? queryOrKnex : null);
89
+ ctx ??= (queryOrKnex.client.transacting ? queryOrKnex : null);
82
90
  const q = queryOrKnex.toSQL();
83
91
  queryOrKnex = q.sql;
84
92
  params = q.bindings;
@@ -86,7 +94,7 @@ class AbstractSqlConnection extends core_1.Connection {
86
94
  const formatted = this.platform.formatQuery(queryOrKnex, params);
87
95
  const sql = this.getSql(queryOrKnex, formatted);
88
96
  const res = await this.executeQuery(sql, () => {
89
- const query = this.client.raw(formatted);
97
+ const query = this.getKnex().raw(formatted);
90
98
  if (ctx) {
91
99
  query.transacting(ctx);
92
100
  }
@@ -99,7 +107,7 @@ class AbstractSqlConnection extends core_1.Connection {
99
107
  */
100
108
  async loadFile(path) {
101
109
  const buf = await (0, fs_extra_1.readFile)(path);
102
- await this.client.raw(buf.toString());
110
+ await this.getKnex().raw(buf.toString());
103
111
  }
104
112
  createKnexClient(type) {
105
113
  const driverOptions = this.config.get('driverOptions');
@@ -146,7 +154,7 @@ class AbstractSqlConnection extends core_1.Connection {
146
154
  if (logger.isEnabled('query-params')) {
147
155
  return formatted;
148
156
  }
149
- return this.client.client.positionBindings(query);
157
+ return this.getKnex().client.positionBindings(query);
150
158
  }
151
159
  /**
152
160
  * do not call `positionBindings` when there are no bindings - it was messing up with
@@ -177,5 +185,5 @@ class AbstractSqlConnection extends core_1.Connection {
177
185
  };
178
186
  }
179
187
  }
180
- exports.AbstractSqlConnection = AbstractSqlConnection;
181
188
  AbstractSqlConnection.__patched = false;
189
+ exports.AbstractSqlConnection = AbstractSqlConnection;
@@ -6,17 +6,17 @@ import type { AbstractSqlPlatform } from './AbstractSqlPlatform';
6
6
  import { QueryBuilder, QueryType } from './query';
7
7
  import { SqlEntityManager } from './SqlEntityManager';
8
8
  import type { Field } from './typings';
9
- export declare abstract class AbstractSqlDriver<C extends AbstractSqlConnection = AbstractSqlConnection> extends DatabaseDriver<C> {
9
+ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlConnection = AbstractSqlConnection, Platform extends AbstractSqlPlatform = AbstractSqlPlatform> extends DatabaseDriver<Connection> {
10
10
  [EntityManagerType]: SqlEntityManager<this>;
11
- protected readonly connection: C;
12
- protected readonly replicas: C[];
13
- protected readonly platform: AbstractSqlPlatform;
14
- protected constructor(config: Configuration, platform: AbstractSqlPlatform, connection: Constructor<C>, connector: string[]);
15
- getPlatform(): AbstractSqlPlatform;
11
+ protected readonly connection: Connection;
12
+ protected readonly replicas: Connection[];
13
+ protected readonly platform: Platform;
14
+ protected constructor(config: Configuration, platform: Platform, connection: Constructor<Connection>, connector: string[]);
15
+ getPlatform(): Platform;
16
16
  createEntityManager<D extends IDatabaseDriver = IDatabaseDriver>(useContext?: boolean): D[typeof EntityManagerType];
17
- find<T extends object, P extends string = never>(entityName: string, where: FilterQuery<T>, options?: FindOptions<T, P>): Promise<EntityData<T>[]>;
18
- findOne<T extends object, P extends string = never>(entityName: string, where: FilterQuery<T>, options?: FindOneOptions<T, P>): Promise<EntityData<T> | null>;
19
- findVirtual<T extends object>(entityName: string, where: FilterQuery<T>, options: FindOptions<T, any>): Promise<EntityData<T>[]>;
17
+ find<T extends object, P extends string = never, F extends string = '*'>(entityName: string, where: FilterQuery<T>, options?: FindOptions<T, P, F>): Promise<EntityData<T>[]>;
18
+ findOne<T extends object, P extends string = never, F extends string = '*'>(entityName: string, where: FilterQuery<T>, options?: FindOneOptions<T, P, F>): Promise<EntityData<T> | null>;
19
+ findVirtual<T extends object>(entityName: string, where: FilterQuery<T>, options: FindOptions<T, any, any>): Promise<EntityData<T>[]>;
20
20
  countVirtual<T extends object>(entityName: string, where: FilterQuery<T>, options: CountOptions<T>): Promise<number>;
21
21
  protected wrapVirtualExpressionInSubquery<T extends object>(meta: EntityMetadata<T>, expression: string, where: FilterQuery<T>, options: FindOptions<T, any>, type: QueryType.COUNT): Promise<number>;
22
22
  protected wrapVirtualExpressionInSubquery<T extends object>(meta: EntityMetadata<T>, expression: string, where: FilterQuery<T>, options: FindOptions<T, any>, type: QueryType.SELECT): Promise<T[]>;
@@ -36,7 +36,7 @@ export declare abstract class AbstractSqlDriver<C extends AbstractSqlConnection
36
36
  /**
37
37
  * 1:1 owner side needs to be marked for population so QB auto-joins the owner id
38
38
  */
39
- protected autoJoinOneToOneOwner<T extends object, P extends string = never>(meta: EntityMetadata, populate: PopulateOptions<T>[], fields?: readonly EntityField<T, P>[]): PopulateOptions<T>[];
39
+ protected autoJoinOneToOneOwner<T extends object, P extends string = never>(meta: EntityMetadata<T>, populate: PopulateOptions<T>[], fields?: readonly EntityField<T, P>[]): PopulateOptions<T>[];
40
40
  protected joinedProps<T>(meta: EntityMetadata, populate: PopulateOptions<T>[]): PopulateOptions<T>[];
41
41
  /**
42
42
  * @internal
@@ -58,7 +58,7 @@ export declare abstract class AbstractSqlDriver<C extends AbstractSqlConnection
58
58
  protected updateCollectionDiff<T extends object, O extends object>(meta: EntityMetadata<O>, prop: EntityProperty<T>, pks: Primary<O>[], deleteDiff: Primary<T>[][] | boolean, insertDiff: Primary<T>[][], options?: DriverMethodOptions & {
59
59
  ownerSchema?: string;
60
60
  }): Promise<void>;
61
- lockPessimistic<T>(entity: T, options: LockOptions): Promise<void>;
61
+ lockPessimistic<T extends object>(entity: T, options: LockOptions): Promise<void>;
62
62
  protected buildJoinedPropsOrderBy<T extends object>(entityName: string, qb: QueryBuilder<T>, meta: EntityMetadata<T>, populate: PopulateOptions<T>[], parentPath?: string): QueryOrderMap<T>[];
63
63
  protected normalizeFields<T extends object>(fields: Field<T>[], prefix?: string): string[];
64
64
  protected processField<T extends object>(meta: EntityMetadata<T>, prop: EntityProperty<T> | undefined, field: string, ret: Field<T>[], populate: PopulateOptions<T>[], joinedProps: PopulateOptions<T>[], qb: QueryBuilder<T>): void;
@@ -29,16 +29,25 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
29
29
  const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false);
30
30
  const fields = this.buildFields(meta, populate, joinedProps, qb, options.fields);
31
31
  const joinedPropsOrderBy = this.buildJoinedPropsOrderBy(entityName, qb, meta, joinedProps);
32
+ const orderBy = [...core_1.Utils.asArray(options.orderBy), ...joinedPropsOrderBy];
32
33
  if (core_1.Utils.isPrimaryKey(where, meta.compositePK)) {
33
34
  where = { [core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: where };
34
35
  }
36
+ const { first, last, before, after } = options;
37
+ const isCursorPagination = [first, last, before, after].some(v => v != null);
35
38
  qb.select(fields)
36
39
  .populate(populate, joinedProps.length > 0 ? options.populateWhere : undefined)
37
40
  .where(where)
38
- .orderBy([...core_1.Utils.asArray(options.orderBy), ...joinedPropsOrderBy])
39
41
  .groupBy(options.groupBy)
40
42
  .having(options.having)
41
43
  .withSchema(this.getSchemaName(meta, options));
44
+ if (isCursorPagination) {
45
+ const { orderBy: newOrderBy, where } = this.processCursorOptions(meta, options, orderBy);
46
+ qb.andWhere(where).orderBy(newOrderBy);
47
+ }
48
+ else {
49
+ qb.orderBy(orderBy);
50
+ }
42
51
  if (options.limit !== undefined) {
43
52
  qb.limit(options.limit, options.offset);
44
53
  }
@@ -47,6 +56,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
47
56
  }
48
57
  core_1.Utils.asArray(options.flags).forEach(flag => qb.setFlag(flag));
49
58
  const result = await this.rethrow(qb.execute('all'));
59
+ if (isCursorPagination && !first && !!last) {
60
+ result.reverse();
61
+ }
50
62
  if (joinedProps.length > 0) {
51
63
  return this.mergeJoinedResult(result, meta);
52
64
  }
@@ -61,7 +73,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
61
73
  opts.limit = 1;
62
74
  }
63
75
  if (opts.limit > 0 && !opts.flags?.includes(core_1.QueryFlag.DISABLE_PAGINATE)) {
64
- opts.flags ?? (opts.flags = []);
76
+ opts.flags ??= [];
65
77
  opts.flags.push(core_1.QueryFlag.DISABLE_PAGINATE);
66
78
  }
67
79
  const res = await this.find(entityName, where, opts);
@@ -112,7 +124,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
112
124
  qb.where(where);
113
125
  const kqb = qb.getKnexQuery().clear('select');
114
126
  if (type === query_1.QueryType.COUNT) {
115
- kqb.select(qb.raw('count(*) as count'));
127
+ kqb.select(this.connection.getKnex().raw('count(*) as count'));
116
128
  }
117
129
  else { // select
118
130
  kqb.select('*');
@@ -154,8 +166,8 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
154
166
  }));
155
167
  if (!hasPK) {
156
168
  // initialize empty collections
157
- if ([core_1.ReferenceType.MANY_TO_MANY, core_1.ReferenceType.ONE_TO_MANY].includes(relation.reference)) {
158
- result[relation.name] = result[relation.name] || [];
169
+ if ([core_1.ReferenceKind.MANY_TO_MANY, core_1.ReferenceKind.ONE_TO_MANY].includes(relation.kind)) {
170
+ result[relation.name] ??= [];
159
171
  }
160
172
  return;
161
173
  }
@@ -179,8 +191,8 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
179
191
  else {
180
192
  map[key] = result;
181
193
  }
182
- if ([core_1.ReferenceType.MANY_TO_MANY, core_1.ReferenceType.ONE_TO_MANY].includes(relation.reference)) {
183
- result[relation.name] = result[relation.name] || [];
194
+ if ([core_1.ReferenceKind.MANY_TO_MANY, core_1.ReferenceKind.ONE_TO_MANY].includes(relation.kind)) {
195
+ result[relation.name] ??= [];
184
196
  this.appendToCollection(meta2, result[relation.name], relationPojo);
185
197
  }
186
198
  else {
@@ -215,7 +227,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
215
227
  return this.rethrow(qb.getCount());
216
228
  }
217
229
  async nativeInsert(entityName, data, options = {}) {
218
- options.convertCustomTypes ?? (options.convertCustomTypes = true);
230
+ options.convertCustomTypes ??= true;
219
231
  const meta = this.metadata.find(entityName);
220
232
  const collections = this.extractManyToMany(entityName, data);
221
233
  const pks = meta?.primaryKeys ?? [this.config.getNamingStrategy().referenceColumnName()];
@@ -235,16 +247,20 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
235
247
  return res;
236
248
  }
237
249
  async nativeInsertMany(entityName, data, options = {}) {
238
- options.processCollections ?? (options.processCollections = true);
239
- options.convertCustomTypes ?? (options.convertCustomTypes = true);
250
+ options.processCollections ??= true;
251
+ options.convertCustomTypes ??= true;
240
252
  const meta = this.metadata.find(entityName);
241
253
  const collections = options.processCollections ? data.map(d => this.extractManyToMany(entityName, d)) : [];
242
254
  const pks = this.getPrimaryKeyFields(entityName);
243
255
  const set = new Set();
244
- data.forEach(row => Object.keys(row).forEach(k => set.add(k)));
256
+ data.forEach(row => core_1.Utils.keys(row).forEach(k => set.add(k)));
245
257
  const props = [...set].map(name => meta?.properties[name] ?? { name, fieldNames: [name] });
246
- const fields = core_1.Utils.flatten(props.map(prop => prop.fieldNames));
258
+ let fields = core_1.Utils.flatten(props.map(prop => prop.fieldNames));
259
+ const duplicates = core_1.Utils.findDuplicates(fields);
247
260
  const params = [];
261
+ if (duplicates.length) {
262
+ fields = core_1.Utils.unique(fields);
263
+ }
248
264
  /* istanbul ignore next */
249
265
  const tableName = meta ? this.getTableName(meta, options) : this.platform.quoteIdentifier(entityName);
250
266
  let sql = `insert into ${tableName} `;
@@ -255,29 +271,45 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
255
271
  else {
256
272
  sql += ' ' + data.map(() => `select null as ${this.platform.quoteIdentifier(pks[0])}`).join(' union all ');
257
273
  }
274
+ const addParams = (prop, row) => {
275
+ if (options.convertCustomTypes && prop.customType) {
276
+ params.push(prop.customType.convertToDatabaseValue(row[prop.name], this.platform, { key: prop.name, mode: 'query-data' }));
277
+ return;
278
+ }
279
+ params.push(row[prop.name]);
280
+ };
258
281
  if (fields.length > 0 || this.platform.usesDefaultKeyword()) {
259
282
  sql += data.map(row => {
260
283
  const keys = [];
261
284
  props.forEach(prop => {
262
285
  if (prop.fieldNames.length > 1) {
263
- params.push(...(row[prop.name] ?? prop.fieldNames.map(() => null)));
264
- keys.push(...prop.fieldNames.map(_ => '?'));
286
+ const param = row[prop.name] ?? prop.fieldNames.map(() => null);
287
+ const key = (row[prop.name] ?? prop.fieldNames).map(() => '?');
288
+ prop.fieldNames.forEach((field, idx) => {
289
+ if (duplicates.includes(field)) {
290
+ param.splice(idx, 1);
291
+ key.splice(idx, 1);
292
+ }
293
+ });
294
+ params.push(...param);
295
+ keys.push(...key);
265
296
  }
266
297
  else if (prop.customType && 'convertToDatabaseValueSQL' in prop.customType && !this.platform.isRaw(row[prop.name])) {
267
298
  keys.push(prop.customType.convertToDatabaseValueSQL('?', this.platform));
268
- params.push(row[prop.name]);
299
+ addParams(prop, row);
269
300
  }
270
301
  else {
271
- params.push(row[prop.name]);
302
+ addParams(prop, row);
272
303
  keys.push('?');
273
304
  }
274
305
  });
275
306
  return '(' + (keys.join(', ') || 'default') + ')';
276
307
  }).join(', ');
277
308
  }
278
- if (this.platform.usesReturningStatement()) {
279
- /* istanbul ignore next */
280
- const returningProps = meta.hydrateProps.filter(prop => prop.persist !== false && (prop.primary || prop.defaultRaw));
309
+ if (meta && this.platform.usesReturningStatement()) {
310
+ const returningProps = meta.hydrateProps
311
+ .filter(prop => prop.persist !== false && ((prop.primary && prop.autoincrement) || prop.defaultRaw))
312
+ .filter(prop => !(prop.name in data[0]) || core_1.Utils.isRawSql(data[0][prop.name]));
281
313
  const returningFields = core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames));
282
314
  /* istanbul ignore next */
283
315
  sql += returningFields.length > 0 ? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}` : '';
@@ -289,8 +321,8 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
289
321
  pk = data.map(d => core_1.Utils.getPrimaryKeyCond(d, pks));
290
322
  }
291
323
  else {
292
- res.row ?? (res.row = {});
293
- res.rows ?? (res.rows = []);
324
+ res.row ??= {};
325
+ res.rows ??= [];
294
326
  pk = data.map((d, i) => d[pks[0]] ?? res.rows[i]?.[pks[0]]).map(d => [d]);
295
327
  res.insertId = res.insertId || res.row[pks[0]];
296
328
  }
@@ -300,7 +332,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
300
332
  return res;
301
333
  }
302
334
  async nativeUpdate(entityName, where, data, options = {}) {
303
- options.convertCustomTypes ?? (options.convertCustomTypes = true);
335
+ options.convertCustomTypes ??= true;
304
336
  const meta = this.metadata.find(entityName);
305
337
  const pks = this.getPrimaryKeyFields(entityName);
306
338
  const collections = this.extractManyToMany(entityName, data);
@@ -314,11 +346,11 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
314
346
  .withSchema(this.getSchemaName(meta, options));
315
347
  if (options.upsert) {
316
348
  /* istanbul ignore next */
317
- const uniqueFields = core_1.Utils.isPlainObject(where) ? Object.keys(where) : meta.primaryKeys;
349
+ const uniqueFields = core_1.Utils.isPlainObject(where) ? core_1.Utils.keys(where) : meta.primaryKeys;
318
350
  /* istanbul ignore next */
319
351
  qb.insert(data)
320
352
  .onConflict(uniqueFields.map(p => meta?.properties[p]?.fieldNames[0] ?? p))
321
- .merge(Object.keys(data).filter(f => !uniqueFields.includes(f)));
353
+ .merge(core_1.Utils.keys(data).filter(f => !uniqueFields.includes(f)));
322
354
  }
323
355
  else {
324
356
  qb.update(data).where(where);
@@ -331,12 +363,28 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
331
363
  return res;
332
364
  }
333
365
  async nativeUpdateMany(entityName, where, data, options = {}) {
334
- options.processCollections ?? (options.processCollections = true);
335
- options.convertCustomTypes ?? (options.convertCustomTypes = true);
366
+ options.processCollections ??= true;
367
+ options.convertCustomTypes ??= true;
336
368
  const meta = this.metadata.get(entityName);
369
+ if (options.upsert) {
370
+ const uniqueFields = core_1.Utils.isPlainObject(where[0]) ? Object.keys(where[0]) : meta.primaryKeys;
371
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes).withSchema(this.getSchemaName(meta, options));
372
+ qb.insert(data)
373
+ .onConflict(uniqueFields.map(p => meta?.properties[p]?.fieldNames[0] ?? p))
374
+ .merge(Object.keys(data[0]).filter(f => !uniqueFields.includes(f)));
375
+ return qb.execute('run', false);
376
+ }
337
377
  const collections = options.processCollections ? data.map(d => this.extractManyToMany(entityName, d)) : [];
338
378
  const keys = new Set();
339
- data.forEach(row => Object.keys(row).forEach(k => keys.add(k)));
379
+ const returning = new Set();
380
+ data.forEach(row => {
381
+ core_1.Utils.keys(row).forEach(k => {
382
+ keys.add(k);
383
+ if (core_1.Utils.isRawSql(row[k])) {
384
+ returning.add(k);
385
+ }
386
+ });
387
+ });
340
388
  const pkCond = core_1.Utils.flatten(meta.primaryKeys.map(pk => meta.properties[pk].fieldNames)).map(pk => `${this.platform.quoteIdentifier(pk)} = ?`).join(' and ');
341
389
  const params = [];
342
390
  let sql = `update ${this.getTableName(meta, options)} set `;
@@ -396,6 +444,11 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
396
444
  return '?';
397
445
  });
398
446
  sql += ` in (${conds.join(', ')})`;
447
+ if (this.platform.usesReturningStatement() && returning.size > 0) {
448
+ const returningFields = core_1.Utils.flatten([...returning].map(prop => meta.properties[prop].fieldNames));
449
+ /* istanbul ignore next */
450
+ sql += returningFields.length > 0 ? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}` : '';
451
+ }
399
452
  const res = await this.rethrow(this.execute(sql, params, 'run', options.ctx));
400
453
  for (let i = 0; i < collections.length; i++) {
401
454
  await this.processManyToMany(meta, where[i], collections[i], false, options);
@@ -430,7 +483,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
430
483
  deleteDiff.push(...snapshot);
431
484
  insertDiff.push(...current);
432
485
  }
433
- if (coll.property.reference === core_1.ReferenceType.ONE_TO_MANY) {
486
+ if (coll.property.kind === core_1.ReferenceKind.ONE_TO_MANY) {
434
487
  const cols = coll.property.referencedColumnNames;
435
488
  const qb = this.createQueryBuilder(coll.property.type, ctx, 'write')
436
489
  .withSchema(this.getSchemaName(meta, options))
@@ -444,7 +497,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
444
497
  const pivotMeta = this.metadata.find(coll.property.pivotEntity);
445
498
  if (pivotMeta.schema === '*') {
446
499
  /* istanbul ignore next */
447
- options ?? (options = {});
500
+ options ??= {};
448
501
  options.schema = ownerSchema;
449
502
  }
450
503
  return this.rethrow(this.updateCollectionDiff(meta, coll.property, pks, deleteDiff, insertDiff, options));
@@ -512,7 +565,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
512
565
  }
513
566
  const relationsToPopulate = populate.map(({ field }) => field);
514
567
  const toPopulate = meta.relations
515
- .filter(prop => prop.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
568
+ .filter(prop => prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
516
569
  .filter(prop => fields.length === 0 || fields.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
517
570
  .map(prop => ({ field: prop.name, strategy: prop.strategy }));
518
571
  return [...populate, ...toPopulate];
@@ -520,7 +573,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
520
573
  joinedProps(meta, populate) {
521
574
  return populate.filter(p => {
522
575
  const prop = meta.properties[p.field] || {};
523
- return (p.strategy || prop.strategy || this.config.get('loadStrategy')) === core_1.LoadStrategy.JOINED && prop.reference !== core_1.ReferenceType.SCALAR;
576
+ return (p.strategy || prop.strategy || this.config.get('loadStrategy')) === core_1.LoadStrategy.JOINED && prop.kind !== core_1.ReferenceKind.SCALAR;
524
577
  });
525
578
  }
526
579
  /**
@@ -545,14 +598,17 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
545
598
  getFieldsForJoinedLoad(qb, meta, explicitFields, populate = [], parentTableAlias, parentJoinPath) {
546
599
  const fields = [];
547
600
  const joinedProps = this.joinedProps(meta, populate);
601
+ if (explicitFields?.includes('*')) {
602
+ fields.push('*');
603
+ }
548
604
  const shouldHaveColumn = (prop, populate, fields) => {
549
605
  if (!this.platform.shouldHaveColumn(prop, populate)) {
550
606
  return false;
551
607
  }
552
608
  if (!fields || prop.primary) {
553
- return true;
609
+ return !fields?.includes('*');
554
610
  }
555
- return fields.includes(prop.name);
611
+ return fields.some(f => f === prop.name || f.toString().startsWith(prop.name + '.'));
556
612
  };
557
613
  // alias all fields in the primary table
558
614
  meta.props
@@ -582,7 +638,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
582
638
  const aliased = qb.ref(tableAlias ? `${tableAlias}__${prop.fieldNames[0]}` : prop.fieldNames[0]).toString();
583
639
  if (prop.customType?.convertToJSValueSQL && tableAlias) {
584
640
  const prefixed = qb.ref(prop.fieldNames[0]).withSchema(tableAlias).toString();
585
- return [qb.raw(prop.customType.convertToJSValueSQL(prefixed, this.platform) + ' as ' + aliased).toString()];
641
+ return [prop.customType.convertToJSValueSQL(prefixed, this.platform) + ' as ' + aliased];
586
642
  }
587
643
  if (prop.formula) {
588
644
  const alias = qb.ref(tableAlias ?? qb.alias).toString();
@@ -620,7 +676,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
620
676
  }
621
677
  const ret = {};
622
678
  this.metadata.find(entityName).relations.forEach(prop => {
623
- if (prop.reference === core_1.ReferenceType.MANY_TO_MANY && data[prop.name]) {
679
+ if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY && data[prop.name]) {
624
680
  ret[prop.name] = data[prop.name].map((item) => core_1.Utils.asArray(item));
625
681
  delete data[prop.name];
626
682
  }
@@ -663,7 +719,11 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
663
719
  });
664
720
  /* istanbul ignore else */
665
721
  if (this.platform.allowsMultiInsert()) {
666
- await this.nativeInsertMany(prop.pivotEntity, items, { ...options, convertCustomTypes: false, processCollections: false });
722
+ await this.nativeInsertMany(prop.pivotEntity, items, {
723
+ ...options,
724
+ convertCustomTypes: false,
725
+ processCollections: false,
726
+ });
667
727
  }
668
728
  else {
669
729
  await core_1.Utils.runSerial(items, item => {
@@ -691,7 +751,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
691
751
  const path = `${parentPath ? parentPath : entityName}.${relation.field}`;
692
752
  const propAlias = qb.getAliasForJoinPath(path);
693
753
  if (propOrderBy) {
694
- Object.keys(propOrderBy).forEach(field => {
754
+ core_1.Utils.keys(propOrderBy).forEach(field => {
695
755
  orderBy.push({ [`${propAlias}.${field}`]: propOrderBy[field] });
696
756
  });
697
757
  }
@@ -718,10 +778,10 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
718
778
  return ret;
719
779
  }
720
780
  processField(meta, prop, field, ret, populate, joinedProps, qb) {
721
- if (!prop || (prop.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner)) {
781
+ if (!prop || (prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner)) {
722
782
  return;
723
783
  }
724
- if (prop.reference === core_1.ReferenceType.EMBEDDED) {
784
+ if (prop.kind === core_1.ReferenceKind.EMBEDDED) {
725
785
  if (prop.object) {
726
786
  ret.push(prop.fieldNames[0]);
727
787
  return;
@@ -736,7 +796,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
736
796
  return;
737
797
  }
738
798
  if (prop.fieldNames) {
739
- ret.push(prop.fieldNames[0]);
799
+ ret.push(...prop.fieldNames);
740
800
  }
741
801
  }
742
802
  buildFields(meta, populate, joinedProps, qb, fields) {
@@ -1,7 +1,7 @@
1
1
  import type { Constructor, EntityManager, EntityRepository, IDatabaseDriver, MikroORM } from '@mikro-orm/core';
2
2
  import { Platform } from '@mikro-orm/core';
3
3
  import type { SchemaHelper } from './schema';
4
- import { SchemaGenerator } from './schema';
4
+ import { SqlSchemaGenerator } from './schema';
5
5
  export declare abstract class AbstractSqlPlatform extends Platform {
6
6
  protected readonly schemaHelper?: SchemaHelper;
7
7
  usesPivotTable(): boolean;
@@ -10,9 +10,7 @@ export declare abstract class AbstractSqlPlatform extends Platform {
10
10
  getSchemaHelper(): SchemaHelper | undefined;
11
11
  /** @inheritDoc */
12
12
  lookupExtensions(orm: MikroORM): void;
13
- getSchemaGenerator(driver: IDatabaseDriver, em?: EntityManager): SchemaGenerator;
14
- getEntityGenerator(em: EntityManager): any;
15
- getMigrator(em: EntityManager): any;
13
+ getSchemaGenerator(driver: IDatabaseDriver, em?: EntityManager): SqlSchemaGenerator;
16
14
  quoteValue(value: any): string;
17
15
  formatQuery(sql: string, params: readonly any[]): string;
18
16
  getSearchJsonPropertySQL(path: string, type: string, aliased: boolean): string;
@@ -20,26 +20,16 @@ class AbstractSqlPlatform extends core_1.Platform {
20
20
  }
21
21
  /** @inheritDoc */
22
22
  lookupExtensions(orm) {
23
- schema_1.SchemaGenerator.register(orm);
23
+ schema_1.SqlSchemaGenerator.register(orm);
24
24
  }
25
- // TODO remove in v6 (https://github.com/mikro-orm/mikro-orm/issues/3743)
25
+ /* istanbul ignore next: kept for type inference only */
26
26
  getSchemaGenerator(driver, em) {
27
- /* istanbul ignore next */
28
- return this.config.getCachedService(schema_1.SchemaGenerator, em ?? driver); // cast as `any` to get around circular dependencies
29
- }
30
- // TODO remove in v6 (https://github.com/mikro-orm/mikro-orm/issues/3743)
31
- getEntityGenerator(em) {
32
- // eslint-disable-next-line @typescript-eslint/no-var-requires
33
- const { EntityGenerator } = require('@mikro-orm/entity-generator');
34
- return this.config.getCachedService(EntityGenerator, em);
35
- }
36
- // TODO remove in v6 (https://github.com/mikro-orm/mikro-orm/issues/3743)
37
- getMigrator(em) {
38
- // eslint-disable-next-line @typescript-eslint/no-var-requires
39
- const { Migrator } = require('@mikro-orm/migrations');
40
- return this.config.getCachedService(Migrator, em);
27
+ return new schema_1.SqlSchemaGenerator(em ?? driver);
41
28
  }
42
29
  quoteValue(value) {
30
+ if (core_1.Utils.isRawSql(value)) {
31
+ return this.formatQuery(value.sql, value.params ?? []);
32
+ }
43
33
  if (this.isRaw(value)) {
44
34
  return value;
45
35
  }