@mikro-orm/knex 7.0.0-dev.9 → 7.0.0-dev.91

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 (59) hide show
  1. package/AbstractSqlConnection.d.ts +11 -5
  2. package/AbstractSqlConnection.js +78 -32
  3. package/AbstractSqlDriver.d.ts +9 -5
  4. package/AbstractSqlDriver.js +267 -227
  5. package/AbstractSqlPlatform.js +5 -5
  6. package/PivotCollectionPersister.d.ts +8 -4
  7. package/PivotCollectionPersister.js +55 -31
  8. package/README.md +3 -2
  9. package/SqlEntityManager.d.ts +10 -2
  10. package/SqlEntityManager.js +11 -2
  11. package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +2 -0
  12. package/dialects/mssql/MsSqlNativeQueryBuilder.js +42 -3
  13. package/dialects/mysql/MySqlExceptionConverter.d.ts +3 -3
  14. package/dialects/mysql/MySqlExceptionConverter.js +4 -5
  15. package/dialects/mysql/MySqlSchemaHelper.js +2 -2
  16. package/dialects/postgresql/PostgreSqlTableCompiler.d.ts +1 -0
  17. package/dialects/postgresql/PostgreSqlTableCompiler.js +1 -0
  18. package/dialects/sqlite/BaseSqliteConnection.d.ts +3 -2
  19. package/dialects/sqlite/BaseSqliteConnection.js +2 -14
  20. package/dialects/sqlite/BaseSqlitePlatform.js +1 -2
  21. package/dialects/sqlite/SqliteExceptionConverter.d.ts +2 -2
  22. package/dialects/sqlite/SqliteExceptionConverter.js +6 -4
  23. package/dialects/sqlite/SqliteSchemaHelper.js +5 -6
  24. package/index.d.ts +2 -1
  25. package/index.js +2 -1
  26. package/package.json +5 -5
  27. package/plugin/index.d.ts +53 -0
  28. package/plugin/index.js +42 -0
  29. package/plugin/transformer.d.ts +115 -0
  30. package/plugin/transformer.js +883 -0
  31. package/query/ArrayCriteriaNode.d.ts +1 -0
  32. package/query/ArrayCriteriaNode.js +3 -0
  33. package/query/CriteriaNode.d.ts +4 -5
  34. package/query/CriteriaNode.js +13 -9
  35. package/query/CriteriaNodeFactory.js +12 -7
  36. package/query/NativeQueryBuilder.js +1 -1
  37. package/query/ObjectCriteriaNode.d.ts +1 -0
  38. package/query/ObjectCriteriaNode.js +35 -8
  39. package/query/QueryBuilder.d.ts +59 -10
  40. package/query/QueryBuilder.js +166 -50
  41. package/query/QueryBuilderHelper.d.ts +1 -1
  42. package/query/QueryBuilderHelper.js +20 -14
  43. package/query/ScalarCriteriaNode.d.ts +3 -3
  44. package/query/ScalarCriteriaNode.js +9 -7
  45. package/query/index.d.ts +1 -0
  46. package/query/index.js +1 -0
  47. package/query/raw.d.ts +59 -0
  48. package/query/raw.js +68 -0
  49. package/query/rawKnex.d.ts +58 -0
  50. package/query/rawKnex.js +72 -0
  51. package/schema/DatabaseSchema.js +25 -4
  52. package/schema/DatabaseTable.d.ts +5 -4
  53. package/schema/DatabaseTable.js +65 -34
  54. package/schema/SchemaComparator.js +5 -6
  55. package/schema/SchemaHelper.d.ts +2 -0
  56. package/schema/SchemaHelper.js +14 -10
  57. package/schema/SqlSchemaGenerator.d.ts +13 -6
  58. package/schema/SqlSchemaGenerator.js +41 -20
  59. package/typings.d.ts +85 -3
@@ -1,4 +1,4 @@
1
- import { ALIAS_REPLACEMENT_RE, DatabaseDriver, EntityManagerType, getOnConflictFields, getOnConflictReturningFields, helper, isRaw, LoadStrategy, parseJsonSafe, QueryFlag, QueryHelper, QueryOrder, raw, RawQueryFragment, ReferenceKind, Utils, } from '@mikro-orm/core';
1
+ import { ALIAS_REPLACEMENT_RE, DatabaseDriver, EntityManagerType, getLoadingStrategy, getOnConflictFields, getOnConflictReturningFields, helper, isRaw, LoadStrategy, parseJsonSafe, QueryFlag, QueryHelper, QueryOrder, raw, RawQueryFragment, ReferenceKind, Utils, } from '@mikro-orm/core';
2
2
  import { QueryBuilder } from './query/QueryBuilder.js';
3
3
  import { JoinType, QueryType } from './query/enums.js';
4
4
  import { SqlEntityManager } from './SqlEntityManager.js';
@@ -21,15 +21,11 @@ export class AbstractSqlDriver extends DatabaseDriver {
21
21
  const EntityManagerClass = this.config.get('entityManager', SqlEntityManager);
22
22
  return new EntityManagerClass(this.config, this, this.metadata, useContext);
23
23
  }
