@mikro-orm/knex 6.0.0-dev.17 → 6.0.0-dev.171

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/AbstractSqlConnection.d.ts +8 -4
  2. package/AbstractSqlConnection.js +30 -18
  3. package/AbstractSqlDriver.d.ts +21 -23
  4. package/AbstractSqlDriver.js +389 -224
  5. package/AbstractSqlPlatform.d.ts +5 -4
  6. package/AbstractSqlPlatform.js +34 -5
  7. package/MonkeyPatchable.d.ts +1 -0
  8. package/MonkeyPatchable.js +3 -0
  9. package/PivotCollectionPersister.d.ts +17 -0
  10. package/PivotCollectionPersister.js +128 -0
  11. package/README.md +64 -107
  12. package/SqlEntityManager.d.ts +2 -6
  13. package/SqlEntityManager.js +5 -9
  14. package/SqlEntityRepository.d.ts +7 -6
  15. package/SqlEntityRepository.js +11 -8
  16. package/index.mjs +24 -5
  17. package/package.json +8 -8
  18. package/query/ArrayCriteriaNode.d.ts +4 -3
  19. package/query/ArrayCriteriaNode.js +5 -0
  20. package/query/CriteriaNode.d.ts +10 -8
  21. package/query/CriteriaNode.js +23 -10
  22. package/query/CriteriaNodeFactory.d.ts +6 -6
  23. package/query/CriteriaNodeFactory.js +17 -13
  24. package/query/ObjectCriteriaNode.d.ts +4 -3
  25. package/query/ObjectCriteriaNode.js +48 -17
  26. package/query/QueryBuilder.d.ts +53 -24
  27. package/query/QueryBuilder.js +393 -133
  28. package/query/QueryBuilderHelper.d.ts +11 -16
  29. package/query/QueryBuilderHelper.js +194 -155
  30. package/query/ScalarCriteriaNode.d.ts +3 -2
  31. package/query/ScalarCriteriaNode.js +10 -6
  32. package/query/enums.d.ts +7 -0
  33. package/query/enums.js +10 -2
  34. package/schema/DatabaseSchema.d.ts +5 -2
  35. package/schema/DatabaseSchema.js +28 -10
  36. package/schema/DatabaseTable.d.ts +5 -3
  37. package/schema/DatabaseTable.js +69 -34
  38. package/schema/SchemaComparator.d.ts +5 -5
  39. package/schema/SchemaComparator.js +59 -35
  40. package/schema/SchemaHelper.d.ts +6 -4
  41. package/schema/SchemaHelper.js +18 -7
  42. package/schema/SqlSchemaGenerator.d.ts +3 -6
  43. package/schema/SqlSchemaGenerator.js +71 -36
  44. package/typings.d.ts +19 -10
@@ -26,6 +26,12 @@ const CriteriaNodeFactory_1 = require("./CriteriaNodeFactory");
26
26
  * ```
27
27
  */
28
28
  class QueryBuilder {
29
+ metadata;
30
+ driver;
31
+ context;
32
+ connectionType;
33
+ em;
34
+ logging;
29
35
  get mainAlias() {
30
36
  this.ensureFromClause();
31
37
  return this._mainAlias;
@@ -37,31 +43,56 @@ class QueryBuilder {
37
43
  this.ensureFromClause();
38
44
  return this._helper;
39
45
  }
46
+ /** @internal */
47
+ type;
48
+ /** @internal */
49
+ _fields;
50
+ /** @internal */
51
+ _populate = [];
52
+ /** @internal */
53
+ _populateWhere;
54
+ /** @internal */
55
+ _populateMap = {};
56
+ aliasCounter = 0;
57
+ flags = new Set([core_1.QueryFlag.CONVERT_CUSTOM_TYPES]);
58
+ finalized = false;
59
+ _joins = {};
60
+ _explicitAlias = false;
61
+ _schema;
62
+ _cond = {};
63
+ _data;
64
+ _orderBy = [];
65
+ _groupBy = [];
66
+ _having = {};
67
+ _returning;
68
+ _onConflict;
69
+ _limit;
70
+ _offset;
71
+ _distinctOn;
72
+ _joinedProps = new Map();
73
+ _cache;
74
+ _indexHint;
75
+ _comments = [];
76
+ _hintComments = [];
77
+ flushMode;
78
+ lockMode;
79
+ lockTables;
80
+ subQueries = {};
81
+ _mainAlias;
82
+ _aliases = {};
83
+ _helper;
84
+ platform;
85
+ knex;
40
86
  /**
41
87
  * @internal
42
88
  */
43
- constructor(entityName, metadata, driver, context, alias, connectionType, em) {
89
+ constructor(entityName, metadata, driver, context, alias, connectionType, em, logging) {
44
90
  this.metadata = metadata;
45
91
  this.driver = driver;
46
92
  this.context = context;
47
93
  this.connectionType = connectionType;
48
94
  this.em = em;
49
- /** @internal */
50
- this._populate = [];
51
- /** @internal */
52
- this._populateMap = {};
53
- this.aliasCounter = 0;
54
- this.flags = new Set([core_1.QueryFlag.CONVERT_CUSTOM_TYPES]);
55
- this.finalized = false;
56
- this._joins = {};
57
- this._explicitAlias = false;
58
- this._cond = {};
59
- this._orderBy = [];
60
- this._groupBy = [];
61
- this._having = {};
62
- this._joinedProps = new Map();
63
- this.subQueries = {};
64
- this._aliases = {};
95
+ this.logging = logging;
65
96
  this.platform = this.driver.getPlatform();
66
97
  this.knex = this.driver.getConnection(this.connectionType).getKnex();
67
98
  if (alias) {
@@ -116,27 +147,46 @@ class QueryBuilder {
116
147
  this._fields = this.mainAlias.metadata.primaryKeys;
117
148
  }
118
149
  else {
119
- this._fields = [this.raw('*')];
150
+ this._fields = [(0, core_1.raw)('*')];
120
151
  }
121
152
  if (distinct) {
122
153
  this.flags.add(core_1.QueryFlag.DISTINCT);
123
154
  }
124
155
  return this.init(enums_1.QueryType.COUNT);
125
156
  }
126
- join(field, alias, cond = {}, type = 'innerJoin', path) {
127
- this.joinReference(field, alias, cond, type, path);
157
+ join(field, alias, cond = {}, type = enums_1.JoinType.innerJoin, path, schema) {
158
+ this.joinReference(field, alias, cond, type, path, schema);
159
+ return this;
160
+ }
161
+ innerJoin(field, alias, cond = {}, schema) {
162
+ this.join(field, alias, cond, enums_1.JoinType.innerJoin, undefined, schema);
128
163
  return this;
129
164
  }
130
- leftJoin(field, alias, cond = {}) {
131
- return this.join(field, alias, cond, 'leftJoin');
165
+ innerJoinLateral(field, alias, cond = {}, schema) {
166
+ this.join(field, alias, cond, enums_1.JoinType.innerJoinLateral, undefined, schema);
167
+ return this;
168
+ }
169
+ leftJoin(field, alias, cond = {}, schema) {
170
+ return this.join(field, alias, cond, enums_1.JoinType.leftJoin, undefined, schema);
171
+ }
172
+ leftJoinLateral(field, alias, cond = {}, schema) {
173
+ return this.join(field, alias, cond, enums_1.JoinType.leftJoinLateral, undefined, schema);
132
174
  }
133
- joinAndSelect(field, alias, cond = {}, type = 'innerJoin', path) {
175
+ joinAndSelect(field, alias, cond = {}, type = enums_1.JoinType.innerJoin, path, fields, schema) {
134
176
  if (!this.type) {
135
177
  this.select('*');
136
178
  }
137
- const prop = this.joinReference(field, alias, cond, type, path);
138
- this.addSelect(this.getFieldsForJoinedLoad(prop, alias));
179
+ let subquery;
180
+ if (Array.isArray(field)) {
181
+ subquery = field[1] instanceof QueryBuilder ? field[1].getFormattedQuery() : field[1].toString();
182
+ field = field[0];
183
+ }
184
+ const prop = this.joinReference(field, alias, cond, type, path, schema, subquery);
139
185
  const [fromAlias] = this.helper.splitField(field);
186
+ if (subquery) {
187
+ this._joins[`${fromAlias}.${prop.name}#${alias}`].subquery = subquery;
188
+ }
189
+ this.addSelect(this.getFieldsForJoinedLoad(prop, alias, fields));
140
190
  const populate = this._joinedProps.get(fromAlias);
