@mikro-orm/knex 7.0.0-dev.6 → 7.0.0-dev.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/AbstractSqlConnection.d.ts +11 -5
  2. package/AbstractSqlConnection.js +79 -32
  3. package/AbstractSqlDriver.d.ts +14 -10
  4. package/AbstractSqlDriver.js +273 -222
  5. package/AbstractSqlPlatform.js +3 -3
  6. package/PivotCollectionPersister.d.ts +3 -2
  7. package/PivotCollectionPersister.js +6 -2
  8. package/README.md +3 -2
  9. package/SqlEntityManager.d.ts +9 -2
  10. package/SqlEntityManager.js +2 -2
  11. package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +2 -0
  12. package/dialects/mssql/MsSqlNativeQueryBuilder.js +43 -2
  13. package/dialects/mysql/MySqlPlatform.js +2 -1
  14. package/dialects/postgresql/PostgreSqlTableCompiler.d.ts +1 -0
  15. package/dialects/postgresql/PostgreSqlTableCompiler.js +1 -0
  16. package/dialects/sqlite/BaseSqliteConnection.d.ts +4 -2
  17. package/dialects/sqlite/BaseSqliteConnection.js +8 -5
  18. package/dialects/sqlite/SqliteSchemaHelper.js +1 -1
  19. package/index.d.ts +1 -1
  20. package/index.js +1 -1
  21. package/package.json +5 -5
  22. package/query/ArrayCriteriaNode.d.ts +1 -0
  23. package/query/ArrayCriteriaNode.js +3 -0
  24. package/query/CriteriaNode.d.ts +4 -2
  25. package/query/CriteriaNode.js +11 -6
  26. package/query/CriteriaNodeFactory.js +11 -6
  27. package/query/NativeQueryBuilder.js +1 -1
  28. package/query/ObjectCriteriaNode.d.ts +1 -0
  29. package/query/ObjectCriteriaNode.js +38 -7
  30. package/query/QueryBuilder.d.ts +67 -6
  31. package/query/QueryBuilder.js +195 -43
  32. package/query/QueryBuilderHelper.d.ts +1 -1
  33. package/query/QueryBuilderHelper.js +13 -6
  34. package/query/ScalarCriteriaNode.d.ts +3 -3
  35. package/query/ScalarCriteriaNode.js +7 -5
  36. package/query/index.d.ts +1 -0
  37. package/query/index.js +1 -0
  38. package/query/raw.d.ts +59 -0
  39. package/query/raw.js +68 -0
  40. package/query/rawKnex.d.ts +58 -0
  41. package/query/rawKnex.js +72 -0
  42. package/schema/DatabaseSchema.js +25 -4
  43. package/schema/DatabaseTable.d.ts +5 -4
  44. package/schema/DatabaseTable.js +80 -34
  45. package/schema/SchemaComparator.js +2 -2
  46. package/schema/SchemaHelper.d.ts +2 -0
  47. package/schema/SchemaHelper.js +8 -4
  48. package/schema/SqlSchemaGenerator.d.ts +9 -2
  49. package/schema/SqlSchemaGenerator.js +31 -11
  50. package/typings.d.ts +86 -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);