24
- async find(entityName, where, options = {}) {
25
- options = { populate: [], orderBy: [], ...options };
26
- const meta = this.metadata.find(entityName);
27
- if (meta?.virtual) {
28
- return this.findVirtual(entityName, where, options);
29
- }
24
+ async createQueryBuilderFromOptions(meta, where, options = {}) {
25
+ const connectionType = this.resolveConnectionType({ ctx: options.ctx, connectionType: options.connectionType });
30
26
  const populate = this.autoJoinOneToOneOwner(meta, options.populate, options.fields);
31
27
  const joinedProps = this.joinedProps(meta, populate, options);
32
- const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false, options.logging);
28
+ const qb = this.createQueryBuilder(meta.className, options.ctx, connectionType, false, options.logging, undefined, options.em);
33
29
  const fields = this.buildFields(meta, populate, joinedProps, qb, qb.alias, options);
34
30
  const orderBy = this.buildOrderBy(qb, meta, populate, options);
35
31
  const populateWhere = this.buildPopulateWhere(meta, joinedProps, options);
@@ -66,8 +62,17 @@ export class AbstractSqlDriver extends DatabaseDriver {
66
62
  if (options.em) {
67
63
  await qb.applyJoinedFilters(options.em, options.filters);
68
64
  }
65
+ return qb;
66
+ }
67
+ async find(entityName, where, options = {}) {
68
+ options = { populate: [], orderBy: [], ...options };
69
+ const meta = this.metadata.find(entityName);
70
+ if (meta?.virtual) {
71
+ return this.findVirtual(entityName, where, options);
72
+ }
73
+ const qb = await this.createQueryBuilderFromOptions(meta, where, options);
69
74
  const result = await this.rethrow(qb.execute('all'));
70
- if (isCursorPagination && !first && !!last) {
75
+ if (options.last && !options.first) {
71
76
  result.reverse();
72
77
  }
73
78
  return result;
@@ -107,7 +112,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
107
112
  }
108
113
  async findFromVirtual(entityName, where, options, type) {
109
114
  const meta = this.metadata.get(entityName);
110
- /* v8 ignore next 3 */
115
+ /* v8 ignore next */
111
116
  if (!meta.expression) {
112
117
  return type === QueryType.SELECT ? [] : 0;
113
118
  }
@@ -130,32 +135,46 @@ export class AbstractSqlDriver extends DatabaseDriver {
130
135
  /* v8 ignore next */
131
136
  return res;
132
137
  }
133
- async wrapVirtualExpressionInSubquery(meta, expression, where, options, type) {
134
- const qb = this.createQueryBuilder(meta.className, options?.ctx, options.connectionType, options.convertCustomTypes, options.logging)
135
- .indexHint(options.indexHint)
136
- .comment(options.comments)
137
- .hintComment(options.hintComments);
138
- qb.where(where);
139
- const { first, last, before, after } = options;
140
- const isCursorPagination = [first, last, before, after].some(v => v != null);
141
- if (type !== QueryType.COUNT) {
142
- if (options.orderBy) {
143
- if (isCursorPagination) {
144
- const { orderBy: newOrderBy, where } = this.processCursorOptions(meta, options, options.orderBy);
145
- qb.andWhere(where).orderBy(newOrderBy);
146
- }
147
- else {
148
- qb.orderBy(options.orderBy);
149
- }
150
- }
151
- qb.limit(options?.limit, options?.offset);
138
+ async *streamFromVirtual(entityName, where, options) {
139
+ const meta = this.metadata.get(entityName);
140
+ /* v8 ignore next */
141
+ if (!meta.expression) {
142
+ return;
152
143
  }
153
- const native = qb.getNativeQuery(false).clear('select');
154
- if (type === QueryType.COUNT) {
155
- native.count();
144
+ if (typeof meta.expression === 'string') {
145
+ yield* this.wrapVirtualExpressionInSubqueryStream(meta, meta.expression, where, options, QueryType.SELECT);
146
+ return;
156
147
  }
157
- else { // select
158
- native.select('*');
148
+ const em = this.createEntityManager();
149
+ em.setTransactionContext(options.ctx);
150
+ const res = meta.expression(em, where, options, true);
151
+ if (typeof res === 'string') {
152
+ yield* this.wrapVirtualExpressionInSubqueryStream(meta, res, where, options, QueryType.SELECT);
153
+ return;
154
+ }
155
+ if (res instanceof QueryBuilder) {
156
+ yield* this.wrapVirtualExpressionInSubqueryStream(meta, res.getFormattedQuery(), where, options, QueryType.SELECT);
157
+ return;
158
+ }
159
+ if (res instanceof RawQueryFragment) {
160
+ const expr = this.platform.formatQuery(res.sql, res.params);
161
+ yield* this.wrapVirtualExpressionInSubqueryStream(meta, expr, where, options, QueryType.SELECT);
162
+ return;
163
+ }
164
+ /* v8 ignore next */
165
+ yield* res;
166
+ }
167
+ async wrapVirtualExpressionInSubquery(meta, expression, where, options, type) {
168
+ const qb = await this.createQueryBuilderFromOptions(meta, where, options);
169
+ qb.setFlag(QueryFlag.DISABLE_PAGINATE);
170
+ const isCursorPagination = [options.first, options.last, options.before, options.after].some(v => v != null);
171
+ const native = qb.getNativeQuery(false);
172
+ if (type === QueryType.COUNT) {
173
+ native
174
+ .clear('select')
175
+ .clear('limit')
176
+ .clear('offset')
177
+ .count();
159
178
  }
160
179
  native.from(raw(`(${expression}) as ${this.platform.quoteIdentifier(qb.alias)}`));
161
180
  const query = native.compile();
@@ -163,14 +182,26 @@ export class AbstractSqlDriver extends DatabaseDriver {
163
182
  if (type === QueryType.COUNT) {
164
183
  return res[0].count;
165
184
  }
166
- if (isCursorPagination && !first && !!last) {
185
+ if (isCursorPagination && !options.first && !!options.last) {
167
186
  res.reverse();
168
187
  }
169
188
  return res.map(row => this.mapResult(row, meta));
170
189
  }
190
+ async *wrapVirtualExpressionInSubqueryStream(meta, expression, where, options, type) {
191
+ const qb = await this.createQueryBuilderFromOptions(meta, where, options);
192
+ qb.unsetFlag(QueryFlag.DISABLE_PAGINATE);
193
+ const native = qb.getNativeQuery(false);
194
+ native.from(raw(`(${expression}) as ${this.platform.quoteIdentifier(qb.alias)}`));
195
+ const query = native.compile();
196
+ const connectionType = this.resolveConnectionType({ ctx: options.ctx, connectionType: options.connectionType });
197
+ const res = this.getConnection(connectionType).stream(query.sql, query.params, options.ctx, options.loggerContext);
198
+ for await (const row of res) {
199
+ yield this.mapResult(row, meta);
200
+ }
201
+ }
171
202
  mapResult(result, meta, populate = [], qb, map = {}) {
172
203
  const ret = super.mapResult(result, meta);
173
- /* v8 ignore next 3 */
204
+ /* v8 ignore next */
174
205
  if (!ret) {
175
206
  return null;
176
207
  }
@@ -185,7 +216,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
185
216
  joinedProps.forEach(hint => {
186
217
  const [propName, ref] = hint.field.split(':', 2);
187
218
  const prop = meta.properties[propName];
188
- /* v8 ignore next 3 */
219
+ /* v8 ignore next */
189
220
  if (!prop) {
190
221
  return;
191
222
  }
@@ -199,7 +230,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
199
230
  path += '[pivot]';
200
231
  }
201
232
  const relationAlias = qb.getAliasForJoinPath(path, { matchPopulateJoins: true });
202
- /* v8 ignore next 3 */
233
+ /* v8 ignore next */
203
234
  if (!relationAlias) {
204
235
  return;
205
236
  }
@@ -221,9 +252,13 @@ export class AbstractSqlDriver extends DatabaseDriver {
221
252
  }
222
253
  return;
223
254
  }
255
+ const mapToPk = !!(ref || prop.mapToPk);
256
+ const targetProps = mapToPk
257
+ ? meta2.getPrimaryProps()
258
+ : meta2.props.filter(prop => this.platform.shouldHaveColumn(prop, hint.children || []));
224
259
  // If the primary key value for the relation is null, we know we haven't joined to anything
225
260
  // and therefore we don't return any record (since all values would be null)
226
- const hasPK = meta2.primaryKeys.every(pk => meta2.properties[pk].fieldNames.every(name => {
261
+ const hasPK = meta2.getPrimaryProps().every(pk => pk.fieldNames.every(name => {
227
262
  return root[`${relationAlias}__${name}`] != null;
228
263
  }));
229
264
  if (!hasPK) {
@@ -233,14 +268,18 @@ export class AbstractSqlDriver extends DatabaseDriver {
233
268
  if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
234
269
  result[prop.name] = null;
235
270
  }
271
+ for (const prop of targetProps) {
272
+ for (const name of prop.fieldNames) {
273
+ delete root[`${relationAlias}__${name}`];
274
+ }
275
+ }
236
276
  return;
237
277
  }
238
278
  let relationPojo = {};
239
279
  meta2.props
240
280
  .filter(prop => !ref && prop.persist === false && prop.fieldNames)
241
- .filter(prop => !prop.lazy || populate.some(p => p.field === prop.name || p.all))
242
281
  .forEach(prop => {
243
- /* v8 ignore next 3 */
282
+ /* v8 ignore next */
244
283
  if (prop.fieldNames.length > 1) { // composite keys
245
284
  relationPojo[prop.name] = prop.fieldNames.map(name => root[`${relationAlias}__${name}`]);
246
285
  }
@@ -249,10 +288,6 @@ export class AbstractSqlDriver extends DatabaseDriver {
249
288
  relationPojo[prop.name] = root[alias];
250
289
  }
251
290
  });
252
- const mapToPk = !!(ref || prop.mapToPk);
253
- const targetProps = mapToPk
254
- ? meta2.getPrimaryProps()
255
- : meta2.props.filter(prop => this.platform.shouldHaveColumn(prop, hint.children || []));
256
291
  const tz = this.platform.getTimezone();
257
292
  for (const prop of targetProps) {
258
293
  if (prop.fieldNames.every(name => typeof root[`${relationAlias}__${name}`] === 'undefined')) {
@@ -293,7 +328,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
293
328
  // properties can be mapped to multiple places, e.g. when sharing a column in multiple FKs,
294
329
  // so we need to delete them after everything is mapped from given level
295
330
  for (const prop of targetProps) {
296
- prop.fieldNames.map(name => delete root[`${relationAlias}__${name}`]);
331
+ for (const name of prop.fieldNames) {
332
+ delete root[`${relationAlias}__${name}`];
333
+ }
297
334
  }
298
335
  if (mapToPk) {
299
336
  const tmp = Object.values(relationPojo);
@@ -312,15 +349,20 @@ export class AbstractSqlDriver extends DatabaseDriver {
312
349
  });
313
350
  }
314
351
  async count(entityName, where, options = {}) {
315
- const meta = this.metadata.find(entityName);
316
- if (meta?.virtual) {
352
+ const meta = this.metadata.get(entityName);
353
+ if (meta.virtual) {
317
354
  return this.countVirtual(entityName, where, options);
318
355
  }
319
- const joinedProps = meta ? this.joinedProps(meta, options.populate ?? []) : [];
320
- const populateWhere = meta ? this.buildPopulateWhere(meta, joinedProps, options) : undefined;
321
- const populate = options.populate ?? [];
322
- const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false, options.logging)
323
- .indexHint(options.indexHint)
356
+ options = { populate: [], ...options };
357
+ const populate = options.populate;
358
+ const joinedProps = this.joinedProps(meta, populate, options);
359
+ const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false, options.logging);
360
+ const populateWhere = this.buildPopulateWhere(meta, joinedProps, options);
361
+ if (meta && !Utils.isEmpty(populate)) {
362
+ this.buildFields(meta, populate, joinedProps, qb, qb.alias, options);
363
+ }
364
+ qb.__populateWhere = options._populateWhere;
365
+ qb.indexHint(options.indexHint)
324
366
  .comment(options.comments)
325
367
  .hintComment(options.hintComments)
326
368
  .groupBy(options.groupBy)
@@ -328,8 +370,8 @@ export class AbstractSqlDriver extends DatabaseDriver {
328
370
  .populate(populate, joinedProps.length > 0 ? populateWhere : undefined, joinedProps.length > 0 ? options.populateFilter : undefined)
329
371
  .withSchema(this.getSchemaName(meta, options))
330
372
  .where(where);
331
- if (meta && !Utils.isEmpty(populate)) {
332
- this.buildFields(meta, populate, joinedProps, qb, qb.alias, options, true);
373
+ if (options.em) {
374
+ await qb.applyJoinedFilters(options.em, options.filters);
333
375
  }
334
376
  return this.rethrow(qb.getCount());
335
377
  }
@@ -338,7 +380,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
338
380
  const meta = this.metadata.find(entityName);
339
381
  const collections = this.extractManyToMany(entityName, data);
340
382
  const pks = meta?.primaryKeys ?? [this.config.getNamingStrategy().referenceColumnName()];
341
- const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes).withSchema(this.getSchemaName(meta, options));
383
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes, options.loggerContext).withSchema(this.getSchemaName(meta, options));
342
384
  const res = await this.rethrow(qb.insert(data).execute('run', false));
343
385
  res.row = res.row || {};
344
386
  let pk;
@@ -386,7 +428,12 @@ export class AbstractSqlDriver extends DatabaseDriver {
386
428
  sql += ' ' + data.map(() => `select null as ${this.platform.quoteIdentifier(pks[0])}`).join(' union all ');
387
429
  }
388
430
  const addParams = (prop, row) => {
389
- let value = row[prop.name] ?? prop.default;
431
+ const rowValue = row[prop.name];
432
+ if (prop.nullable && rowValue === null) {
433
+ params.push(null);
434
+ return;
435
+ }
436
+ let value = rowValue ?? prop.default;
390
437
  if (prop.kind === ReferenceKind.EMBEDDED && prop.object) {
391
438
  if (prop.array && value) {
392
439
  value = this.platform.cloneEmbeddable(value);
@@ -399,14 +446,14 @@ export class AbstractSqlDriver extends DatabaseDriver {
399
446
  value = this.mapDataToFieldNames(value, false, prop.embeddedProps, options.convertCustomTypes);
400
447
  }
401
448
  }
402
- if (options.convertCustomTypes && prop.customType) {
403
- params.push(prop.customType.convertToDatabaseValue(value, this.platform, { key: prop.name, mode: 'query-data' }));
404
- return;
405
- }
406
449
  if (typeof value === 'undefined' && this.platform.usesDefaultKeyword()) {
407
450
  params.push(raw('default'));
408
451
  return;
409
452
  }
453
+ if (options.convertCustomTypes && prop.customType) {
454
+ params.push(prop.customType.convertToDatabaseValue(value, this.platform, { key: prop.name, mode: 'query-data' }));
455
+ return;
456
+ }
410
457
  params.push(value);
411
458
  };
412
459
  if (fields.length > 0 || this.platform.usesDefaultKeyword()) {
@@ -465,9 +512,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
465
512
  if (transform) {
466
513
  sql = transform(sql);
467
514
  }
468
- const res = await this.execute(sql, params, 'run', options.ctx);
515
+ const res = await this.execute(sql, params, 'run', options.ctx, options.loggerContext);
469
516
  let pk;
470
- /* v8 ignore next 3 */
517
+ /* v8 ignore next */
471
518
  if (pks.length > 1) { // owner has composite pk
472
519
  pk = data.map(d => Utils.getPrimaryKeyCond(d, pks));
473
520
  }
@@ -493,7 +540,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
493
540
  where = { [meta?.primaryKeys[0] ?? pks[0]]: where };
494
541
  }
495
542
  if (Utils.hasObjectKeys(data)) {
496
- const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes)
543
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes, options.loggerContext)
497
544
  .withSchema(this.getSchemaName(meta, options));
498
545
  if (options.upsert) {
499
546
  /* v8 ignore next */
@@ -532,7 +579,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
532
579
  const meta = this.metadata.get(entityName);
533
580
  if (options.upsert) {
534
581
  const uniqueFields = options.onConflictFields ?? (Utils.isPlainObject(where[0]) ? Object.keys(where[0]).flatMap(key => Utils.splitPrimaryKeys(key)) : meta.primaryKeys);
535
- const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes).withSchema(this.getSchemaName(meta, options));
582
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes, options.loggerContext).withSchema(this.getSchemaName(meta, options));
536
583
  const returning = getOnConflictReturningFields(meta, data[0], uniqueFields, options);
537
584
  qb.insert(data)
538
585
  .onConflict(uniqueFields)
@@ -645,7 +692,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
645
692
  /* v8 ignore next */
646
693
  sql += returningFields.length > 0 ? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}` : '';
647
694
  }
648
- const res = await this.rethrow(this.execute(sql, params, 'run', options.ctx));
695
+ const res = await this.rethrow(this.execute(sql, params, 'run', options.ctx, options.loggerContext));
649
696
  for (let i = 0; i < collections.length; i++) {
650
697
  await this.processManyToMany(meta, where[i], collections[i], false, options);
651
698
  }
@@ -657,7 +704,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
657
704
  if (Utils.isPrimaryKey(where) && pks.length === 1) {
658
705
  where = { [pks[0]]: where };
659
706
  }
660
- const qb = this.createQueryBuilder(entityName, options.ctx, 'write', false).delete(where).withSchema(this.getSchemaName(meta, options));
707
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', false, options.loggerContext).delete(where).withSchema(this.getSchemaName(meta, options));
661
708
  return this.rethrow(qb.execute('run', false));
662
709
  }
663
710
  /**
@@ -721,7 +768,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
721
768
  await this.rethrow(query.execute());
722
769
  continue;
723
770
  }
724
- /* v8 ignore next 5 */
771
+ /* v8 ignore next */
725
772
  const query = qb.update({ [coll.property.mappedBy]: pks })
726
773
  .where({ [cols.join(Utils.PK_SEPARATOR)]: { $in: insertDiff } });
727
774
  await this.rethrow(query.execute());
@@ -744,126 +791,96 @@ export class AbstractSqlDriver extends DatabaseDriver {
744
791
  schema = this.config.get('schema');
745
792
  }
746
793
  const tableName = `${schema ?? '_'}.${pivotMeta.tableName}`;
747
- const persister = groups[tableName] ??= new PivotCollectionPersister(pivotMeta, this, options?.ctx, schema);
748
- persister.enqueueUpdate(coll.property, insertDiff, deleteDiff, pks);
794
+ const persister = groups[tableName] ??= new PivotCollectionPersister(pivotMeta, this, options?.ctx, schema, options?.loggerContext);
795
+ persister.enqueueUpdate(coll.property, insertDiff, deleteDiff, pks, coll.isInitialized());
749
796
  }
750
797
  for (const persister of Utils.values(groups)) {
751
798
  await this.rethrow(persister.execute());
752
799
  }
753
800
  }
754
801
  async loadFromPivotTable(prop, owners, where = {}, orderBy, ctx, options, pivotJoin) {
802
+ if (owners.length === 0) {
803
+ return {};
804
+ }
755
805
  const pivotMeta = this.metadata.find(prop.pivotEntity);
756
806
  const pivotProp1 = pivotMeta.relations[prop.owner ? 1 : 0];
757
807
  const pivotProp2 = pivotMeta.relations[prop.owner ? 0 : 1];
758
808
  const ownerMeta = this.metadata.find(pivotProp2.type);
759
- options = { ...options };
760
- const qb = this.createQueryBuilder(prop.pivotEntity, ctx, options.connectionType, undefined, options?.logging)
761
- .withSchema(this.getSchemaName(pivotMeta, options))
762
- .indexHint(options.indexHint)
763
- .comment(options.comments)
764
- .hintComment(options.hintComments);
765
- const pivotAlias = qb.alias;
766
- const pivotKey = pivotProp2.joinColumns.map(column => `${pivotAlias}.${column}`).join(Utils.PK_SEPARATOR);
767
809
  const cond = {
768
- [pivotKey]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) },
810
+ [pivotProp2.name]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) },
769
811
  };
770
- /* v8 ignore next 3 */
771
- if (!Utils.isEmpty(where) && Object.keys(where).every(k => Utils.isOperator(k, false))) {
772
- where = cond;
773
- }
774
- else {
775
- where = { ...where, ...cond };
776
- }
777
- orderBy = this.getPivotOrderBy(prop, pivotProp1, pivotAlias, orderBy);
778
- const populate = this.autoJoinOneToOneOwner(prop.targetMeta, []);
779
- const fields = [];
780
- const k1 = !prop.owner ? 'joinColumns' : 'inverseJoinColumns';
781
- const k2 = prop.owner ? 'joinColumns' : 'inverseJoinColumns';
782
- const cols = [
783
- ...prop[k1].map(col => `${pivotAlias}.${col} as fk__${col}`),
784
- ...prop[k2].map(col => `${pivotAlias}.${col} as fk__${col}`),
785
- ];
786
- fields.push(...cols);
787
- if (!pivotJoin) {
788
- const targetAlias = qb.getNextAlias(prop.targetMeta.tableName);
789
- const targetSchema = this.getSchemaName(prop.targetMeta, options) ?? this.platform.getDefaultSchemaName();
790
- qb.innerJoin(pivotProp1.name, targetAlias, {}, targetSchema);
791
- const targetFields = this.buildFields(prop.targetMeta, (options.populate ?? []), [], qb, targetAlias, options);
792
- const additionalFields = [];
793
- for (const field of targetFields) {
794
- const f = field.toString();
795
- additionalFields.push(f.includes('.') ? field : `${targetAlias}.${f}`);
796
- if (RawQueryFragment.isKnownFragment(field)) {
797
- qb.rawFragments.add(f);
798
- }
799
- }
800
- fields.unshift(...additionalFields);
801
- // we need to handle 1:1 owner auto-joins explicitly, as the QB type is the pivot table, not the target
802
- populate.forEach(hint => {
803
- const alias = qb.getNextAlias(prop.targetMeta.tableName);
804
- qb.leftJoin(`${targetAlias}.${hint.field}`, alias);
805
- // eslint-disable-next-line dot-notation
806
- for (const join of Object.values(qb['_joins'])) {
807
- const [propName] = hint.field.split(':', 2);
808
- if (join.alias === alias && join.prop.name === propName) {
809
- fields.push(...qb.helper.mapJoinColumns(qb.type, join));
810
- }
811
- }
812
- });
813
- }
814
- qb.select(fields)
815
- .where({ [pivotProp1.name]: where })
816
- .orderBy(orderBy)
817
- .setLockMode(options.lockMode, options.lockTableAliases);
818
- if (owners.length === 1 && (options.offset != null || options.limit != null)) {
819
- qb.limit(options.limit, options.offset);
820
- }
821
- const res = owners.length ? await this.rethrow(qb.execute('all', { mergeResults: false, mapResults: false })) : [];
822
- const tmp = {};
823
- const items = res.map((row) => {
824
- const root = super.mapResult(row, prop.targetMeta);
825
- this.mapJoinedProps(root, prop.targetMeta, populate, qb, root, tmp, pivotMeta.className + '.' + pivotProp1.name);
826
- return root;
812
+ if (!Utils.isEmpty(where)) {
813
+ cond[pivotProp1.name] = { ...where };
814
+ }
815
+ where = cond;
816
+ const populateField = pivotJoin ? `${pivotProp1.name}:ref` : pivotProp1.name;
817
+ const populate = this.autoJoinOneToOneOwner(prop.targetMeta, options?.populate ?? [], options?.fields);
818
+ const childFields = !Utils.isEmpty(options?.fields) ? options.fields.map(f => `${pivotProp1.name}.${f}`) : [];
819
+ const childExclude = !Utils.isEmpty(options?.exclude) ? options.exclude.map(f => `${pivotProp1.name}.${f}`) : [];
820
+ const fields = pivotJoin
821
+ ? [pivotProp1.name, pivotProp2.name]
822
+ : [pivotProp1.name, pivotProp2.name, ...childFields];
823
+ const res = await this.find(pivotMeta.className, where, {
824
+ ctx,
825
+ ...options,
826
+ fields,
827
+ exclude: childExclude,
828
+ orderBy: this.getPivotOrderBy(prop, pivotProp1, orderBy, options?.orderBy),
829
+ populate: [{ field: populateField, strategy: LoadStrategy.JOINED, joinType: JoinType.innerJoin, children: populate }],
830
+ populateWhere: undefined,
831
+ // @ts-ignore
832
+ _populateWhere: 'infer',
833
+ populateFilter: !Utils.isEmpty(options?.populateFilter) ? { [pivotProp2.name]: options?.populateFilter } : undefined,
827
834
  });
828
- qb.clearRawFragmentsCache();
829
835
  const map = {};
830
- const pkProps = ownerMeta.getPrimaryProps();
831
836
  for (const owner of owners) {
832
- const key = Utils.getPrimaryKeyHash(prop.joinColumns.map((_col, idx) => {
833
- const pkProp = pkProps[idx];
834
- return pkProp.customType ? pkProp.customType.convertToJSValue(owner[idx], this.platform) : owner[idx];
835
- }));
837
+ const key = Utils.getPrimaryKeyHash(owner);
836
838
  map[key] = [];
837
839
  }
838
- for (const item of items) {
839
- const key = Utils.getPrimaryKeyHash(prop.joinColumns.map((col, idx) => {
840
- const pkProp = pkProps[idx];
841
- return pkProp.customType ? pkProp.customType.convertToJSValue(item[`fk__${col}`], this.platform) : item[`fk__${col}`];
842
- }));
843
- map[key].push(item);
844
- prop.joinColumns.forEach(col => delete item[`fk__${col}`]);
845
- prop.inverseJoinColumns.forEach((col, idx) => {
846
- Utils.renameKey(item, `fk__${col}`, prop.targetMeta.primaryKeys[idx]);
847
- });
840
+ for (const item of res) {
841
+ const key = Utils.getPrimaryKeyHash(Utils.asArray(item[pivotProp2.name]));
842
+ map[key].push(item[pivotProp1.name]);
848
843
  }
849
844
  return map;
850
845
  }
851
- getPivotOrderBy(prop, pivotProp, pivotAlias, orderBy) {
852
- // FIXME this is ignoring the rest of the array items
846
+ getPivotOrderBy(prop, pivotProp, orderBy, parentOrderBy) {
853
847
  if (!Utils.isEmpty(orderBy)) {
854
- return [{ [pivotProp.name]: Utils.asArray(orderBy)[0] }];
848
+ return Utils.asArray(orderBy).map(o => ({ [pivotProp.name]: o }));
849
+ }
850
+ if (prop.kind === ReferenceKind.MANY_TO_MANY && Utils.asArray(parentOrderBy).some(o => o[prop.name])) {
851
+ return Utils.asArray(parentOrderBy)
852
+ .filter(o => o[prop.name])
853
+ .map(o => ({ [pivotProp.name]: o[prop.name] }));
855
854
  }
856
855
  if (!Utils.isEmpty(prop.orderBy)) {
857
- return [{ [pivotProp.name]: Utils.asArray(prop.orderBy)[0] }];
856
+ return Utils.asArray(prop.orderBy).map(o => ({ [pivotProp.name]: o }));
858
857
  }
859
858
  if (prop.fixedOrder) {
860
- return [{ [`${pivotAlias}.${prop.fixedOrderColumn}`]: QueryOrder.ASC }];
859
+ return [{ [prop.fixedOrderColumn]: QueryOrder.ASC }];
861
860
  }
862
861
  return [];
863
862
  }
864
863
  async execute(query, params = [], method = 'all', ctx, loggerContext) {
865
864
  return this.rethrow(this.connection.execute(query, params, method, ctx, loggerContext));
866
865
  }
866
+ async *stream(entityName, where, options) {
867
+ options = { populate: [], orderBy: [], ...options };
868
+ const meta = this.metadata.find(entityName);
869
+ if (meta?.virtual) {
870
+ yield* this.streamFromVirtual(entityName, where, options);
871
+ return;
872
+ }
873
+ const qb = await this.createQueryBuilderFromOptions(meta, where, options);
874
+ try {
875
+ const result = qb.stream(options);
876
+ for await (const item of result) {
877
+ yield item;
878
+ }
879
+ }
880
+ catch (e) {
881
+ throw this.convertException(e);
882
+ }
883
+ }
867
884
  /**
868
885
  * 1:1 owner side needs to be marked for population so QB auto-joins the owner id
869
886
  */
@@ -875,7 +892,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
875
892
  const toPopulate = meta.relations
876
893
  .filter(prop => prop.kind === ReferenceKind.ONE_TO_ONE && !prop.owner && !prop.lazy && !relationsToPopulate.includes(prop.name))
877
894
  .filter(prop => fields.length === 0 || fields.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
878
- .map(prop => ({ field: `${prop.name}:ref`, strategy: prop.strategy }));
895
+ .map(prop => ({ field: `${prop.name}:ref`, strategy: LoadStrategy.JOINED }));
879
896
  return [...populate, ...toPopulate];
880
897
  }
881
898
  /**
@@ -885,14 +902,15 @@ export class AbstractSqlDriver extends DatabaseDriver {
885
902
  return populate.filter(hint => {
886
903
  const [propName, ref] = hint.field.split(':', 2);
887
904
  const prop = meta.properties[propName] || {};
888
- if (hint.filter && hint.strategy === LoadStrategy.JOINED) {
905
+ const strategy = getLoadingStrategy(hint.strategy || prop.strategy || options?.strategy || this.config.get('loadStrategy'), prop.kind);
906
+ if (ref && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind)) {
889
907
  return true;
890
908
  }
891
909
  // skip redundant joins for 1:1 owner population hints when using `mapToPk`
892
910
  if (prop.kind === ReferenceKind.ONE_TO_ONE && prop.mapToPk && prop.owner) {
893
911
  return false;
894
912
  }
895
- if ((options?.strategy || hint.strategy || prop.strategy || this.config.get('loadStrategy')) !== LoadStrategy.JOINED) {
913
+ if (strategy !== LoadStrategy.JOINED) {
896
914
  // force joined strategy for explicit 1:1 owner populate hint as it would require a join anyway
897
915
  return prop.kind === ReferenceKind.ONE_TO_ONE && !prop.owner;
898
916
  }
@@ -903,31 +921,26 @@ export class AbstractSqlDriver extends DatabaseDriver {
903
921
  * @internal
904
922
  */
905
923
  mergeJoinedResult(rawResults, meta, joinedProps) {
924
+ if (rawResults.length <= 1) {
925
+ return rawResults;
926
+ }
906
927
  const res = [];
907
928
  const map = {};
929
+ const collectionsToMerge = {};
930
+ const hints = joinedProps.map(hint => {
931
+ const [propName, ref] = hint.field.split(':', 2);
932
+ return { propName, ref, children: hint.children };
933
+ });
908
934
  for (const item of rawResults) {
909
935
  const pk = Utils.getCompositeKeyHash(item, meta);
910
936
  if (map[pk]) {
911
- for (const hint of joinedProps) {
912
- const [propName, ref] = hint.field.split(':', 2);
913
- const prop = meta.properties[propName];
937
+ for (const { propName } of hints) {
914
938
  if (!item[propName]) {
915
939
  continue;
916
940
  }
917
- if ([ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind) && ref) {
918
- map[pk][propName] = [...map[pk][propName], ...item[propName]];
919
- continue;
920
- }
921
- switch (prop.kind) {
922
- case ReferenceKind.ONE_TO_MANY:
923
- case ReferenceKind.MANY_TO_MANY:
924
- map[pk][propName] = this.mergeJoinedResult([...map[pk][propName], ...item[propName]], prop.targetMeta, hint.children ?? []);
925
- break;
926
- case ReferenceKind.MANY_TO_ONE:
927
- case ReferenceKind.ONE_TO_ONE:
928
- map[pk][propName] = this.mergeJoinedResult([map[pk][propName], item[propName]], prop.targetMeta, hint.children ?? [])[0];
929
- break;
930
- }
941
+ collectionsToMerge[pk] ??= {};
942
+ collectionsToMerge[pk][propName] ??= [map[pk][propName]];
943
+ collectionsToMerge[pk][propName].push(item[propName]);
931
944
  }
932
945
  }
933
946
  else {
@@ -935,37 +948,59 @@ export class AbstractSqlDriver extends DatabaseDriver {
935
948
  res.push(item);
936
949
  }
937
950
  }
951
+ for (const pk in collectionsToMerge) {
952
+ const entity = map[pk];
953
+ const collections = collectionsToMerge[pk];
954
+ for (const { propName, ref, children } of hints) {
955
+ if (!collections[propName]) {
956
+ continue;
957
+ }
958
+ const prop = meta.properties[propName];
959
+ const items = collections[propName].flat();
960
+ if ([ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind) && ref) {
961
+ entity[propName] = items;
962
+ continue;
963
+ }
964
+ switch (prop.kind) {
965
+ case ReferenceKind.ONE_TO_MANY:
966
+ case ReferenceKind.MANY_TO_MANY:
967
+ entity[propName] = this.mergeJoinedResult(items, prop.targetMeta, children ?? []);
968
+ break;
969
+ case ReferenceKind.MANY_TO_ONE:
970
+ case ReferenceKind.ONE_TO_ONE:
971
+ entity[propName] = this.mergeJoinedResult(items, prop.targetMeta, children ?? [])[0];
972
+ break;
973
+ }
974
+ }
975
+ }
938
976
  return res;
939
977
  }
978
+ shouldHaveColumn(meta, prop, populate, fields, exclude) {
979
+ if (!this.platform.shouldHaveColumn(prop, populate, exclude)) {
980
+ return false;
981
+ }
982
+ if (!fields || fields.includes('*') || prop.primary || meta.root.discriminatorColumn === prop.name) {
983
+ return true;
984
+ }
985
+ return fields.some(f => f === prop.name || f.toString().startsWith(prop.name + '.'));
986
+ }
940
987
  getFieldsForJoinedLoad(qb, meta, options) {
941
988
  const fields = [];
942
989
  const populate = options.populate ?? [];
943
990
  const joinedProps = this.joinedProps(meta, populate, options);
944
- const shouldHaveColumn = (meta, prop, populate, fields) => {
945
- if (!this.platform.shouldHaveColumn(prop, populate, options.exclude)) {
946
- return false;
947
- }
948
- if (!fields || fields.includes('*') || prop.primary || meta.root.discriminatorColumn === prop.name) {
949
- return true;
950
- }
951
- return fields.some(f => f === prop.name || f.toString().startsWith(prop.name + '.'));
952
- };
953
991
  const populateWhereAll = options?._populateWhere === 'all' || Utils.isEmpty(options?._populateWhere);
954
992
  // root entity is already handled, skip that
955
993
  if (options.parentJoinPath) {
956
994
  // alias all fields in the primary table
957
995
  meta.props
958
- .filter(prop => shouldHaveColumn(meta, prop, populate, options.explicitFields))
959
- .forEach(prop => fields.push(...this.mapPropToFieldNames(qb, prop, options.parentTableAlias)));
996
+ .filter(prop => this.shouldHaveColumn(meta, prop, populate, options.explicitFields, options.exclude))
997
+ .forEach(prop => fields.push(...this.mapPropToFieldNames(qb, prop, options.parentTableAlias, options.explicitFields)));
960
998
  }
961
999
  for (const hint of joinedProps) {
962
1000
  const [propName, ref] = hint.field.split(':', 2);
963
1001
  const prop = meta.properties[propName];
964
1002
  // ignore ref joins of known FKs unless it's a filter hint
965
- if (ref && !hint.filter && (prop.kind === ReferenceKind.MANY_TO_ONE || (prop.kind === ReferenceKind.ONE_TO_ONE && !prop.owner))) {
966
- continue;
967
- }
968
- if (options.count && !options?.populateFilter?.[prop.name]) {
1003
+ if (ref && !hint.filter && (prop.kind === ReferenceKind.MANY_TO_ONE || (prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner))) {
969
1004
  continue;
970
1005
  }
971
1006
  const meta2 = this.metadata.find(prop.type);
@@ -976,11 +1011,14 @@ export class AbstractSqlDriver extends DatabaseDriver {
976
1011
  if (!options.parentJoinPath && populateWhereAll && !hint.filter && !path.startsWith('[populate]')) {
977
1012
  path = '[populate]' + path;
978
1013
  }
1014
+ const mandatoryToOneProperty = [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && !prop.nullable;
979
1015
  const joinType = pivotRefJoin
980
1016
  ? JoinType.pivotJoin
981
- : hint.filter && !prop.nullable
982
- ? JoinType.innerJoin
983
- : JoinType.leftJoin;
1017
+ : hint.joinType
1018
+ ? hint.joinType
1019
+ : (hint.filter && !prop.nullable) || mandatoryToOneProperty
1020
+ ? JoinType.innerJoin
1021
+ : JoinType.leftJoin;
984
1022
  qb.join(field, tableAlias, {}, joinType, path);
985
1023
  if (pivotRefJoin) {
986
1024
  fields.push(...prop.joinColumns.map(col => qb.helper.mapper(`${tableAlias}.${col}`, qb.type, undefined, `${tableAlias}__${col}`)), ...prop.inverseJoinColumns.map(col => qb.helper.mapper(`${tableAlias}.${col}`, qb.type, undefined, `${tableAlias}__${col}`)));
@@ -1012,7 +1050,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1012
1050
  parentJoinPath: path,
1013
1051
  }));
1014
1052
  }
1015
- else if (hint.filter || prop.mapToPk) {
1053
+ else if (hint.filter || prop.mapToPk || (ref && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind))) {
1016
1054
  fields.push(...prop.referencedColumnNames.map(col => qb.helper.mapper(`${tableAlias}.${col}`, qb.type, undefined, `${tableAlias}__${col}`)));
1017
1055
  }
1018
1056
  }
@@ -1021,9 +1059,18 @@ export class AbstractSqlDriver extends DatabaseDriver {
1021
1059
  /**
1022
1060
  * @internal
1023
1061
  */
1024
- mapPropToFieldNames(qb, prop, tableAlias) {
1062
+ mapPropToFieldNames(qb, prop, tableAlias, explicitFields) {
1063
+ if (prop.kind === ReferenceKind.EMBEDDED && !prop.object) {
1064
+ return Object.entries(prop.embeddedProps).flatMap(([name, childProp]) => {
1065
+ const childFields = explicitFields ? Utils.extractChildElements(explicitFields, prop.name) : [];
1066
+ if (!this.shouldHaveColumn(prop.targetMeta, { ...childProp, name }, [], childFields.length > 0 ? childFields : undefined)) {
1067
+ return [];
1068
+ }
1069
+ return this.mapPropToFieldNames(qb, childProp, tableAlias, childFields);
1070
+ });
1071
+ }
1025
1072
  const aliased = this.platform.quoteIdentifier(`${tableAlias}__${prop.fieldNames[0]}`);
1026
- if (prop.customTypes?.some(type => type?.convertToJSValueSQL)) {
1073
+ if (prop.customTypes?.some(type => !!type?.convertToJSValueSQL)) {
1027
1074
  return prop.fieldNames.map((col, idx) => {
1028
1075
  if (!prop.customTypes[idx]?.convertToJSValueSQL) {
1029
1076
  return col;
@@ -1087,7 +1134,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1087
1134
  for (const prop of meta.relations) {
1088
1135
  if (collections[prop.name]) {
1089
1136
  const pivotMeta = this.metadata.find(prop.pivotEntity);
1090
- const persister = new PivotCollectionPersister(pivotMeta, this, options?.ctx, options?.schema);
1137
+ const persister = new PivotCollectionPersister(pivotMeta, this, options?.ctx, options?.schema, options?.loggerContext);
1091
1138
  persister.enqueueUpdate(prop, collections[prop.name], clear, pks);
1092
1139
  await this.rethrow(persister.execute());
1093
1140
  }
@@ -1154,7 +1201,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1154
1201
  let path = parentPath;
1155
1202
  const meta2 = this.metadata.find(prop.type);
1156
1203
  const childOrder = orderHint[prop.name];
1157
- if (![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) || !prop.owner || Utils.isPlainObject(childOrder)) {
1204
+ if (prop.kind !== ReferenceKind.SCALAR && (![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) || !prop.owner || Utils.isPlainObject(childOrder))) {
1158
1205
  path += `.${propName}`;
1159
1206
  }
1160
1207
  if (prop.kind === ReferenceKind.MANY_TO_MANY && typeof childOrder !== 'object') {
@@ -1162,10 +1209,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
1162
1209
  }
1163
1210
  const join = qb.getJoinForPath(path, { matchPopulateJoins: true });
1164
1211
  const propAlias = qb.getAliasForJoinPath(join ?? path, { matchPopulateJoins: true }) ?? parentAlias;
1165
- if (!join && parentAlias === qb.alias) {
1212
+ if (!join) {
1166
1213
  continue;
1167
1214
  }
1168
- if (![ReferenceKind.SCALAR, ReferenceKind.EMBEDDED].includes(prop.kind) && typeof childOrder === 'object') {
1215
+ if (join && ![ReferenceKind.SCALAR, ReferenceKind.EMBEDDED].includes(prop.kind) && typeof childOrder === 'object') {
1169
1216
  const children = this.buildPopulateOrderBy(qb, meta2, Utils.asArray(childOrder), path, explicit, propAlias);
1170
1217
  orderBy.push(...children);
1171
1218
  continue;
@@ -1243,7 +1290,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1243
1290
  }
1244
1291
  return ret;
1245
1292
  }
1246
- processField(meta, prop, field, ret, populate, joinedProps, qb) {
1293
+ processField(meta, prop, field, ret) {
1247
1294
  if (!prop || (prop.kind === ReferenceKind.ONE_TO_ONE && !prop.owner)) {
1248
1295
  return;
1249
1296
  }
@@ -1256,7 +1303,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1256
1303
  const top = parts.shift();
1257
1304
  for (const key of Object.keys(prop.embeddedProps)) {
1258
1305
  if (!top || key === top) {
1259
- this.processField(meta, prop.embeddedProps[key], parts.join('.'), ret, populate, joinedProps, qb);
1306
+ this.processField(meta, prop.embeddedProps[key], parts.join('.'), ret);
1260
1307
  }
1261
1308
  }
1262
1309
  return;
@@ -1266,16 +1313,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1266
1313
  }
1267
1314
  ret.push(prop.name);
1268
1315
  }
1269
- isPopulated(meta, prop, hint, name) {
1270
- if (hint.field === prop.name || hint.field === name || hint.all) {
1271
- return true;
1272
- }
1273
- if (prop.embedded && hint.children && meta.properties[prop.embedded[0]].name === hint.field) {
1274
- return hint.children.some(c => this.isPopulated(meta, prop, c, prop.embedded[1]));
1275
- }
1276
- return false;
1277
- }
1278
- buildFields(meta, populate, joinedProps, qb, alias, options, count = false) {
1316
+ buildFields(meta, populate, joinedProps, qb, alias, options) {
1279
1317
  const lazyProps = meta.props.filter(prop => prop.lazy && !populate.some(p => this.isPopulated(meta, prop, p)));
1280
1318
  const hasLazyFormulas = meta.props.some(p => p.lazy && p.formula);
1281
1319
  const requiresSQLConversion = meta.props.some(p => p.customType?.convertToJSValueSQL && p.persist !== false);
@@ -1298,7 +1336,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1298
1336
  where: {},
1299
1337
  aliasMap: qb.getAliasMap(),
1300
1338
  });
1301
- this.processField(meta, prop, parts.join('.'), ret, populate, joinedProps, qb);
1339
+ this.processField(meta, prop, parts.join('.'), ret);
1302
1340
  }
1303
1341
  if (!options.fields.includes('*') && !options.fields.includes(`${qb.alias}.*`)) {
1304
1342
  ret.unshift(...meta.primaryKeys.filter(pk => !options.fields.includes(pk)));
@@ -1308,7 +1346,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1308
1346
  }
1309
1347
  }
1310
1348
  else if (!Utils.isEmpty(options.exclude) || lazyProps.some(p => !p.formula && (p.kind !== '1:1' || p.owner))) {
1311
- const props = meta.props.filter(prop => this.platform.shouldHaveColumn(prop, populate, options.exclude, false));
1349
+ const props = meta.props.filter(prop => this.platform.shouldHaveColumn(prop, populate, options.exclude, false, false));
1312
1350
  ret.push(...props.filter(p => !lazyProps.includes(p)).map(p => p.name));
1313
1351
  addFormulas = true;
1314
1352
  }
@@ -1320,16 +1358,19 @@ export class AbstractSqlDriver extends DatabaseDriver {
1320
1358
  ret.push('*');
1321
1359
  }
1322
1360
  if (ret.length > 0 && !hasExplicitFields && addFormulas) {
1323
- meta.props
1324
- .filter(prop => prop.formula && !lazyProps.includes(prop))
1325
- .forEach(prop => {
1326
- const a = this.platform.quoteIdentifier(alias);
1327
- const aliased = this.platform.quoteIdentifier(prop.fieldNames[0]);
1328
- ret.push(raw(`${prop.formula(a)} as ${aliased}`));
1329
- });
1330
- meta.props
1331
- .filter(prop => !prop.object && (prop.hasConvertToDatabaseValueSQL || prop.hasConvertToJSValueSQL))
1332
- .forEach(prop => ret.push(prop.name));
1361
+ for (const prop of meta.props) {
1362
+ if (lazyProps.includes(prop)) {
1363
+ continue;
1364
+ }
1365
+ if (prop.formula) {
1366
+ const a = this.platform.quoteIdentifier(alias);
1367
+ const aliased = this.platform.quoteIdentifier(prop.fieldNames[0]);
1368
+ ret.push(raw(`${prop.formula(a)} as ${aliased}`));
1369
+ }
1370
+ if (!prop.object && (prop.hasConvertToDatabaseValueSQL || prop.hasConvertToJSValueSQL)) {
1371
+ ret.push(prop.name);
1372
+ }
1373
+ }
1333
1374
  }
1334
1375
  // add joined relations after the root entity fields
1335
1376
  if (joinedProps.length > 0) {
@@ -1338,7 +1379,6 @@ export class AbstractSqlDriver extends DatabaseDriver {
1338
1379
  exclude: options.exclude,
1339
1380
  populate,
1340
1381
  parentTableAlias: alias,
1341
- count,
1342
1382
  ...options,
1343
1383
  }));
1344
1384
  }