@mikro-orm/knex 6.0.0-dev.14 → 6.0.0-dev.140

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 (43) 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 +394 -225
  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 -6
  17. package/package.json +8 -8
  18. package/query/ArrayCriteriaNode.d.ts +3 -3
  19. package/query/CriteriaNode.d.ts +9 -8
  20. package/query/CriteriaNode.js +17 -9
  21. package/query/CriteriaNodeFactory.d.ts +6 -6
  22. package/query/CriteriaNodeFactory.js +17 -13
  23. package/query/ObjectCriteriaNode.d.ts +3 -3
  24. package/query/ObjectCriteriaNode.js +14 -10
  25. package/query/QueryBuilder.d.ts +50 -21
  26. package/query/QueryBuilder.js +389 -132
  27. package/query/QueryBuilderHelper.d.ts +11 -16
  28. package/query/QueryBuilderHelper.js +194 -155
  29. package/query/ScalarCriteriaNode.d.ts +3 -2
  30. package/query/ScalarCriteriaNode.js +10 -6
  31. package/query/enums.d.ts +7 -0
  32. package/query/enums.js +10 -2
  33. package/schema/DatabaseSchema.d.ts +5 -2
  34. package/schema/DatabaseSchema.js +27 -9
  35. package/schema/DatabaseTable.d.ts +4 -2
  36. package/schema/DatabaseTable.js +67 -32
  37. package/schema/SchemaComparator.d.ts +3 -3
  38. package/schema/SchemaComparator.js +50 -30
  39. package/schema/SchemaHelper.d.ts +6 -4
  40. package/schema/SchemaHelper.js +18 -7
  41. package/schema/SqlSchemaGenerator.d.ts +3 -6
  42. package/schema/SqlSchemaGenerator.js +69 -34
  43. package/typings.d.ts +17 -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
+ });
467
591
  }