@@ -63,8 +59,20 @@ export class AbstractSqlDriver extends DatabaseDriver {
63
59
  if (options.lockMode) {
64
60
  qb.setLockMode(options.lockMode, options.lockTableAliases);
65
61
  }
62
+ if (options.em) {
63
+ await qb.applyJoinedFilters(options.em, options.filters);
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);
66
74
  const result = await this.rethrow(qb.execute('all'));
67
- if (isCursorPagination && !first && !!last) {
75
+ if (options.last && !options.first) {
68
76
  result.reverse();
69
77
  }
70
78
  return result;
@@ -124,35 +132,49 @@ export class AbstractSqlDriver extends DatabaseDriver {
124
132
  const expr = this.platform.formatQuery(res.sql, res.params);
125
133
  return this.wrapVirtualExpressionInSubquery(meta, expr, where, options, type);
126
134
  }
127
- /* v8 ignore next */
135
+ /* v8 ignore next 2 */
128
136
  return res;
129
137
  }
130
- async wrapVirtualExpressionInSubquery(meta, expression, where, options, type) {
131
- const qb = this.createQueryBuilder(meta.className, options?.ctx, options.connectionType, options.convertCustomTypes, options.logging)
132
- .indexHint(options.indexHint)
133
- .comment(options.comments)
134
- .hintComment(options.hintComments);
135
- qb.where(where);
136
- const { first, last, before, after } = options;
137
- const isCursorPagination = [first, last, before, after].some(v => v != null);
138
- if (type !== QueryType.COUNT) {
139
- if (options.orderBy) {
140
- if (isCursorPagination) {
141
- const { orderBy: newOrderBy, where } = this.processCursorOptions(meta, options, options.orderBy);
142
- qb.andWhere(where).orderBy(newOrderBy);
143
- }
144
- else {
145
- qb.orderBy(options.orderBy);
146
- }
147
- }
148
- qb.limit(options?.limit, options?.offset);
138
+ async *streamFromVirtual(entityName, where, options) {
139
+ const meta = this.metadata.get(entityName);
140
+ /* v8 ignore next 3 */
141
+ if (!meta.expression) {
142
+ return;
149
143
  }
150
- const native = qb.getNativeQuery(false).clear('select');
151
- if (type === QueryType.COUNT) {
152
- native.count();
144
+ if (typeof meta.expression === 'string') {
145
+ yield* this.wrapVirtualExpressionInSubqueryStream(meta, meta.expression, where, options, QueryType.SELECT);
146
+ return;
147
+ }
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;
153
154
  }
154
- else { // select
155
- native.select('*');
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 2 */
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();
156
178
  }
157
179
  native.from(raw(`(${expression}) as ${this.platform.quoteIdentifier(qb.alias)}`));
158
180
  const query = native.compile();
@@ -160,11 +182,23 @@ export class AbstractSqlDriver extends DatabaseDriver {
160
182
  if (type === QueryType.COUNT) {
161
183
  return res[0].count;
162
184
  }
163
- if (isCursorPagination && !first && !!last) {
185
+ if (isCursorPagination && !options.first && !!options.last) {
164
186
  res.reverse();
165
187
  }
166
188
  return res.map(row => this.mapResult(row, meta));
167
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
+ }
168
202
  mapResult(result, meta, populate = [], qb, map = {}) {
169
203
  const ret = super.mapResult(result, meta);
170
204
  /* v8 ignore next 3 */
@@ -218,9 +252,13 @@ export class AbstractSqlDriver extends DatabaseDriver {
218
252
  }
219
253
  return;
220
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 || []));
221
259
  // If the primary key value for the relation is null, we know we haven't joined to anything
222
260
  // and therefore we don't return any record (since all values would be null)
223
- const hasPK = meta2.primaryKeys.every(pk => meta2.properties[pk].fieldNames.every(name => {
261
+ const hasPK = meta2.getPrimaryProps().every(pk => pk.fieldNames.every(name => {
224
262
  return root[`${relationAlias}__${name}`] != null;
225
263
  }));
226
264
  if (!hasPK) {
@@ -230,12 +268,16 @@ export class AbstractSqlDriver extends DatabaseDriver {
230
268
  if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
231
269
  result[prop.name] = null;
232
270
  }
271
+ for (const prop of targetProps) {
272
+ for (const name of prop.fieldNames) {
273
+ delete root[`${relationAlias}__${name}`];
274
+ }
275
+ }
233
276
  return;
234
277
  }
235
278
  let relationPojo = {};
236
279
  meta2.props
237
280
  .filter(prop => !ref && prop.persist === false && prop.fieldNames)
238
- .filter(prop => !prop.lazy || populate.some(p => p.field === prop.name || p.all))
239
281
  .forEach(prop => {
240
282
  /* v8 ignore next 3 */
241
283
  if (prop.fieldNames.length > 1) { // composite keys
@@ -246,10 +288,6 @@ export class AbstractSqlDriver extends DatabaseDriver {
246
288
  relationPojo[prop.name] = root[alias];
247
289
  }
248
290
  });
249
- const mapToPk = !!(ref || prop.mapToPk);
250
- const targetProps = mapToPk
251
- ? meta2.getPrimaryProps()
252
- : meta2.props.filter(prop => this.platform.shouldHaveColumn(prop, hint.children || []));
253
291
  const tz = this.platform.getTimezone();
254
292
  for (const prop of targetProps) {
255
293
  if (prop.fieldNames.every(name => typeof root[`${relationAlias}__${name}`] === 'undefined')) {
@@ -290,7 +328,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
290
328
  // properties can be mapped to multiple places, e.g. when sharing a column in multiple FKs,
291
329
  // so we need to delete them after everything is mapped from given level
292
330
  for (const prop of targetProps) {
293
- prop.fieldNames.map(name => delete root[`${relationAlias}__${name}`]);
331
+ for (const name of prop.fieldNames) {
332
+ delete root[`${relationAlias}__${name}`];
333
+ }
294
334
  }
295
335
  if (mapToPk) {
296
336
  const tmp = Object.values(relationPojo);
@@ -309,15 +349,20 @@ export class AbstractSqlDriver extends DatabaseDriver {
309
349
  });
310
350
  }
311
351
  async count(entityName, where, options = {}) {
312
- const meta = this.metadata.find(entityName);
313
- if (meta?.virtual) {
352
+ const meta = this.metadata.get(entityName);
353
+ if (meta.virtual) {
314
354
  return this.countVirtual(entityName, where, options);
315
355
  }
316
- const joinedProps = meta ? this.joinedProps(meta, options.populate ?? []) : [];
317
- const populateWhere = meta ? this.buildPopulateWhere(meta, joinedProps, options) : undefined;
318
- const populate = options.populate ?? [];
319
- const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false, options.logging)
320
- .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)
321
366
  .comment(options.comments)
322
367
  .hintComment(options.hintComments)
323
368
  .groupBy(options.groupBy)
@@ -325,8 +370,8 @@ export class AbstractSqlDriver extends DatabaseDriver {
325
370
  .populate(populate, joinedProps.length > 0 ? populateWhere : undefined, joinedProps.length > 0 ? options.populateFilter : undefined)
326
371
  .withSchema(this.getSchemaName(meta, options))
327
372
  .where(where);
328
- if (meta && !Utils.isEmpty(populate)) {
329
- this.buildFields(meta, populate, joinedProps, qb, qb.alias, options, true);
373
+ if (options.em) {
374
+ await qb.applyJoinedFilters(options.em, options.filters);
330
375
  }
331
376
  return this.rethrow(qb.getCount());
332
377
  }
@@ -335,7 +380,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
335
380
  const meta = this.metadata.find(entityName);
336
381
  const collections = this.extractManyToMany(entityName, data);
337
382
  const pks = meta?.primaryKeys ?? [this.config.getNamingStrategy().referenceColumnName()];
338
- 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));
339
384
  const res = await this.rethrow(qb.insert(data).execute('run', false));
340
385
  res.row = res.row || {};
341
386
  let pk;
@@ -383,7 +428,12 @@ export class AbstractSqlDriver extends DatabaseDriver {
383
428
  sql += ' ' + data.map(() => `select null as ${this.platform.quoteIdentifier(pks[0])}`).join(' union all ');
384
429
  }
385
430
  const addParams = (prop, row) => {
386
- 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;
387
437
  if (prop.kind === ReferenceKind.EMBEDDED && prop.object) {
388
438
  if (prop.array && value) {
389
439
  value = this.platform.cloneEmbeddable(value);
@@ -396,14 +446,14 @@ export class AbstractSqlDriver extends DatabaseDriver {
396
446
  value = this.mapDataToFieldNames(value, false, prop.embeddedProps, options.convertCustomTypes);
397
447
  }
398
448
  }
399
- if (options.convertCustomTypes && prop.customType) {
400
- params.push(prop.customType.convertToDatabaseValue(value, this.platform, { key: prop.name, mode: 'query-data' }));
401
- return;
402
- }
403
449
  if (typeof value === 'undefined' && this.platform.usesDefaultKeyword()) {
404
450
  params.push(raw('default'));
405
451
  return;
406
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
+ }
407
457
  params.push(value);
408
458
  };
409
459
  if (fields.length > 0 || this.platform.usesDefaultKeyword()) {
@@ -437,7 +487,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
437
487
  else {
438
488
  const field = prop.fieldNames[0];
439
489
  if (!duplicates.includes(field) || !usedDups.includes(field)) {
440
- if (prop.customType && !prop.object && 'convertToDatabaseValueSQL' in prop.customType && !isRaw(row[prop.name])) {
490
+ if (prop.customType && !prop.object && 'convertToDatabaseValueSQL' in prop.customType && row[prop.name] != null && !isRaw(row[prop.name])) {
441
491
  keys.push(prop.customType.convertToDatabaseValueSQL('?', this.platform));
442
492
  }
443
493
  else {
@@ -462,7 +512,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
462
512
  if (transform) {
463
513
  sql = transform(sql);
464
514
  }
465
- const res = await this.execute(sql, params, 'run', options.ctx);
515
+ const res = await this.execute(sql, params, 'run', options.ctx, options.loggerContext);
466
516
  let pk;
467
517
  /* v8 ignore next 3 */
468
518
  if (pks.length > 1) { // owner has composite pk
@@ -490,7 +540,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
490
540
  where = { [meta?.primaryKeys[0] ?? pks[0]]: where };
491
541
  }
492
542
  if (Utils.hasObjectKeys(data)) {
493
- const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes)
543
+ const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes, options.loggerContext)
494
544
  .withSchema(this.getSchemaName(meta, options));
495
545
  if (options.upsert) {
496
546
  /* v8 ignore next */
@@ -529,7 +579,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
529
579
  const meta = this.metadata.get(entityName);
530
580
  if (options.upsert) {
531
581
  const uniqueFields = options.onConflictFields ?? (Utils.isPlainObject(where[0]) ? Object.keys(where[0]).flatMap(key => Utils.splitPrimaryKeys(key)) : meta.primaryKeys);
532
- 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));
533
583
  const returning = getOnConflictReturningFields(meta, data[0], uniqueFields, options);
534
584
  qb.insert(data)
535
585
  .onConflict(uniqueFields)
@@ -588,7 +638,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
588
638
  if (key in data[idx]) {
589
639
  const pks = Utils.getOrderedPrimaryKeys(cond, meta);
590
640
  sql += ` when (${pkCond}) then `;
591
- if (prop.customType && !prop.object && 'convertToDatabaseValueSQL' in prop.customType && !isRaw(data[idx][key])) {
641
+ if (prop.customType && !prop.object && 'convertToDatabaseValueSQL' in prop.customType && data[idx][prop.name] != null && !isRaw(data[idx][key])) {
592
642
  sql += prop.customType.convertToDatabaseValueSQL('?', this.platform);
593
643
  }
594
644
  else {
@@ -642,7 +692,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
642
692
  /* v8 ignore next */
643
693
  sql += returningFields.length > 0 ? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}` : '';
644
694
  }
645
- 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));
646
696
  for (let i = 0; i < collections.length; i++) {
647
697
  await this.processManyToMany(meta, where[i], collections[i], false, options);
648
698
  }
@@ -654,7 +704,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
654
704
  if (Utils.isPrimaryKey(where) && pks.length === 1) {
655
705
  where = { [pks[0]]: where };
656
706
  }
657
- 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));
658
708
  return this.rethrow(qb.execute('run', false));
659
709
  }
660
710
  /**
@@ -728,14 +778,20 @@ export class AbstractSqlDriver extends DatabaseDriver {
728
778
  const pivotMeta = this.metadata.find(coll.property.pivotEntity);
729
779
  let schema = pivotMeta.schema;
730
780
  if (schema === '*') {
731
- const ownerSchema = wrapped.getSchema() === '*' ? this.config.get('schema') : wrapped.getSchema();
732
- schema = coll.property.owner ? ownerSchema : this.config.get('schema');
781
+ if (coll.property.owner) {
782
+ schema = wrapped.getSchema() === '*' ? options?.schema ?? this.config.get('schema') : wrapped.getSchema();
783
+ }
784
+ else {
785
+ const targetMeta = coll.property.targetMeta;
786
+ const targetSchema = (coll[0] ?? snap?.[0]) && helper(coll[0] ?? snap?.[0]).getSchema();
787
+ schema = targetMeta.schema === '*' ? options?.schema ?? targetSchema ?? this.config.get('schema') : targetMeta.schema;
788
+ }
733
789
  }
734
790
  else if (schema == null) {
735
791
  schema = this.config.get('schema');
736
792
  }
737
793
  const tableName = `${schema ?? '_'}.${pivotMeta.tableName}`;
738
- const persister = groups[tableName] ??= new PivotCollectionPersister(pivotMeta, this, options?.ctx, schema);
794
+ const persister = groups[tableName] ??= new PivotCollectionPersister(pivotMeta, this, options?.ctx, schema, options?.loggerContext);
739
795
  persister.enqueueUpdate(coll.property, insertDiff, deleteDiff, pks);
740
796
  }
741
797
  for (const persister of Utils.values(groups)) {
@@ -743,116 +799,88 @@ export class AbstractSqlDriver extends DatabaseDriver {
743
799
  }
744
800
  }
745
801
  async loadFromPivotTable(prop, owners, where = {}, orderBy, ctx, options, pivotJoin) {
802
+ if (owners.length === 0) {
803
+ return {};
804
+ }
746
805
  const pivotMeta = this.metadata.find(prop.pivotEntity);
747
806
  const pivotProp1 = pivotMeta.relations[prop.owner ? 1 : 0];
748
807
  const pivotProp2 = pivotMeta.relations[prop.owner ? 0 : 1];
749
808
  const ownerMeta = this.metadata.find(pivotProp2.type);
750
- options = { ...options };
751
- const qb = this.createQueryBuilder(prop.pivotEntity, ctx, options.connectionType, undefined, options?.logging)
752
- .withSchema(this.getSchemaName(pivotMeta, options))
753
- .indexHint(options.indexHint)
754
- .comment(options.comments)
755
- .hintComment(options.hintComments);
756
- const pivotAlias = qb.alias;
757
- const pivotKey = pivotProp2.joinColumns.map(column => `${pivotAlias}.${column}`).join(Utils.PK_SEPARATOR);
758
809
  const cond = {
759
- [pivotKey]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) },
810
+ [pivotProp2.name]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) },
760
811
  };
761
- /* v8 ignore next 3 */
762
- if (!Utils.isEmpty(where) && Object.keys(where).every(k => Utils.isOperator(k, false))) {
763
- where = cond;
764
- }
765
- else {
766
- where = { ...where, ...cond };
767
- }
768
- orderBy = this.getPivotOrderBy(prop, pivotProp1, pivotAlias, orderBy);
769
- const populate = this.autoJoinOneToOneOwner(prop.targetMeta, []);
770
- const fields = [];
771
- const k1 = !prop.owner ? 'joinColumns' : 'inverseJoinColumns';
772
- const k2 = prop.owner ? 'joinColumns' : 'inverseJoinColumns';
773
- const cols = [
774
- ...prop[k1].map(col => `${pivotAlias}.${col} as fk__${col}`),
775
- ...prop[k2].map(col => `${pivotAlias}.${col} as fk__${col}`),
776
- ];
777
- fields.push(...cols);
778
- if (!pivotJoin) {
779
- const targetAlias = qb.getNextAlias(prop.targetMeta.tableName);
780
- const targetSchema = this.getSchemaName(prop.targetMeta, options) ?? this.platform.getDefaultSchemaName();
781
- qb.innerJoin(pivotProp1.name, targetAlias, {}, targetSchema);
782
- const targetFields = this.buildFields(prop.targetMeta, (options.populate ?? []), [], qb, targetAlias, options);
783
- for (const field of targetFields) {
784
- const f = field.toString();
785
- fields.unshift(f.includes('.') ? field : `${targetAlias}.${f}`);
786
- if (RawQueryFragment.isKnownFragment(field)) {
787
- qb.rawFragments.add(f);
788
- }
789
- }
790
- // we need to handle 1:1 owner auto-joins explicitly, as the QB type is the pivot table, not the target
791
- populate.forEach(hint => {
792
- const alias = qb.getNextAlias(prop.targetMeta.tableName);
793
- qb.leftJoin(`${targetAlias}.${hint.field}`, alias);
794
- // eslint-disable-next-line dot-notation
795
- for (const join of Object.values(qb['_joins'])) {
796
- const [propName] = hint.field.split(':', 2);
797
- if (join.alias === alias && join.prop.name === propName) {
798
- fields.push(...qb.helper.mapJoinColumns(qb.type, join));
799
- }
800
- }
801
- });
802
- }
803
- qb.select(fields)
804
- .where({ [pivotProp1.name]: where })
805
- .orderBy(orderBy)
806
- .setLockMode(options.lockMode, options.lockTableAliases);
807
- if (owners.length === 1 && (options.offset != null || options.limit != null)) {
808
- qb.limit(options.limit, options.offset);
809
- }
810
- const res = owners.length ? await this.rethrow(qb.execute('all', { mergeResults: false, mapResults: false })) : [];
811
- const tmp = {};
812
- const items = res.map((row) => {
813
- const root = super.mapResult(row, prop.targetMeta);
814
- this.mapJoinedProps(root, prop.targetMeta, populate, qb, root, tmp, pivotMeta.className + '.' + pivotProp1.name);
815
- 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,
816
834
  });
817
- qb.clearRawFragmentsCache();
818
835
  const map = {};
819
- const pkProps = ownerMeta.getPrimaryProps();
820
836
  for (const owner of owners) {
821
- const key = Utils.getPrimaryKeyHash(prop.joinColumns.map((_col, idx) => {
822
- const pkProp = pkProps[idx];
823
- return pkProp.customType ? pkProp.customType.convertToJSValue(owner[idx], this.platform) : owner[idx];
824
- }));
837
+ const key = Utils.getPrimaryKeyHash(owner);
825
838
  map[key] = [];
826
839
  }
827
- for (const item of items) {
828
- const key = Utils.getPrimaryKeyHash(prop.joinColumns.map((col, idx) => {
829
- const pkProp = pkProps[idx];
830
- return pkProp.customType ? pkProp.customType.convertToJSValue(item[`fk__${col}`], this.platform) : item[`fk__${col}`];
831
- }));
832
- map[key].push(item);
833
- prop.joinColumns.forEach(col => delete item[`fk__${col}`]);
834
- prop.inverseJoinColumns.forEach((col, idx) => {
835
- Utils.renameKey(item, `fk__${col}`, prop.targetMeta.primaryKeys[idx]);
836
- });
840
+ for (const item of res) {
841
+ const key = Utils.getPrimaryKeyHash(Utils.asArray(item[pivotProp2.name]));
842
+ map[key].push(item[pivotProp1.name]);
837
843
  }
838
844
  return map;
839
845
  }
840
- getPivotOrderBy(prop, pivotProp, pivotAlias, orderBy) {
841
- // FIXME this is ignoring the rest of the array items
846
+ getPivotOrderBy(prop, pivotProp, orderBy, parentOrderBy) {
842
847
  if (!Utils.isEmpty(orderBy)) {
843
- 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] }));
844
854
  }
845
855
  if (!Utils.isEmpty(prop.orderBy)) {
846
- return [{ [pivotProp.name]: Utils.asArray(prop.orderBy)[0] }];
856
+ return Utils.asArray(prop.orderBy).map(o => ({ [pivotProp.name]: o }));
847
857
  }
848
858
  if (prop.fixedOrder) {
849
- return [{ [`${pivotAlias}.${prop.fixedOrderColumn}`]: QueryOrder.ASC }];
859
+ return [{ [prop.fixedOrderColumn]: QueryOrder.ASC }];
850
860
  }
851
861
  return [];
852
862
  }
853
863
  async execute(query, params = [], method = 'all', ctx, loggerContext) {
854
864
  return this.rethrow(this.connection.execute(query, params, method, ctx, loggerContext));
855
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
+ }
856
884
  /**
857
885
  * 1:1 owner side needs to be marked for population so QB auto-joins the owner id
858
886
  */
@@ -864,7 +892,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
864
892
  const toPopulate = meta.relations
865
893
  .filter(prop => prop.kind === ReferenceKind.ONE_TO_ONE && !prop.owner && !prop.lazy && !relationsToPopulate.includes(prop.name))
866
894
  .filter(prop => fields.length === 0 || fields.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
867
- .map(prop => ({ field: `${prop.name}:ref`, strategy: prop.strategy }));
895
+ .map(prop => ({ field: `${prop.name}:ref`, strategy: LoadStrategy.JOINED }));
868
896
  return [...populate, ...toPopulate];
869
897
  }
870
898
  /**
@@ -874,14 +902,15 @@ export class AbstractSqlDriver extends DatabaseDriver {
874
902
  return populate.filter(hint => {
875
903
  const [propName, ref] = hint.field.split(':', 2);
876
904
  const prop = meta.properties[propName] || {};
877
- 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)) {
878
907
  return true;
879
908
  }
880
909
  // skip redundant joins for 1:1 owner population hints when using `mapToPk`
881
910
  if (prop.kind === ReferenceKind.ONE_TO_ONE && prop.mapToPk && prop.owner) {
882
911
  return false;
883
912
  }
884
- if ((options?.strategy || hint.strategy || prop.strategy || this.config.get('loadStrategy')) !== LoadStrategy.JOINED) {
913
+ if (strategy !== LoadStrategy.JOINED) {
885
914
  // force joined strategy for explicit 1:1 owner populate hint as it would require a join anyway
886
915
  return prop.kind === ReferenceKind.ONE_TO_ONE && !prop.owner;
887
916
  }
@@ -892,31 +921,26 @@ export class AbstractSqlDriver extends DatabaseDriver {
892
921
  * @internal
893
922
  */
894
923
  mergeJoinedResult(rawResults, meta, joinedProps) {
924
+ if (rawResults.length <= 1) {
925
+ return rawResults;
926
+ }
895
927
  const res = [];
896
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
+ });
897
934
  for (const item of rawResults) {
898
935
  const pk = Utils.getCompositeKeyHash(item, meta);
899
936
  if (map[pk]) {
900
- for (const hint of joinedProps) {
901
- const [propName, ref] = hint.field.split(':', 2);
902
- const prop = meta.properties[propName];
937
+ for (const { propName } of hints) {
903
938
  if (!item[propName]) {
904
939
  continue;
905
940
  }
906
- if ([ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind) && ref) {
907
- map[pk][propName] = [...map[pk][propName], ...item[propName]];
908
- continue;
909
- }
910
- switch (prop.kind) {
911
- case ReferenceKind.ONE_TO_MANY:
912
- case ReferenceKind.MANY_TO_MANY:
913
- map[pk][propName] = this.mergeJoinedResult([...map[pk][propName], ...item[propName]], prop.targetMeta, hint.children ?? []);
914
- break;
915
- case ReferenceKind.MANY_TO_ONE:
916
- case ReferenceKind.ONE_TO_ONE:
917
- map[pk][propName] = this.mergeJoinedResult([map[pk][propName], item[propName]], prop.targetMeta, hint.children ?? [])[0];
918
- break;
919
- }
941
+ collectionsToMerge[pk] ??= {};
942
+ collectionsToMerge[pk][propName] ??= [map[pk][propName]];
943
+ collectionsToMerge[pk][propName].push(item[propName]);
920
944
  }
921
945
  }
922
946
  else {
@@ -924,37 +948,59 @@ export class AbstractSqlDriver extends DatabaseDriver {
924
948
  res.push(item);
925
949
  }
926
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
+ }
927
976
  return res;
928
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
+ }
929
987
  getFieldsForJoinedLoad(qb, meta, options) {
930
988
  const fields = [];
931
989
  const populate = options.populate ?? [];
932
990
  const joinedProps = this.joinedProps(meta, populate, options);
933
- const shouldHaveColumn = (meta, prop, populate, fields) => {
934
- if (!this.platform.shouldHaveColumn(prop, populate, options.exclude)) {
935
- return false;
936
- }
937
- if (!fields || fields.includes('*') || prop.primary || meta.root.discriminatorColumn === prop.name) {
938
- return true;
939
- }
940
- return fields.some(f => f === prop.name || f.toString().startsWith(prop.name + '.'));
941
- };
942
991
  const populateWhereAll = options?._populateWhere === 'all' || Utils.isEmpty(options?._populateWhere);
943
992
  // root entity is already handled, skip that
944
993
  if (options.parentJoinPath) {
945
994
  // alias all fields in the primary table
946
995
  meta.props
947
- .filter(prop => shouldHaveColumn(meta, prop, populate, options.explicitFields))
948
- .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)));
949
998
  }
950
999
  for (const hint of joinedProps) {
951
1000
  const [propName, ref] = hint.field.split(':', 2);
952
1001
  const prop = meta.properties[propName];
953
1002
  // ignore ref joins of known FKs unless it's a filter hint
954
- if (ref && !hint.filter && (prop.kind === ReferenceKind.MANY_TO_ONE || (prop.kind === ReferenceKind.ONE_TO_ONE && !prop.owner))) {
955
- continue;
956
- }
957
- 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))) {
958
1004
  continue;
959
1005
  }
960
1006
  const meta2 = this.metadata.find(prop.type);
@@ -965,11 +1011,14 @@ export class AbstractSqlDriver extends DatabaseDriver {
965
1011
  if (!options.parentJoinPath && populateWhereAll && !hint.filter && !path.startsWith('[populate]')) {
966
1012
  path = '[populate]' + path;
967
1013
  }
1014
+ const mandatoryToOneProperty = [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && !prop.nullable;
968
1015
  const joinType = pivotRefJoin
969
1016
  ? JoinType.pivotJoin
970
- : hint.filter && !prop.nullable
971
- ? JoinType.innerJoin
972
- : JoinType.leftJoin;
1017
+ : hint.joinType
1018
+ ? hint.joinType
1019
+ : (hint.filter && !prop.nullable) || mandatoryToOneProperty
1020
+ ? JoinType.innerJoin
1021
+ : JoinType.leftJoin;
973
1022
  qb.join(field, tableAlias, {}, joinType, path);
974
1023
  if (pivotRefJoin) {
975
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}`)));
@@ -1001,7 +1050,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1001
1050
  parentJoinPath: path,
1002
1051
  }));
1003
1052
  }
1004
- 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))) {
1005
1054
  fields.push(...prop.referencedColumnNames.map(col => qb.helper.mapper(`${tableAlias}.${col}`, qb.type, undefined, `${tableAlias}__${col}`)));
1006
1055
  }
1007
1056
  }
@@ -1010,9 +1059,18 @@ export class AbstractSqlDriver extends DatabaseDriver {
1010
1059
  /**
1011
1060
  * @internal
1012
1061
  */
1013
- 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 (childFields.length > 0 && !this.shouldHaveColumn(prop.targetMeta, { ...childProp, name }, [], childFields)) {
1067
+ return [];
1068
+ }
1069
+ return this.mapPropToFieldNames(qb, childProp, tableAlias, childFields);
1070
+ });
1071
+ }
1014
1072
  const aliased = this.platform.quoteIdentifier(`${tableAlias}__${prop.fieldNames[0]}`);
1015
- if (prop.customTypes?.some(type => type?.convertToJSValueSQL)) {
1073
+ if (prop.customTypes?.some(type => !!type?.convertToJSValueSQL)) {
1016
1074
  return prop.fieldNames.map((col, idx) => {
1017
1075
  if (!prop.customTypes[idx]?.convertToJSValueSQL) {
1018
1076
  return col;
@@ -1076,7 +1134,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1076
1134
  for (const prop of meta.relations) {
1077
1135
  if (collections[prop.name]) {
1078
1136
  const pivotMeta = this.metadata.find(prop.pivotEntity);
1079
- const persister = new PivotCollectionPersister(pivotMeta, this, options?.ctx, options?.schema);
1137
+ const persister = new PivotCollectionPersister(pivotMeta, this, options?.ctx, options?.schema, options?.loggerContext);
1080
1138
  persister.enqueueUpdate(prop, collections[prop.name], clear, pks);
1081
1139
  await this.rethrow(persister.execute());
1082
1140
  }
@@ -1143,7 +1201,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1143
1201
  let path = parentPath;
1144
1202
  const meta2 = this.metadata.find(prop.type);
1145
1203
  const childOrder = orderHint[prop.name];
1146
- 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))) {
1147
1205
  path += `.${propName}`;
1148
1206
  }
1149
1207
  if (prop.kind === ReferenceKind.MANY_TO_MANY && typeof childOrder !== 'object') {
@@ -1151,10 +1209,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
1151
1209
  }
1152
1210
  const join = qb.getJoinForPath(path, { matchPopulateJoins: true });
1153
1211
  const propAlias = qb.getAliasForJoinPath(join ?? path, { matchPopulateJoins: true }) ?? parentAlias;
1154
- if (!join && parentAlias === qb.alias) {
1212
+ if (!join) {
1155
1213
  continue;
1156
1214
  }
1157
- if (![ReferenceKind.SCALAR, ReferenceKind.EMBEDDED].includes(prop.kind) && typeof childOrder === 'object') {
1215
+ if (join && ![ReferenceKind.SCALAR, ReferenceKind.EMBEDDED].includes(prop.kind) && typeof childOrder === 'object') {
1158
1216
  const children = this.buildPopulateOrderBy(qb, meta2, Utils.asArray(childOrder), path, explicit, propAlias);
1159
1217
  orderBy.push(...children);
1160
1218
  continue;
@@ -1232,7 +1290,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1232
1290
  }
1233
1291
  return ret;
1234
1292
  }
1235
- processField(meta, prop, field, ret, populate, joinedProps, qb) {
1293
+ processField(meta, prop, field, ret) {
1236
1294
  if (!prop || (prop.kind === ReferenceKind.ONE_TO_ONE && !prop.owner)) {
1237
1295
  return;
1238
1296
  }
@@ -1245,7 +1303,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1245
1303
  const top = parts.shift();
1246
1304
  for (const key of Object.keys(prop.embeddedProps)) {
1247
1305
  if (!top || key === top) {
1248
- this.processField(meta, prop.embeddedProps[key], parts.join('.'), ret, populate, joinedProps, qb);
1306
+ this.processField(meta, prop.embeddedProps[key], parts.join('.'), ret);
1249
1307
  }
1250
1308
  }
1251
1309
  return;
@@ -1255,16 +1313,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1255
1313
  }
1256
1314
  ret.push(prop.name);
1257
1315
  }
1258
- isPopulated(meta, prop, hint, name) {
1259
- if (hint.field === prop.name || hint.field === name || hint.all) {
1260
- return true;
1261
- }
1262
- if (prop.embedded && hint.children && meta.properties[prop.embedded[0]].name === hint.field) {
1263
- return hint.children.some(c => this.isPopulated(meta, prop, c, prop.embedded[1]));
1264
- }
1265
- return false;
1266
- }
1267
- buildFields(meta, populate, joinedProps, qb, alias, options, count = false) {
1316
+ buildFields(meta, populate, joinedProps, qb, alias, options) {
1268
1317
  const lazyProps = meta.props.filter(prop => prop.lazy && !populate.some(p => this.isPopulated(meta, prop, p)));
1269
1318
  const hasLazyFormulas = meta.props.some(p => p.lazy && p.formula);
1270
1319
  const requiresSQLConversion = meta.props.some(p => p.customType?.convertToJSValueSQL && p.persist !== false);
@@ -1287,7 +1336,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1287
1336
  where: {},
1288
1337
  aliasMap: qb.getAliasMap(),
1289
1338
  });
1290
- this.processField(meta, prop, parts.join('.'), ret, populate, joinedProps, qb);
1339
+ this.processField(meta, prop, parts.join('.'), ret);
1291
1340
  }
1292
1341
  if (!options.fields.includes('*') && !options.fields.includes(`${qb.alias}.*`)) {
1293
1342
  ret.unshift(...meta.primaryKeys.filter(pk => !options.fields.includes(pk)));
@@ -1297,7 +1346,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1297
1346
  }
1298
1347
  }
1299
1348
  else if (!Utils.isEmpty(options.exclude) || lazyProps.some(p => !p.formula && (p.kind !== '1:1' || p.owner))) {
1300
- 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));
1301
1350
  ret.push(...props.filter(p => !lazyProps.includes(p)).map(p => p.name));
1302
1351
  addFormulas = true;
1303
1352
  }
@@ -1309,16 +1358,19 @@ export class AbstractSqlDriver extends DatabaseDriver {
1309
1358
  ret.push('*');
1310
1359
  }
1311
1360
  if (ret.length > 0 && !hasExplicitFields && addFormulas) {
1312
- meta.props
1313
- .filter(prop => prop.formula && !lazyProps.includes(prop))
1314
- .forEach(prop => {
1315
- const a = this.platform.quoteIdentifier(alias);
1316
- const aliased = this.platform.quoteIdentifier(prop.fieldNames[0]);
1317
- ret.push(raw(`${prop.formula(a)} as ${aliased}`));
1318
- });
1319
- meta.props
1320
- .filter(prop => !prop.object && (prop.hasConvertToDatabaseValueSQL || prop.hasConvertToJSValueSQL))
1321
- .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
+ }
1322
1374
  }
1323
1375
  // add joined relations after the root entity fields
1324
1376
  if (joinedProps.length > 0) {
@@ -1327,7 +1379,6 @@ export class AbstractSqlDriver extends DatabaseDriver {
1327
1379
  exclude: options.exclude,
1328
1380
  populate,
1329
1381
  parentTableAlias: alias,
1330
- count,
1331
1382
  ...options,
1332
1383
  }));
1333
1384
  }