141
191
  const item = { field: prop.name, strategy: core_1.LoadStrategy.JOINED, children: [] };
142
192
  if (populate) {
@@ -148,13 +198,22 @@ class QueryBuilder {
148
198
  this._joinedProps.set(alias, item);
149
199
  return this;
150
200
  }
151
- leftJoinAndSelect(field, alias, cond = {}) {
152
- return this.joinAndSelect(field, alias, cond, 'leftJoin');
201
+ leftJoinAndSelect(field, alias, cond = {}, fields, schema) {
202
+ return this.joinAndSelect(field, alias, cond, enums_1.JoinType.leftJoin, undefined, fields, schema);
203
+ }
204
+ leftJoinLateralAndSelect(field, alias, cond = {}, fields, schema) {
205
+ return this.joinAndSelect(field, alias, cond, enums_1.JoinType.leftJoinLateral, undefined, fields, schema);
153
206
  }
154
- getFieldsForJoinedLoad(prop, alias) {
207
+ innerJoinAndSelect(field, alias, cond = {}, fields, schema) {
208
+ return this.joinAndSelect(field, alias, cond, enums_1.JoinType.innerJoin, undefined, fields, schema);
209
+ }
210
+ innerJoinLateralAndSelect(field, alias, cond = {}, fields, schema) {
211
+ return this.joinAndSelect(field, alias, cond, enums_1.JoinType.innerJoinLateral, undefined, fields, schema);
212
+ }
213
+ getFieldsForJoinedLoad(prop, alias, explicitFields) {
155
214
  const fields = [];
156
215
  prop.targetMeta.props
157
- .filter(prop => this.platform.shouldHaveColumn(prop, this._populate))
216
+ .filter(prop => explicitFields ? explicitFields.includes(prop.name) || prop.primary : this.platform.shouldHaveColumn(prop, this._populate))
158
217
  .forEach(prop => fields.push(...this.driver.mapPropToFieldNames(this, prop, alias)));
159
218
  return fields;
160
219
  }
@@ -165,9 +224,15 @@ class QueryBuilder {
165
224
  }
166
225
  where(cond, params, operator) {
167
226
  this.ensureNotFinalized();
168
- if (core_1.Utils.isString(cond)) {
169
- cond = { [`(${cond})`]: core_1.Utils.asArray(params) };
170
- operator = operator || '$and';
227
+ const rawField = core_1.RawQueryFragment.getKnownFragment(cond);
228
+ if (rawField) {
229
+ const sql = this.platform.formatQuery(rawField.sql, rawField.params);
230
+ cond = { [(0, core_1.raw)(`(${sql})`)]: core_1.Utils.asArray(params) };
231
+ operator ??= '$and';
232
+ }
233
+ else if (core_1.Utils.isString(cond)) {
234
+ cond = { [(0, core_1.raw)(`(${cond})`, core_1.Utils.asArray(params))]: [] };
235
+ operator ??= '$and';
171
236
  }
172
237
  else {
173
238
  cond = core_1.QueryHelper.processWhere({
@@ -222,6 +287,7 @@ class QueryBuilder {
222
287
  aliasMap: this.getAliasMap(),
223
288
  aliased: !this.type || [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(this.type),
224
289
  convertCustomTypes: false,
290
+ type: 'orderBy',
225
291
  });
226
292
  this._orderBy.push(CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, processed).process(this));
227
293
  });
@@ -235,15 +301,22 @@ class QueryBuilder {
235
301
  having(cond = {}, params) {
236
302
  this.ensureNotFinalized();
237
303
  if (core_1.Utils.isString(cond)) {
238
- cond = { [`(${cond})`]: core_1.Utils.asArray(params) };
304
+ cond = { [(0, core_1.raw)(`(${cond})`, params)]: [] };
239
305
  }
240
306
  this._having = CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, cond).process(this);
241
307
  return this;
242
308
  }
243
309
  onConflict(fields = []) {
310
+ const meta = this.mainAlias.metadata;
244
311
  this.ensureNotFinalized();
245
- this._onConflict = this._onConflict || [];
246
- this._onConflict.push({ fields: core_1.Utils.asArray(fields).map(f => f.toString()) });
312
+ this._onConflict ??= [];
313
+ this._onConflict.push({
314
+ fields: core_1.Utils.asArray(fields).flatMap(f => {
315
+ const key = f.toString();
316
+ /* istanbul ignore next */
317
+ return meta.properties[key]?.fieldNames ?? [key];
318
+ }),
319
+ });
247
320
  return this;
248
321
  }
249
322
  ignore() {
@@ -263,6 +336,10 @@ class QueryBuilder {
263
336
  this._onConflict[this._onConflict.length - 1].merge = data;
264
337
  return this;
265
338
  }
339
+ returning(fields) {
340
+ this._returning = fields != null ? core_1.Utils.asArray(fields) : fields;
341
+ return this;
342
+ }
266
343
  /**
267
344
  * @internal
268
345
  */
@@ -272,17 +349,6 @@ class QueryBuilder {
272
349
  this._populateWhere = populateWhere;
273
350
  return this;
274
351
  }
275
- /**
276
- * @internal
277
- */
278
- ref(field) {
279
- return this.knex.ref(field);
280
- }
281
- raw(sql, bindings = []) {
282
- const raw = this.knex.raw(sql, bindings);
283
- raw.__raw = true; // tag it as there is now way to check via `instanceof`
284
- return raw;
285
- }
286
352
  limit(limit, offset = 0) {
287
353
  this.ensureNotFinalized();
288
354
  this._limit = limit;
@@ -338,6 +404,24 @@ class QueryBuilder {
338
404
  this._indexHint = sql;
339
405
  return this;
340
406
  }
407
+ /**
408
+ * Prepend comment to the sql query using the syntax `/* ... *‍/`. Some characters are forbidden such as `/*, *‍/` and `?`.
409
+ */
410
+ comment(comment) {
411
+ this.ensureNotFinalized();
412
+ this._comments.push(...core_1.Utils.asArray(comment));
413
+ return this;
414
+ }
415
+ /**
416
+ * Add hints to the query using comment-like syntax `/*+ ... *‍/`. MySQL and Oracle use this syntax for optimizer hints.
417
+ * Also various DB proxies and routers use this syntax to pass hints to alter their behavior. In other dialects the hints
418
+ * are ignored as simple comments.
419
+ */
420
+ hintComment(comment) {
421
+ this.ensureNotFinalized();
422
+ this._hintComments.push(...core_1.Utils.asArray(comment));
423
+ return this;
424
+ }
341
425
  from(target, aliasName) {
342
426
  this.ensureNotFinalized();
343
427
  if (target instanceof QueryBuilder) {
@@ -355,17 +439,21 @@ class QueryBuilder {
355
439
  getKnexQuery() {
356
440
  this.finalize();
357
441
  const qb = this.getQueryBase();
442
+ qb.__raw = true; // tag it as there is now way to check via `instanceof`
358
443
  core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type ?? enums_1.QueryType.SELECT, this._cond, qb), this._cond && !this._onConflict);
359
444
  core_1.Utils.runIfNotEmpty(() => qb.groupBy(this.prepareFields(this._groupBy, 'groupBy')), this._groupBy);
360
445
  core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type ?? enums_1.QueryType.SELECT, this._having, qb, undefined, 'having'), this._having);
361
446
  core_1.Utils.runIfNotEmpty(() => {
362
447
  const queryOrder = this.helper.getQueryOrder(this.type ?? enums_1.QueryType.SELECT, this._orderBy, this._populateMap);
363
448
  if (queryOrder) {
364
- return qb.orderByRaw(queryOrder);
449
+ qb.orderByRaw(queryOrder);
450
+ return;
365
451
  }
366
452
  }, this._orderBy);
367
453
  core_1.Utils.runIfNotEmpty(() => qb.limit(this._limit), this._limit != null);
368
454
  core_1.Utils.runIfNotEmpty(() => qb.offset(this._offset), this._offset);
455
+ core_1.Utils.runIfNotEmpty(() => this._comments.forEach(comment => qb.comment(comment)), this._comments);
456
+ core_1.Utils.runIfNotEmpty(() => this._hintComments.forEach(comment => qb.hintComment(comment)), this._hintComments);
369
457
  core_1.Utils.runIfNotEmpty(() => this.helper.appendOnConflictClause(this.type ?? enums_1.QueryType.SELECT, this._onConflict, qb), this._onConflict);
370
458
  if (this.type === enums_1.QueryType.TRUNCATE && this.platform.usesCascadeStatement()) {
371
459
  return this.knex.raw(qb.toSQL().toNative().sql + ' cascade');
@@ -373,26 +461,35 @@ class QueryBuilder {
373
461
  if (this.lockMode) {
374
462
  this.helper.getLockSQL(qb, this.lockMode, this.lockTables);
375
463
  }
376
- this.helper.finalize(this.type ?? enums_1.QueryType.SELECT, qb, this.mainAlias.metadata);
464
+ this.helper.finalize(this.type ?? enums_1.QueryType.SELECT, qb, this.mainAlias.metadata, this._data, this._returning);
377
465
  return qb;
378
466
  }
379
467
  /**
380
468
  * Returns the query with parameters as wildcards.
381
469
  */
382
470
  getQuery() {
383
- return this.getKnexQuery().toSQL().toNative().sql;
471
+ return this.toQuery().sql;
472
+ }
473
+ #query;
474
+ toQuery() {
475
+ if (this.#query) {
476
+ return this.#query;
477
+ }
478
+ const sql = this.getKnexQuery().toSQL();
479
+ const query = sql.toNative();
480
+ return this.#query = { sql: query.sql, params: query.bindings ?? [], _sql: sql };
384
481
  }
385
482
  /**
386
483
  * Returns the list of all parameters for this query.
387
484
  */
388
485
  getParams() {
389
- return this.getKnexQuery().toSQL().toNative().bindings;
486
+ return this.toQuery().params;
390
487
  }
391
488
  /**
392
489
  * Returns raw interpolated query string with all the parameters inlined.
393
490
  */
394
491
  getFormattedQuery() {
395
- const query = this.getKnexQuery().toSQL();
492
+ const query = this.toQuery()._sql;
396
493
  return this.platform.formatQuery(query.sql, query.bindings);
397
494
  }
398
495
  /**
@@ -424,7 +521,10 @@ class QueryBuilder {
424
521
  * Executes this QB and returns the raw results, mapped to the property names (unless disabled via last parameter).
425
522
  * Use `method` to specify what kind of result you want to get (array/single/meta).
426
523
  */
427
- async execute(method = 'all', mapResults = true) {
524
+ async execute(method = 'all', options) {
525
+ options = typeof options === 'boolean' ? { mapResults: options } : (options ?? {});
526
+ options.mergeResults ??= true;
527
+ options.mapResults ??= true;
428
528
  if (!this.connectionType && method !== 'run' && [enums_1.QueryType.INSERT, enums_1.QueryType.UPDATE, enums_1.QueryType.DELETE, enums_1.QueryType.TRUNCATE].includes(this.type ?? enums_1.QueryType.SELECT)) {
429
529
  this.connectionType = 'write';
430
530
  }
@@ -433,21 +533,33 @@ class QueryBuilder {
433
533
  if (cached?.data) {
434
534
  return cached.data;
435
535
  }
436
- const type = this.connectionType || (method === 'run' ? 'write' : 'read');
437
- const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context);
536
+ const write = method === 'run' || !this.platform.getConfig().get('preferReadReplicas');
537
+ const type = this.connectionType || (write ? 'write' : 'read');
538
+ const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context, this.logging);
438
539
  const meta = this.mainAlias.metadata;
439
- if (!mapResults || !meta) {
540
+ if (!options.mapResults || !meta) {
440
541
  await this.em?.storeCache(this._cache, cached, res);
441
542
  return res;
442
543
  }
443
- if (method === 'all' && Array.isArray(res)) {
544
+ if (method === 'run') {
545
+ return res;
546
+ }
547
+ const joinedProps = this.driver.joinedProps(meta, this._populate);
548
+ let mapped;
549
+ if (Array.isArray(res)) {
444
550
  const map = {};
445
- const mapped = res.map(r => this.driver.mapResult(r, meta, this._populate, this, map));
446
- await this.em?.storeCache(this._cache, cached, mapped);
447
- return mapped;
551
+ mapped = res.map(r => this.driver.mapResult(r, meta, this._populate, this, map));
552
+ if (options.mergeResults && joinedProps.length > 0) {
553
+ mapped = this.driver.mergeJoinedResult(mapped, this.mainAlias.metadata, joinedProps);
554
+ }
555
+ }
556
+ else {
557
+ mapped = [this.driver.mapResult(res, meta, joinedProps, this)];
448
558
  }
449
- const mapped = this.driver.mapResult(res, meta, this._populate, this);
450
559
  await this.em?.storeCache(this._cache, cached, mapped);
560
+ if (method === 'get') {
561
+ return mapped[0];
562
+ }
451
563
  return mapped;
452
564
  }
453
565
  /**
@@ -461,11 +573,28 @@ class QueryBuilder {
461
573
  */
462
574
  async getResultList() {
463
575
  await this.em.tryFlush(this.mainAlias.entityName, { flushMode: this.flushMode });
464
- let res = await this.execute('all', true);
465
- if (this._joinedProps.size > 0) {
466
- res = this.driver.mergeJoinedResult(res, this.mainAlias.metadata);
576
+ const res = await this.execute('all', true);
577
+ const entities = [];
578
+ function propagatePopulateHint(entity, hint) {
579
+ (0, core_1.helper)(entity).__serializationContext.populate ??= hint;
580
+ hint.forEach(pop => {
581
+ const value = entity[pop.field];
582
+ if (core_1.Utils.isEntity(value, true)) {
583
+ (0, core_1.helper)(value).populated();
584
+ propagatePopulateHint(value, pop.children ?? []);
585
+ }
586
+ else if (core_1.Utils.isCollection(value)) {
587
+ value.populated();
588
+ value.getItems(false).forEach(item => propagatePopulateHint(item, pop.children ?? []));
589
+ }
590
+ });
591
+ }
592
+ for (const r of res) {
593
+ const entity = this.em.map(this.mainAlias.entityName, r, { schema: this._schema });
594
+ propagatePopulateHint(entity, this._populate);
595
+ entities.push(entity);
467
596
  }
468
- return res.map(r => this.em.map(this.mainAlias.entityName, r, { schema: this._schema }));
597
+ return entities;
469
598
  }
470
599
  /**
471
600
  * Executes the query, returning the first result or null
@@ -483,17 +612,30 @@ class QueryBuilder {
483
612
  res = await this.execute('get', false);
484
613
  }
485
614
  else {
486
- const qb = this.clone();
615
+ const qb = this.type === undefined ? this : this.clone();
487
616
  qb.count(field, distinct ?? qb.hasToManyJoins()).limit(undefined).offset(undefined).orderBy([]);
488
617
  res = await qb.execute('get', false);
489
618
  }
490
619
  return res ? +res.count : 0;
491
620
  }
621
+ /**
622
+ * Executes the query, returning both array of results and total count query (without offset and limit).
623
+ */
624
+ async getResultAndCount() {
625
+ return Promise.all([
626
+ this.getResultList(),
627
+ this.getCount(),
628
+ ]);
629
+ }
492
630
  /**
493
631
  * Provides promise-like interface so we can await the QB instance.
494
632
  */
495
633
  then(onfulfilled, onrejected) {
496
- switch (this.type ?? enums_1.QueryType.SELECT) {
634
+ let type = this.type ?? enums_1.QueryType.SELECT;
635
+ if (this.flags.has(core_1.QueryFlag.UPDATE_SUB_QUERY) || this.flags.has(core_1.QueryFlag.DELETE_SUB_QUERY)) {
636
+ type = enums_1.QueryType.UPDATE;
637
+ }
638
+ switch (type) {
497
639
  case enums_1.QueryType.INSERT:
498
640
  case enums_1.QueryType.UPDATE:
499
641
  case enums_1.QueryType.DELETE:
@@ -516,15 +658,22 @@ class QueryBuilder {
516
658
  /* istanbul ignore next */
517
659
  alias = meta?.properties[f]?.fieldNames[0] ?? alias;
518
660
  }
519
- return qb.as(alias);
661
+ const ret = qb.as(alias);
662
+ // tag the instance, so it is possible to detect it easily
663
+ Object.defineProperty(ret, '__as', { enumerable: false, value: alias });
664
+ return ret;
520
665
  }
521
- clone() {
666
+ clone(reset) {
522
667
  const qb = new QueryBuilder(this.mainAlias.entityName, this.metadata, this.driver, this.context, this.mainAlias.aliasName, this.connectionType, this.em);
668
+ if (reset) {
669
+ return qb;
670
+ }
523
671
  Object.assign(qb, this);
524
672
  // clone array/object properties
525
673
  const properties = [
526
674
  'flags', '_populate', '_populateWhere', '_populateMap', '_joins', '_joinedProps', '_cond', '_data', '_orderBy',
527
- '_schema', '_indexHint', '_cache', 'subQueries', 'lockMode', 'lockTables',
675
+ '_schema', '_indexHint', '_cache', 'subQueries', 'lockMode', 'lockTables', '_groupBy', '_having', '_returning',
676
+ '_comments', '_hintComments',
528
677
  ];
529
678
  properties.forEach(prop => qb[prop] = core_1.Utils.copy(this[prop]));
530
679
  /* istanbul ignore else */
@@ -552,14 +701,42 @@ class QueryBuilder {
552
701
  }
553
702
  return qb;
554
703
  }
555
- joinReference(field, alias, cond, type, path) {
704
+ joinReference(field, alias, cond, type, path, schema, subquery) {
556
705
  this.ensureNotFinalized();
706
+ if (typeof field === 'object') {
707
+ const prop = {
708
+ name: '__subquery__',
709
+ kind: core_1.ReferenceKind.MANY_TO_ONE,
710
+ };
711
+ if (field instanceof QueryBuilder) {
712
+ prop.type = field.mainAlias.entityName;
713
+ prop.targetMeta = field.mainAlias.metadata;
714
+ field = field.getKnexQuery();
715
+ }
716
+ this._joins[`${this.alias}.${prop.name}#${alias}`] = {
717
+ prop,
718
+ alias,
719
+ type,
720
+ cond,
721
+ schema,
722
+ subquery: field.toString(),
723
+ ownerAlias: this.alias,
724
+ };
725
+ return prop;
726
+ }
727
+ if (!subquery && type.includes('lateral')) {
728
+ throw new Error(`Lateral join can be used only with a sub-query.`);
729
+ }
557
730
  const [fromAlias, fromField] = this.helper.splitField(field);
558
- const entityName = this._aliases[fromAlias]?.entityName;
731
+ const q = (str) => `'${str}'`;
732
+ if (!this._aliases[fromAlias]) {
733
+ throw new Error(`Trying to join ${q(fromField)} with alias ${q(fromAlias)}, but ${q(fromAlias)} is not a known alias. Available aliases are: ${Object.keys(this._aliases).map(q).join(', ')}.`);
734
+ }
735
+ const entityName = this._aliases[fromAlias].entityName;
559
736
  const meta = this.metadata.get(entityName);
560
737
  const prop = meta.properties[fromField];
561
738
  if (!prop) {
562
- throw new Error(`Trying to join ${field}, but ${fromField} is not a defined relation on ${meta.className}`);
739
+ throw new Error(`Trying to join ${q(field)}, but ${q(fromField)} is not a defined relation on ${meta.className}.`);
563
740
  }
564
741
  this.createAlias(prop.type, alias);
565
742
  cond = core_1.QueryHelper.processWhere({
@@ -571,26 +748,26 @@ class QueryBuilder {
571
748
  aliased: !this.type || [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(this.type),
572
749
  });
573
750
  let aliasedName = `${fromAlias}.${prop.name}#${alias}`;
574
- path ?? (path = `${(Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? entityName)}.${prop.name}`);
575
- if (prop.reference === core_1.ReferenceType.ONE_TO_MANY) {
576
- this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
751
+ path ??= `${(Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? entityName)}.${prop.name}`;
752
+ if (prop.kind === core_1.ReferenceKind.ONE_TO_MANY) {
753
+ this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond, schema);
577
754
  }
578
- else if (prop.reference === core_1.ReferenceType.MANY_TO_MANY) {
755
+ else if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY) {
579
756
  let pivotAlias = alias;
580
- if (type !== 'pivotJoin') {
757
+ if (type !== enums_1.JoinType.pivotJoin) {
581
758
  const oldPivotAlias = this.getAliasForJoinPath(path + '[pivot]');
582
759
  pivotAlias = oldPivotAlias ?? this.getNextAlias(prop.pivotEntity);
583
760
  aliasedName = `${fromAlias}.${prop.name}#${pivotAlias}`;
584
761
  }
585
- const joins = this.helper.joinManyToManyReference(prop, fromAlias, alias, pivotAlias, type, cond, path);
762
+ const joins = this.helper.joinManyToManyReference(prop, fromAlias, alias, pivotAlias, type, cond, path, schema);
586
763
  Object.assign(this._joins, joins);
587
764
  this.createAlias(prop.pivotEntity, pivotAlias);
588
765
  }
589
- else if (prop.reference === core_1.ReferenceType.ONE_TO_ONE) {
590
- this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
766
+ else if (prop.kind === core_1.ReferenceKind.ONE_TO_ONE) {
767
+ this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond, schema);
591
768
  }
592
769
  else { // MANY_TO_ONE
593
- this._joins[aliasedName] = this.helper.joinManyToOneReference(prop, fromAlias, alias, type, cond);
770
+ this._joins[aliasedName] = this.helper.joinManyToOneReference(prop, fromAlias, alias, type, cond, schema);
594
771
  }
595
772
  if (!this._joins[aliasedName].path && path) {
596
773
  this._joins[aliasedName].path = path;
@@ -599,34 +776,49 @@ class QueryBuilder {
599
776
  }
600
777
  prepareFields(fields, type = 'where') {
601
778
  const ret = [];
779
+ const getFieldName = (name) => {
780
+ if (type === 'groupBy') {
781
+ return this.helper.mapper(name, this.type, undefined, null);
782
+ }
783
+ return this.helper.mapper(name, this.type);
784
+ };
602
785
  fields.forEach(field => {
786
+ const rawField = core_1.RawQueryFragment.getKnownFragment(field);
787
+ if (rawField) {
788
+ const sql = this.platform.formatQuery(rawField.sql, rawField.params);
789
+ ret.push(this.knex.raw(sql));
790
+ return;
791
+ }
603
792
  if (!core_1.Utils.isString(field)) {
604
- return ret.push(field);
793
+ ret.push(field);
794
+ return;
605
795
  }
606
796
  const join = Object.keys(this._joins).find(k => field === k.substring(0, k.indexOf('#')));
607
797
  if (join && type === 'where') {
608
- return ret.push(...this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[join]));
798
+ ret.push(...this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[join]));
799
+ return;
609
800
  }
610
801
  const [a, f] = this.helper.splitField(field);
611
802
  const prop = this.helper.getProperty(f, a);
612
803
  /* istanbul ignore next */
613
- if (prop && [core_1.ReferenceType.ONE_TO_MANY, core_1.ReferenceType.MANY_TO_MANY].includes(prop.reference)) {
804
+ if (prop && [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
614
805
  return;
615
806
  }
616
807
  if (prop?.embedded) {
617
- const fieldName = this.helper.mapper(prop.fieldNames[0], this.type);
618
- ret.push(fieldName);
808
+ const name = prop.embeddedPath?.join('.') ?? prop.fieldNames[0];
809
+ const aliased = this._aliases[a] ? `${a}.${name}` : name;
810
+ ret.push(getFieldName(aliased));
619
811
  return;
620
812
  }
621
- if (prop?.reference === core_1.ReferenceType.EMBEDDED) {
813
+ if (prop?.kind === core_1.ReferenceKind.EMBEDDED) {
622
814
  if (prop.object) {
623
- ret.push(this.helper.mapper(prop.fieldNames[0], this.type));
815
+ ret.push(getFieldName(prop.fieldNames[0]));
624
816
  }
625
817
  else {
626
818
  const nest = (prop) => {
627
819
  for (const childProp of Object.values(prop.embeddedProps)) {
628
- if (childProp.fieldNames) {
629
- ret.push(this.helper.mapper(childProp.fieldNames[0], this.type));
820
+ if (childProp.fieldNames && (childProp.kind !== core_1.ReferenceKind.EMBEDDED || childProp.object) && childProp.persist !== false) {
821
+ ret.push(getFieldName(childProp.fieldNames[0]));
630
822
  }
631
823
  else {
632
824
  nest(childProp);
@@ -637,11 +829,15 @@ class QueryBuilder {
637
829
  }
638
830
  return;
639
831
  }
640
- ret.push(this.helper.mapper(field, this.type));
832
+ if (prop && prop.fieldNames.length > 1) {
833
+ ret.push(...prop.fieldNames.map(f => getFieldName(f)));
834
+ return;
835
+ }
836
+ ret.push(getFieldName(field));
641
837
  });
642
838
  const meta = this.mainAlias.metadata;
643
839
  /* istanbul ignore next */
644
- const requiresSQLConversion = meta?.props.filter(p => p.customType?.convertToJSValueSQL) ?? [];
840
+ const requiresSQLConversion = meta?.props.filter(p => p.hasConvertToJSValueSQL && p.persist !== false) ?? [];
645
841
  if (this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES) && (fields.includes('*') || fields.includes(`${this.mainAlias.aliasName}.*`)) && requiresSQLConversion.length > 0) {
646
842
  requiresSQLConversion.forEach(p => ret.push(this.helper.mapper(p.name, this.type)));
647
843
  }
@@ -650,7 +846,7 @@ class QueryBuilder {
650
846
  const cols = this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[f]);
651
847
  ret.push(...cols);
652
848
  }
653
- if (this._joins[f].prop.reference !== core_1.ReferenceType.ONE_TO_ONE && this._joins[f].inverseJoinColumns) {
849
+ if (this._joins[f].prop.kind !== core_1.ReferenceKind.ONE_TO_ONE && this._joins[f].inverseJoinColumns) {
654
850
  this._joins[f].inverseJoinColumns.forEach(inverseJoinColumn => {
655
851
  core_1.Utils.renameKey(this._cond, inverseJoinColumn, `${this._joins[f].alias}.${inverseJoinColumn}`);
656
852
  });
@@ -669,10 +865,9 @@ class QueryBuilder {
669
865
  }
670
866
  if (data) {
671
867
  if (core_1.Utils.isEntity(data)) {
672
- // data = helper(data).toJSON();
673
868
  data = this.em?.getComparator().prepareEntity(data) ?? (0, core_1.serialize)(data);
674
869
  }
675
- this._data = this.helper.processData(data, this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES));
870
+ this._data = this.helper.processData(data, this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES), false);
676
871
  }
677
872
  if (cond) {
678
873
  this.where(cond);
@@ -682,6 +877,8 @@ class QueryBuilder {
682
877
  getQueryBase() {
683
878
  const qb = this.getKnex();
684
879
  const schema = this.getSchema(this.mainAlias);
880
+ // Joined tables doesn't need to belong to the same schema as the main table
881
+ const joinSchema = this._schema ?? this.em?.schema ?? schema;
685
882
  if (schema) {
686
883
  qb.withSchema(schema);
687
884
  }
@@ -700,12 +897,12 @@ class QueryBuilder {
700
897
  else if (this.flags.has(core_1.QueryFlag.DISTINCT)) {
701
898
  qb.distinct();
702
899
  }
703
- this.helper.processJoins(qb, this._joins, schema);
900
+ this.helper.processJoins(qb, this._joins, joinSchema);
704
901
  break;
705
902
  case enums_1.QueryType.COUNT: {
706
903
  const m = this.flags.has(core_1.QueryFlag.DISTINCT) ? 'countDistinct' : 'count';
707
904
  qb[m]({ count: this._fields.map(f => this.helper.mapper(f, this.type)) });
708
- this.helper.processJoins(qb, this._joins, schema);
905
+ this.helper.processJoins(qb, this._joins, joinSchema);
709
906
  break;
710
907
  }
711
908
  case enums_1.QueryType.INSERT:
@@ -724,6 +921,24 @@ class QueryBuilder {
724
921
  }
725
922
  return qb;
726
923
  }
924
+ applyDiscriminatorCondition() {
925
+ const meta = this.mainAlias.metadata;
926
+ if (!meta?.discriminatorValue) {
927
+ return;
928
+ }
929
+ const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.find(cls));
930
+ const children = [];
931
+ const lookUpChildren = (ret, type) => {
932
+ const children = types.filter(meta2 => meta2.extends === type);
933
+ children.forEach(m => lookUpChildren(ret, m.className));
934
+ ret.push(...children.filter(c => c.discriminatorValue));
935
+ return children;
936
+ };
937
+ lookUpChildren(children, meta.className);
938
+ this.andWhere({
939
+ [meta.root.discriminatorColumn]: children.length > 0 ? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] } : meta.discriminatorValue,
940
+ });
941
+ }
727
942
  finalize() {
728
943
  if (this.finalized) {
729
944
  return;
@@ -732,10 +947,11 @@ class QueryBuilder {
732
947
  this.select('*');
733
948
  }
734
949
  const meta = this.mainAlias.metadata;
950
+ this.applyDiscriminatorCondition();
735
951
  if (meta && this.flags.has(core_1.QueryFlag.AUTO_JOIN_ONE_TO_ONE_OWNER)) {
736
952
  const relationsToPopulate = this._populate.map(({ field }) => field);
737
953
  meta.relations
738
- .filter(prop => prop.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
954
+ .filter(prop => prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
739
955
  .map(prop => ({ field: prop.name }))
740
956
  .forEach(item => this._populate.push(item));
741
957
  }
@@ -744,16 +960,14 @@ class QueryBuilder {
744
960
  const aliasedField = `${fromAlias}.${fromField}`;
745
961
  const join = Object.keys(this._joins).find(k => `${aliasedField}#${this._joins[k].alias}` === k);
746
962
  if (join && this._joins[join] && this.helper.isOneToOneInverse(fromField)) {
747
- return this._populateMap[join] = this._joins[join].alias;
748
- }
749
- if (this.metadata.find(field)?.pivotTable) { // pivot table entity
750
- this.autoJoinPivotTable(field);
963
+ this._populateMap[join] = this._joins[join].alias;
964
+ return;
751
965
  }
752
- else if (meta && this.helper.isOneToOneInverse(fromField)) {
966
+ if (meta && this.helper.isOneToOneInverse(fromField)) {
753
967
  const prop = meta.properties[fromField];
754
968
  const alias = this.getNextAlias(prop.pivotEntity ?? prop.type);
755
969
  const aliasedName = `${fromAlias}.${prop.name}#${alias}`;
756
- this._joins[aliasedName] = this.helper.joinOneToReference(prop, this.mainAlias.aliasName, alias, 'leftJoin');
970
+ this._joins[aliasedName] = this.helper.joinOneToReference(prop, this.mainAlias.aliasName, alias, enums_1.JoinType.leftJoin);
757
971
  this._populateMap[aliasedName] = this._joins[aliasedName].alias;
758
972
  }
759
973
  });
@@ -765,14 +979,20 @@ class QueryBuilder {
765
979
  const aliased = this.knex.ref(prop.fieldNames[0]).toString();
766
980
  return `${prop.formula(alias)} as ${aliased}`;
767
981
  })
768
- .filter(field => !this._fields.includes(field))
769
- .forEach(field => this.addSelect(field));
982
+ .filter(field => !this._fields.some(f => {
983
+ if (f instanceof core_1.RawQueryFragment) {
984
+ return f.sql === field && f.params.length === 0;
985
+ }
986
+ return f === field;
987
+ }))
988
+ .forEach(field => this._fields.push((0, core_1.raw)(field)));
770
989
  }
990
+ this.processPopulateWhere();
771
991
  core_1.QueryHelper.processObjectParams(this._data);
772
992
  core_1.QueryHelper.processObjectParams(this._cond);
773
993
  core_1.QueryHelper.processObjectParams(this._having);
774
- // automatically enable paginate flag when we detect to-many joins
775
- if (!this.flags.has(core_1.QueryFlag.DISABLE_PAGINATE) && this.hasToManyJoins()) {
994
+ // automatically enable paginate flag when we detect to-many joins, but only if there is no `group by` clause
995
+ if (!this.flags.has(core_1.QueryFlag.DISABLE_PAGINATE) && this._groupBy.length === 0 && this.hasToManyJoins()) {
776
996
  this.flags.add(core_1.QueryFlag.PAGINATE);
777
997
  }
778
998
  if (meta && this.flags.has(core_1.QueryFlag.PAGINATE) && (this._limit > 0 || this._offset > 0)) {
@@ -783,17 +1003,54 @@ class QueryBuilder {
783
1003
  }
784
1004
  this.finalized = true;
785
1005
  }
1006
+ processPopulateWhere() {
1007
+ if (this._populateWhere == null || this._populateWhere === core_1.PopulateHint.ALL) {
1008
+ return;
1009
+ }
1010
+ const joins = Object.values(this._joins);
1011
+ joins.forEach(join => {
1012
+ join.cond_ = join.cond;
1013
+ join.cond = {};
1014
+ });
1015
+ const replaceOnConditions = (cond) => {
1016
+ Object.keys(cond).forEach(k => {
1017
+ if (core_1.Utils.isOperator(k)) {
1018
+ if (Array.isArray(cond[k])) {
1019
+ return cond[k].forEach((c) => replaceOnConditions(c));
1020
+ }
1021
+ return replaceOnConditions(cond[k]);
1022
+ }
1023
+ const [a] = this.helper.splitField(k);
1024
+ const join = joins.find(j => j.alias === a);
1025
+ if (join) {
1026
+ join.cond = { ...join.cond, [k]: cond[k] };
1027
+ }
1028
+ });
1029
+ };
1030
+ if (this._populateWhere === core_1.PopulateHint.INFER) {
1031
+ replaceOnConditions(this._cond);
1032
+ }
1033
+ else if (typeof this._populateWhere === 'object') {
1034
+ const cond = CriteriaNodeFactory_1.CriteriaNodeFactory
1035
+ .createNode(this.metadata, this.mainAlias.entityName, this._populateWhere)
1036
+ .process(this);
1037
+ replaceOnConditions(cond);
1038
+ }
1039
+ }
786
1040
  hasToManyJoins() {
787
1041
  return Object.values(this._joins).some(join => {
788
- return [core_1.ReferenceType.ONE_TO_MANY, core_1.ReferenceType.MANY_TO_MANY].includes(join.prop.reference);
1042
+ return [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(join.prop.kind);
789
1043
  });
790
1044
  }
791
1045
  wrapPaginateSubQuery(meta) {
792
1046
  const pks = this.prepareFields(meta.primaryKeys, 'sub-query');
793
1047
  const subQuery = this.clone().select(pks).groupBy(pks).limit(this._limit);
1048
+ // revert the on conditions added via populateWhere, we want to apply those only once
1049
+ Object.values(subQuery._joins).forEach(join => join.cond = join.cond_ ?? {});
794
1050
  if (this._offset) {
795
1051
  subQuery.offset(this._offset);
796
1052
  }
1053
+ const addToSelect = [];
797
1054
  if (this._orderBy.length > 0) {
798
1055
  const orderBy = [];
799
1056
  for (const orderMap of this._orderBy) {
@@ -801,28 +1058,38 @@ class QueryBuilder {
801
1058
  const [a, f] = this.helper.splitField(field);
802
1059
  const prop = this.helper.getProperty(f, a);
803
1060
  const type = this.platform.castColumn(prop);
804
- orderBy.push({
805
- [`min(${this.ref(this.helper.mapper(field, this.type, undefined, null))}${type})`]: direction,
806
- });
1061
+ const fieldName = this.helper.mapper(field, this.type, undefined, null);
1062
+ if (!prop?.persist && !prop?.formula) {
1063
+ addToSelect.push(fieldName);
1064
+ }
1065
+ orderBy.push({ [(0, core_1.raw)(`min(${this.knex.ref(fieldName)}${type})`)]: direction });
807
1066
  }
808
1067
  }
809
1068
  subQuery.orderBy(orderBy);
810
1069
  }
811
1070
  subQuery.finalized = true;
812
1071
  const knexQuery = subQuery.as(this.mainAlias.aliasName).clearSelect().select(pks);
1072
+ if (addToSelect.length > 0) {
1073
+ addToSelect.forEach(prop => {
1074
+ const field = this._fields.find(field => {
1075
+ if (typeof field === 'object' && field && '__as' in field) {
1076
+ return field.__as === prop;
1077
+ }
1078
+ // not perfect, but should work most of the time, ideally we should check only the alias (`... as alias`)
1079
+ return field.toString().includes(prop);
1080
+ });
1081
+ if (field) {
1082
+ knexQuery.select(field);
1083
+ }
1084
+ });
1085
+ }
813
1086
  // multiple sub-queries are needed to get around mysql limitations with order by + limit + where in + group by (o.O)
814
1087
  // https://stackoverflow.com/questions/17892762/mysql-this-version-of-mysql-doesnt-yet-support-limit-in-all-any-some-subqu
815
1088
  const subSubQuery = this.getKnex().select(pks).from(knexQuery);
1089
+ subSubQuery.__raw = true; // tag it as there is now way to check via `instanceof`
816
1090
  this._limit = undefined;
817
1091
  this._offset = undefined;
818
- const cond = this._cond;
819
1092
  this.select(this._fields).where({ [core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: { $in: subSubQuery } });
820
- if (this._populateWhere === core_1.PopulateHint.INFER) {
821
- this.andWhere(cond);
822
- }
823
- else if (typeof this._populateWhere === 'object') {
824
- this.andWhere(this._populateWhere);
825
- }
826
1093
  }
827
1094
  wrapModifySubQuery(meta) {
828
1095
  const subQuery = this.clone();
@@ -836,21 +1103,10 @@ class QueryBuilder {
836
1103
  [core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: { $in: subSubQuery },
837
1104
  });
838
1105
  }
839
- autoJoinPivotTable(field) {
840
- const pivotMeta = this.metadata.find(field);
841
- const owner = pivotMeta.relations[0];
842
- const inverse = pivotMeta.relations[1];
843
- const prop = this._cond[pivotMeta.name + '.' + owner.name] || this._orderBy[pivotMeta.name + '.' + owner.name] ? inverse : owner;
844
- const pivotAlias = this.getNextAlias(pivotMeta.name);
845
- this._joins[field] = this.helper.joinPivotTable(field, prop, this.mainAlias.aliasName, pivotAlias, 'leftJoin');
846
- core_1.Utils.renameKey(this._cond, `${field}.${owner.name}`, core_1.Utils.getPrimaryKeyHash(owner.fieldNames.map(fieldName => `${pivotAlias}.${fieldName}`)));
847
- core_1.Utils.renameKey(this._cond, `${field}.${inverse.name}`, core_1.Utils.getPrimaryKeyHash(inverse.fieldNames.map(fieldName => `${pivotAlias}.${fieldName}`)));
848
- this._populateMap[field] = this._joins[field].alias;
849
- }
850
1106
  getSchema(alias) {
851
1107
  const { metadata } = alias;
852
1108
  const metaSchema = metadata?.schema && metadata.schema !== '*' ? metadata.schema : undefined;
853
- return this._schema ?? metaSchema ?? this.em?.config.get('schema');
1109
+ return this._schema ?? metaSchema ?? this.em?.schema ?? this.em?.config.get('schema');
854
1110
  }
855
1111
  createAlias(entityName, aliasName, subQuery) {
856
1112
  const metadata = this.metadata.find(entityName);
@@ -866,11 +1122,11 @@ class QueryBuilder {
866
1122
  fromSubQuery(target, aliasName) {
867
1123
  const subQuery = target.getKnexQuery();
868
1124
  const { entityName } = target.mainAlias;
869
- aliasName ?? (aliasName = this.getNextAlias(entityName));
1125
+ aliasName ??= this.getNextAlias(entityName);
870
1126
  this.createMainAlias(entityName, aliasName, subQuery);
871
1127
  }
872
1128
  fromEntityName(entityName, aliasName) {
873
- aliasName ?? (aliasName = this._mainAlias?.aliasName ?? this.getNextAlias(entityName));
1129
+ aliasName ??= this._mainAlias?.aliasName ?? this.getNextAlias(entityName);
874
1130
  this.createMainAlias(entityName, aliasName);
875
1131
  }
876
1132
  createQueryBuilderHelper() {
@@ -888,6 +1144,7 @@ class QueryBuilder {
888
1144
  }
889
1145
  }
890
1146
  /* istanbul ignore next */
1147
+ /** @ignore */
891
1148
  [util_1.inspect.custom](depth) {
892
1149
  const object = { ...this };
893
1150
  const hidden = ['metadata', 'driver', 'context', 'platform', 'knex', 'type'];
@@ -898,6 +1155,9 @@ class QueryBuilder {
898
1155
  if (this._data) {
899
1156
  object.data = this._data;
900
1157
  }
1158
+ if (this._schema) {
1159
+ object.schema = this._schema;
1160
+ }
901
1161
  if (!core_1.Utils.isEmpty(this._cond)) {
902
1162
  object.where = this._cond;
903
1163
  }