468
- return res.map(r => this.em.map(this.mainAlias.entityName, r, { schema: this._schema }));
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);
596
+ }
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,7 +658,10 @@ 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
666
  clone() {
522
667
  const qb = new QueryBuilder(this.mainAlias.entityName, this.metadata, this.driver, this.context, this.mainAlias.aliasName, this.connectionType, this.em);
@@ -524,7 +669,8 @@ class QueryBuilder {
524
669
  // clone array/object properties
525
670
  const properties = [
526
671
  'flags', '_populate', '_populateWhere', '_populateMap', '_joins', '_joinedProps', '_cond', '_data', '_orderBy',
527
- '_schema', '_indexHint', '_cache', 'subQueries', 'lockMode', 'lockTables',
672
+ '_schema', '_indexHint', '_cache', 'subQueries', 'lockMode', 'lockTables', '_groupBy', '_having', '_returning',
673
+ '_comments', '_hintComments',
528
674
  ];
529
675
  properties.forEach(prop => qb[prop] = core_1.Utils.copy(this[prop]));
530
676
  /* istanbul ignore else */
@@ -552,14 +698,42 @@ class QueryBuilder {
552
698
  }
553
699
  return qb;
554
700
  }
555
- joinReference(field, alias, cond, type, path) {
701
+ joinReference(field, alias, cond, type, path, schema, subquery) {
556
702
  this.ensureNotFinalized();
703
+ if (typeof field === 'object') {
704
+ const prop = {
705
+ name: '__subquery__',
706
+ kind: core_1.ReferenceKind.MANY_TO_ONE,
707
+ };
708
+ if (field instanceof QueryBuilder) {
709
+ prop.type = field.mainAlias.entityName;
710
+ prop.targetMeta = field.mainAlias.metadata;
711
+ field = field.getKnexQuery();
712
+ }
713
+ this._joins[`${this.alias}.${prop.name}#${alias}`] = {
714
+ prop,
715
+ alias,
716
+ type,
717
+ cond,
718
+ schema,
719
+ subquery: field.toString(),
720
+ ownerAlias: this.alias,
721
+ };
722
+ return prop;
723
+ }
724
+ if (!subquery && type.includes('lateral')) {
725
+ throw new Error(`Lateral join can be used only with a sub-query.`);
726
+ }
557
727
  const [fromAlias, fromField] = this.helper.splitField(field);
558
- const entityName = this._aliases[fromAlias]?.entityName;
728
+ const q = (str) => `'${str}'`;
729
+ if (!this._aliases[fromAlias]) {
730
+ 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(', ')}.`);
731
+ }
732
+ const entityName = this._aliases[fromAlias].entityName;
559
733
  const meta = this.metadata.get(entityName);
560
734
  const prop = meta.properties[fromField];
561
735
  if (!prop) {
562
- throw new Error(`Trying to join ${field}, but ${fromField} is not a defined relation on ${meta.className}`);
736
+ throw new Error(`Trying to join ${q(field)}, but ${q(fromField)} is not a defined relation on ${meta.className}.`);
563
737
  }
564
738
  this.createAlias(prop.type, alias);
565
739
  cond = core_1.QueryHelper.processWhere({
@@ -571,26 +745,26 @@ class QueryBuilder {
571
745
  aliased: !this.type || [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(this.type),
572
746
  });
573
747
  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);
748
+ path ??= `${(Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? entityName)}.${prop.name}`;
749
+ if (prop.kind === core_1.ReferenceKind.ONE_TO_MANY) {
750
+ this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond, schema);
577
751
  }
578
- else if (prop.reference === core_1.ReferenceType.MANY_TO_MANY) {
752
+ else if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY) {
579
753
  let pivotAlias = alias;
580
- if (type !== 'pivotJoin') {
754
+ if (type !== enums_1.JoinType.pivotJoin) {
581
755
  const oldPivotAlias = this.getAliasForJoinPath(path + '[pivot]');
582
756
  pivotAlias = oldPivotAlias ?? this.getNextAlias(prop.pivotEntity);
583
757
  aliasedName = `${fromAlias}.${prop.name}#${pivotAlias}`;
584
758
  }
585
- const joins = this.helper.joinManyToManyReference(prop, fromAlias, alias, pivotAlias, type, cond, path);
759
+ const joins = this.helper.joinManyToManyReference(prop, fromAlias, alias, pivotAlias, type, cond, path, schema);
586
760
  Object.assign(this._joins, joins);
587
761
  this.createAlias(prop.pivotEntity, pivotAlias);
588
762
  }
589
- else if (prop.reference === core_1.ReferenceType.ONE_TO_ONE) {
590
- this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
763
+ else if (prop.kind === core_1.ReferenceKind.ONE_TO_ONE) {
764
+ this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond, schema);
591
765
  }
592
766
  else { // MANY_TO_ONE
593
- this._joins[aliasedName] = this.helper.joinManyToOneReference(prop, fromAlias, alias, type, cond);
767
+ this._joins[aliasedName] = this.helper.joinManyToOneReference(prop, fromAlias, alias, type, cond, schema);
594
768
  }
595
769
  if (!this._joins[aliasedName].path && path) {
596
770
  this._joins[aliasedName].path = path;
@@ -599,34 +773,49 @@ class QueryBuilder {
599
773
  }
600
774
  prepareFields(fields, type = 'where') {
601
775
  const ret = [];
776
+ const getFieldName = (name) => {
777
+ if (type === 'groupBy') {
778
+ return this.helper.mapper(name, this.type, undefined, null);
779
+ }
780
+ return this.helper.mapper(name, this.type);
781
+ };
602
782
  fields.forEach(field => {
783
+ const rawField = core_1.RawQueryFragment.getKnownFragment(field);
784
+ if (rawField) {
785
+ const sql = this.platform.formatQuery(rawField.sql, rawField.params);
786
+ ret.push(this.knex.raw(sql));
787
+ return;
788
+ }
603
789
  if (!core_1.Utils.isString(field)) {
604
- return ret.push(field);
790
+ ret.push(field);
791
+ return;
605
792
  }
606
793
  const join = Object.keys(this._joins).find(k => field === k.substring(0, k.indexOf('#')));
607
794
  if (join && type === 'where') {
608
- return ret.push(...this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[join]));
795
+ ret.push(...this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[join]));
796
+ return;
609
797
  }
610
798
  const [a, f] = this.helper.splitField(field);
611
799
  const prop = this.helper.getProperty(f, a);
612
800
  /* istanbul ignore next */
613
- if (prop && [core_1.ReferenceType.ONE_TO_MANY, core_1.ReferenceType.MANY_TO_MANY].includes(prop.reference)) {
801
+ if (prop && [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
614
802
  return;
615
803
  }
616
804
  if (prop?.embedded) {
617
- const fieldName = this.helper.mapper(prop.fieldNames[0], this.type);
618
- ret.push(fieldName);
805
+ const name = prop.embeddedPath?.join('.') ?? prop.fieldNames[0];
806
+ const aliased = this._aliases[a] ? `${a}.${name}` : name;
807
+ ret.push(getFieldName(aliased));
619
808
  return;
620
809
  }
621
- if (prop?.reference === core_1.ReferenceType.EMBEDDED) {
810
+ if (prop?.kind === core_1.ReferenceKind.EMBEDDED) {
622
811
  if (prop.object) {
623
- ret.push(this.helper.mapper(prop.fieldNames[0], this.type));
812
+ ret.push(getFieldName(prop.fieldNames[0]));
624
813
  }
625
814
  else {
626
815
  const nest = (prop) => {
627
816
  for (const childProp of Object.values(prop.embeddedProps)) {
628
- if (childProp.fieldNames) {
629
- ret.push(this.helper.mapper(childProp.fieldNames[0], this.type));
817
+ if (childProp.fieldNames && (childProp.kind !== core_1.ReferenceKind.EMBEDDED || childProp.object) && childProp.persist !== false) {
818
+ ret.push(getFieldName(childProp.fieldNames[0]));
630
819
  }
631
820
  else {
632
821
  nest(childProp);
@@ -637,11 +826,15 @@ class QueryBuilder {
637
826
  }
638
827
  return;
639
828
  }
640
- ret.push(this.helper.mapper(field, this.type));
829
+ if (prop && prop.fieldNames.length > 1) {
830
+ ret.push(...prop.fieldNames.map(f => getFieldName(f)));
831
+ return;
832
+ }
833
+ ret.push(getFieldName(field));
641
834
  });
642
835
  const meta = this.mainAlias.metadata;
643
836
  /* istanbul ignore next */
644
- const requiresSQLConversion = meta?.props.filter(p => p.customType?.convertToJSValueSQL) ?? [];
837
+ const requiresSQLConversion = meta?.props.filter(p => p.hasConvertToJSValueSQL && p.persist !== false) ?? [];
645
838
  if (this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES) && (fields.includes('*') || fields.includes(`${this.mainAlias.aliasName}.*`)) && requiresSQLConversion.length > 0) {
646
839
  requiresSQLConversion.forEach(p => ret.push(this.helper.mapper(p.name, this.type)));
647
840
  }
@@ -650,7 +843,7 @@ class QueryBuilder {
650
843
  const cols = this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[f]);
651
844
  ret.push(...cols);
652
845
  }
653
- if (this._joins[f].prop.reference !== core_1.ReferenceType.ONE_TO_ONE && this._joins[f].inverseJoinColumns) {
846
+ if (this._joins[f].prop.kind !== core_1.ReferenceKind.ONE_TO_ONE && this._joins[f].inverseJoinColumns) {
654
847
  this._joins[f].inverseJoinColumns.forEach(inverseJoinColumn => {
655
848
  core_1.Utils.renameKey(this._cond, inverseJoinColumn, `${this._joins[f].alias}.${inverseJoinColumn}`);
656
849
  });
@@ -669,10 +862,9 @@ class QueryBuilder {
669
862
  }
670
863
  if (data) {
671
864
  if (core_1.Utils.isEntity(data)) {
672
- // data = helper(data).toJSON();
673
865
  data = this.em?.getComparator().prepareEntity(data) ?? (0, core_1.serialize)(data);
674
866
  }
675
- this._data = this.helper.processData(data, this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES));
867
+ this._data = this.helper.processData(data, this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES), false);
676
868
  }
677
869
  if (cond) {
678
870
  this.where(cond);
@@ -682,6 +874,8 @@ class QueryBuilder {
682
874
  getQueryBase() {
683
875
  const qb = this.getKnex();
684
876
  const schema = this.getSchema(this.mainAlias);
877
+ // Joined tables doesn't need to belong to the same schema as the main table
878
+ const joinSchema = this._schema ?? this.em?.schema ?? schema;
685
879
  if (schema) {
686
880
  qb.withSchema(schema);
687
881
  }
@@ -700,12 +894,12 @@ class QueryBuilder {
700
894
  else if (this.flags.has(core_1.QueryFlag.DISTINCT)) {
701
895
  qb.distinct();
702
896
  }
703
- this.helper.processJoins(qb, this._joins, schema);
897
+ this.helper.processJoins(qb, this._joins, joinSchema);
704
898
  break;
705
899
  case enums_1.QueryType.COUNT: {
706
900
  const m = this.flags.has(core_1.QueryFlag.DISTINCT) ? 'countDistinct' : 'count';
707
901
  qb[m]({ count: this._fields.map(f => this.helper.mapper(f, this.type)) });
708
- this.helper.processJoins(qb, this._joins, schema);
902
+ this.helper.processJoins(qb, this._joins, joinSchema);
709
903
  break;
710
904
  }
711
905
  case enums_1.QueryType.INSERT:
@@ -724,6 +918,24 @@ class QueryBuilder {
724
918
  }
725
919
  return qb;
726
920
  }
921
+ applyDiscriminatorCondition() {
922
+ const meta = this.mainAlias.metadata;
923
+ if (!meta?.discriminatorValue) {
924
+ return;
925
+ }
926
+ const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.find(cls));
927
+ const children = [];
928
+ const lookUpChildren = (ret, type) => {
929
+ const children = types.filter(meta2 => meta2.extends === type);
930
+ children.forEach(m => lookUpChildren(ret, m.className));
931
+ ret.push(...children.filter(c => c.discriminatorValue));
932
+ return children;
933
+ };
934
+ lookUpChildren(children, meta.className);
935
+ this.andWhere({
936
+ [meta.root.discriminatorColumn]: children.length > 0 ? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] } : meta.discriminatorValue,
937
+ });
938
+ }
727
939
  finalize() {
728
940
  if (this.finalized) {
729
941
  return;
@@ -732,10 +944,11 @@ class QueryBuilder {
732
944
  this.select('*');
733
945
  }
734
946
  const meta = this.mainAlias.metadata;
947
+ this.applyDiscriminatorCondition();
735
948
  if (meta && this.flags.has(core_1.QueryFlag.AUTO_JOIN_ONE_TO_ONE_OWNER)) {
736
949
  const relationsToPopulate = this._populate.map(({ field }) => field);
737
950
  meta.relations
738
- .filter(prop => prop.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
951
+ .filter(prop => prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
739
952
  .map(prop => ({ field: prop.name }))
740
953
  .forEach(item => this._populate.push(item));
741
954
  }
@@ -744,16 +957,14 @@ class QueryBuilder {
744
957
  const aliasedField = `${fromAlias}.${fromField}`;
745
958
  const join = Object.keys(this._joins).find(k => `${aliasedField}#${this._joins[k].alias}` === k);
746
959
  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);
960
+ this._populateMap[join] = this._joins[join].alias;
961
+ return;
751
962
  }
752
- else if (meta && this.helper.isOneToOneInverse(fromField)) {
963
+ if (meta && this.helper.isOneToOneInverse(fromField)) {
753
964
  const prop = meta.properties[fromField];
754
965
  const alias = this.getNextAlias(prop.pivotEntity ?? prop.type);
755
966
  const aliasedName = `${fromAlias}.${prop.name}#${alias}`;
756
- this._joins[aliasedName] = this.helper.joinOneToReference(prop, this.mainAlias.aliasName, alias, 'leftJoin');
967
+ this._joins[aliasedName] = this.helper.joinOneToReference(prop, this.mainAlias.aliasName, alias, enums_1.JoinType.leftJoin);
757
968
  this._populateMap[aliasedName] = this._joins[aliasedName].alias;
758
969
  }
759
970
  });
@@ -765,14 +976,20 @@ class QueryBuilder {
765
976
  const aliased = this.knex.ref(prop.fieldNames[0]).toString();
766
977
  return `${prop.formula(alias)} as ${aliased}`;
767
978
  })
768
- .filter(field => !this._fields.includes(field))
769
- .forEach(field => this.addSelect(field));
979
+ .filter(field => !this._fields.some(f => {
980
+ if (f instanceof core_1.RawQueryFragment) {
981
+ return f.sql === field && f.params.length === 0;
982
+ }
983
+ return f === field;
984
+ }))
985
+ .forEach(field => this._fields.push((0, core_1.raw)(field)));
770
986
  }
987
+ this.processPopulateWhere();
771
988
  core_1.QueryHelper.processObjectParams(this._data);
772
989
  core_1.QueryHelper.processObjectParams(this._cond);
773
990
  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()) {
991
+ // automatically enable paginate flag when we detect to-many joins, but only if there is no `group by` clause
992
+ if (!this.flags.has(core_1.QueryFlag.DISABLE_PAGINATE) && this._groupBy.length === 0 && this.hasToManyJoins()) {
776
993
  this.flags.add(core_1.QueryFlag.PAGINATE);
777
994
  }
778
995
  if (meta && this.flags.has(core_1.QueryFlag.PAGINATE) && (this._limit > 0 || this._offset > 0)) {
@@ -783,17 +1000,54 @@ class QueryBuilder {
783
1000
  }
784
1001
  this.finalized = true;
785
1002
  }
1003
+ processPopulateWhere() {
1004
+ if (this._populateWhere == null || this._populateWhere === core_1.PopulateHint.ALL) {
1005
+ return;
1006
+ }
1007
+ const joins = Object.values(this._joins);
1008
+ joins.forEach(join => {
1009
+ join.cond_ = join.cond;
1010
+ join.cond = {};
1011
+ });
1012
+ const replaceOnConditions = (cond) => {
1013
+ Object.keys(cond).forEach(k => {
1014
+ if (core_1.Utils.isOperator(k)) {
1015
+ if (Array.isArray(cond[k])) {
1016
+ return cond[k].forEach((c) => replaceOnConditions(c));
1017
+ }
1018
+ return replaceOnConditions(cond[k]);
1019
+ }
1020
+ const [a] = this.helper.splitField(k);
1021
+ const join = joins.find(j => j.alias === a);
1022
+ if (join) {
1023
+ join.cond = { ...join.cond, [k]: cond[k] };
1024
+ }
1025
+ });
1026
+ };
1027
+ if (this._populateWhere === core_1.PopulateHint.INFER) {
1028
+ replaceOnConditions(this._cond);
1029
+ }
1030
+ else if (typeof this._populateWhere === 'object') {
1031
+ const cond = CriteriaNodeFactory_1.CriteriaNodeFactory
1032
+ .createNode(this.metadata, this.mainAlias.entityName, this._populateWhere)
1033
+ .process(this);
1034
+ replaceOnConditions(cond);
1035
+ }
1036
+ }
786
1037
  hasToManyJoins() {
787
1038
  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);
1039
+ return [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(join.prop.kind);
789
1040
  });
790
1041
  }
791
1042
  wrapPaginateSubQuery(meta) {
792
1043
  const pks = this.prepareFields(meta.primaryKeys, 'sub-query');
793
1044
  const subQuery = this.clone().select(pks).groupBy(pks).limit(this._limit);
1045
+ // revert the on conditions added via populateWhere, we want to apply those only once
1046
+ Object.values(subQuery._joins).forEach(join => join.cond = join.cond_ ?? {});
794
1047
  if (this._offset) {
795
1048
  subQuery.offset(this._offset);
796
1049
  }
1050
+ const addToSelect = [];
797
1051
  if (this._orderBy.length > 0) {
798
1052
  const orderBy = [];
799
1053
  for (const orderMap of this._orderBy) {
@@ -801,28 +1055,38 @@ class QueryBuilder {
801
1055
  const [a, f] = this.helper.splitField(field);
802
1056
  const prop = this.helper.getProperty(f, a);
803
1057
  const type = this.platform.castColumn(prop);
804
- orderBy.push({
805
- [`min(${this.ref(this.helper.mapper(field, this.type, undefined, null))}${type})`]: direction,
806
- });
1058
+ const fieldName = this.helper.mapper(field, this.type, undefined, null);
1059
+ if (!prop?.persist && !prop?.formula) {
1060
+ addToSelect.push(fieldName);
1061
+ }
1062
+ orderBy.push({ [(0, core_1.raw)(`min(${this.knex.ref(fieldName)}${type})`)]: direction });
807
1063
  }
808
1064
  }
809
1065
  subQuery.orderBy(orderBy);
810
1066
  }
811
1067
  subQuery.finalized = true;
812
1068
  const knexQuery = subQuery.as(this.mainAlias.aliasName).clearSelect().select(pks);
1069
+ if (addToSelect.length > 0) {
1070
+ addToSelect.forEach(prop => {
1071
+ const field = this._fields.find(field => {
1072
+ if (typeof field === 'object' && field && '__as' in field) {
1073
+ return field.__as === prop;
1074
+ }
1075
+ // not perfect, but should work most of the time, ideally we should check only the alias (`... as alias`)
1076
+ return field.toString().includes(prop);
1077
+ });
1078
+ if (field) {
1079
+ knexQuery.select(field);
1080
+ }
1081
+ });
1082
+ }
813
1083
  // multiple sub-queries are needed to get around mysql limitations with order by + limit + where in + group by (o.O)
814
1084
  // https://stackoverflow.com/questions/17892762/mysql-this-version-of-mysql-doesnt-yet-support-limit-in-all-any-some-subqu
815
1085
  const subSubQuery = this.getKnex().select(pks).from(knexQuery);
1086
+ subSubQuery.__raw = true; // tag it as there is now way to check via `instanceof`
816
1087
  this._limit = undefined;
817
1088
  this._offset = undefined;
818
- const cond = this._cond;
819
1089
  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
1090
  }
827
1091
  wrapModifySubQuery(meta) {
828
1092
  const subQuery = this.clone();
@@ -836,21 +1100,10 @@ class QueryBuilder {
836
1100
  [core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: { $in: subSubQuery },
837
1101
  });
838
1102
  }
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
1103
  getSchema(alias) {
851
1104
  const { metadata } = alias;
852
1105
  const metaSchema = metadata?.schema && metadata.schema !== '*' ? metadata.schema : undefined;
853
- return this._schema ?? metaSchema ?? this.em?.config.get('schema');
1106
+ return this._schema ?? metaSchema ?? this.em?.schema ?? this.em?.config.get('schema');
854
1107
  }
855
1108
  createAlias(entityName, aliasName, subQuery) {
856
1109
  const metadata = this.metadata.find(entityName);
@@ -866,11 +1119,11 @@ class QueryBuilder {
866
1119
  fromSubQuery(target, aliasName) {
867
1120
  const subQuery = target.getKnexQuery();
868
1121
  const { entityName } = target.mainAlias;
869
- aliasName ?? (aliasName = this.getNextAlias(entityName));
1122
+ aliasName ??= this.getNextAlias(entityName);
870
1123
  this.createMainAlias(entityName, aliasName, subQuery);
871
1124
  }
872
1125
  fromEntityName(entityName, aliasName) {
873
- aliasName ?? (aliasName = this._mainAlias?.aliasName ?? this.getNextAlias(entityName));
1126
+ aliasName ??= this._mainAlias?.aliasName ?? this.getNextAlias(entityName);
874
1127
  this.createMainAlias(entityName, aliasName);
875
1128
  }
876
1129
  createQueryBuilderHelper() {
@@ -888,6 +1141,7 @@ class QueryBuilder {
888
1141
  }
889
1142
  }
890
1143
  /* istanbul ignore next */
1144
+ /** @ignore */
891
1145
  [util_1.inspect.custom](depth) {
892
1146
  const object = { ...this };
893
1147
  const hidden = ['metadata', 'driver', 'context', 'platform', 'knex', 'type'];
@@ -898,6 +1152,9 @@ class QueryBuilder {
898
1152
  if (this._data) {
899
1153
  object.data = this._data;
900
1154
  }
1155
+ if (this._schema) {
1156
+ object.schema = this._schema;
1157
+ }
901
1158
  if (!core_1.Utils.isEmpty(this._cond)) {
902
1159
  object.where = this._cond;
903
1160
  }