@mikro-orm/knex 6.0.0-dev.19 → 6.0.0-dev.190

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 +390 -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 +4 -8
  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 +28 -14
  24. package/query/ObjectCriteriaNode.d.ts +4 -3
  25. package/query/ObjectCriteriaNode.js +51 -18
  26. package/query/QueryBuilder.d.ts +50 -24
  27. package/query/QueryBuilder.js +401 -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 +13 -5
  37. package/schema/DatabaseTable.js +385 -60
  38. package/schema/SchemaComparator.d.ts +5 -5
  39. package/schema/SchemaComparator.js +59 -35
  40. package/schema/SchemaHelper.d.ts +7 -5
  41. package/schema/SchemaHelper.js +25 -11
  42. package/schema/SqlSchemaGenerator.d.ts +3 -6
  43. package/schema/SqlSchemaGenerator.js +71 -36
  44. package/typings.d.ts +22 -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,45 @@ 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
+ }
140
189
  const populate = this._joinedProps.get(fromAlias);
141
190
  const item = { field: prop.name, strategy: core_1.LoadStrategy.JOINED, children: [] };
142
191
  if (populate) {
@@ -146,15 +195,39 @@ class QueryBuilder {
146
195
  this._populate.push(item);
147
196
  }
148
197
  this._joinedProps.set(alias, item);
198
+ this.addSelect(this.getFieldsForJoinedLoad(prop, alias, fields));
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 = [];
215
+ const populate = [];
216
+ const joinKey = Object.keys(this._joins).find(join => join.endsWith(`#${alias}`));
217
+ if (joinKey) {
218
+ const path = this._joins[joinKey].path.split('.').slice(1);
219
+ let children = this._populate;
220
+ for (let i = 0; i < path.length; i++) {
221
+ const child = children.filter(hint => hint.field === path[i]);
222
+ if (child.length === 0) {
223
+ break;
224
+ }
225
+ children = child.flatMap(c => c.children);
226
+ }
227
+ populate.push(...children);
228
+ }
156
229
  prop.targetMeta.props
157
- .filter(prop => this.platform.shouldHaveColumn(prop, this._populate))
230
+ .filter(prop => explicitFields ? explicitFields.includes(prop.name) || prop.primary : this.platform.shouldHaveColumn(prop, populate))
158
231
  .forEach(prop => fields.push(...this.driver.mapPropToFieldNames(this, prop, alias)));
159
232
  return fields;
160
233
  }
@@ -165,9 +238,15 @@ class QueryBuilder {
165
238
  }
166
239
  where(cond, params, operator) {
167
240
  this.ensureNotFinalized();
168
- if (core_1.Utils.isString(cond)) {
169
- cond = { [`(${cond})`]: core_1.Utils.asArray(params) };
170
- operator = operator || '$and';
241
+ const rawField = core_1.RawQueryFragment.getKnownFragment(cond);
242
+ if (rawField) {
243
+ const sql = this.platform.formatQuery(rawField.sql, rawField.params);
244
+ cond = { [(0, core_1.raw)(`(${sql})`)]: core_1.Utils.asArray(params) };
245
+ operator ??= '$and';
246
+ }
247
+ else if (core_1.Utils.isString(cond)) {
248
+ cond = { [(0, core_1.raw)(`(${cond})`, core_1.Utils.asArray(params))]: [] };
249
+ operator ??= '$and';
171
250
  }
172
251
  else {
173
252
  cond = core_1.QueryHelper.processWhere({
@@ -222,6 +301,7 @@ class QueryBuilder {
222
301
  aliasMap: this.getAliasMap(),
223
302
  aliased: !this.type || [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(this.type),
224
303
  convertCustomTypes: false,
304
+ type: 'orderBy',
225
305
  });
226
306
  this._orderBy.push(CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, processed).process(this));
227
307
  });
@@ -235,15 +315,22 @@ class QueryBuilder {
235
315
  having(cond = {}, params) {
236
316
  this.ensureNotFinalized();
237
317
  if (core_1.Utils.isString(cond)) {
238
- cond = { [`(${cond})`]: core_1.Utils.asArray(params) };
318
+ cond = { [(0, core_1.raw)(`(${cond})`, params)]: [] };
239
319
  }
240
320
  this._having = CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, cond).process(this);
241
321
  return this;
242
322
  }
243
323
  onConflict(fields = []) {
324
+ const meta = this.mainAlias.metadata;
244
325
  this.ensureNotFinalized();
245
- this._onConflict = this._onConflict || [];
246
- this._onConflict.push({ fields: core_1.Utils.asArray(fields).map(f => f.toString()) });
326
+ this._onConflict ??= [];
327
+ this._onConflict.push({
328
+ fields: core_1.Utils.asArray(fields).flatMap(f => {
329
+ const key = f.toString();
330
+ /* istanbul ignore next */
331
+ return meta.properties[key]?.fieldNames ?? [key];
332
+ }),
333
+ });
247
334
  return this;
248
335
  }
249
336
  ignore() {
@@ -263,6 +350,10 @@ class QueryBuilder {
263
350
  this._onConflict[this._onConflict.length - 1].merge = data;
264
351
  return this;
265
352
  }
353
+ returning(fields) {
354
+ this._returning = core_1.Utils.asArray(fields);
355
+ return this;
356
+ }
266
357
  /**
267
358
  * @internal
268
359
  */
@@ -272,17 +363,6 @@ class QueryBuilder {
272
363
  this._populateWhere = populateWhere;
273
364
  return this;
274
365
  }
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
366
  limit(limit, offset = 0) {
287
367
  this.ensureNotFinalized();
288
368
  this._limit = limit;
@@ -325,6 +405,9 @@ class QueryBuilder {
325
405
  this.flags.delete(flag);
326
406
  return this;
327
407
  }
408
+ hasFlag(flag) {
409
+ return this.flags.has(flag);
410
+ }
328
411
  cache(config = true) {
329
412
  this.ensureNotFinalized();
330
413
  this._cache = config;
@@ -338,6 +421,24 @@ class QueryBuilder {
338
421
  this._indexHint = sql;
339
422
  return this;
340
423
  }
424
+ /**
425
+ * Prepend comment to the sql query using the syntax `/* ... *&#8205;/`. Some characters are forbidden such as `/*, *&#8205;/` and `?`.
426
+ */
427
+ comment(comment) {
428
+ this.ensureNotFinalized();
429
+ this._comments.push(...core_1.Utils.asArray(comment));
430
+ return this;
431
+ }
432
+ /**
433
+ * Add hints to the query using comment-like syntax `/*+ ... *&#8205;/`. MySQL and Oracle use this syntax for optimizer hints.
434
+ * Also various DB proxies and routers use this syntax to pass hints to alter their behavior. In other dialects the hints
435
+ * are ignored as simple comments.
436
+ */
437
+ hintComment(comment) {
438
+ this.ensureNotFinalized();
439
+ this._hintComments.push(...core_1.Utils.asArray(comment));
440
+ return this;
441
+ }
341
442
  from(target, aliasName) {
342
443
  this.ensureNotFinalized();
343
444
  if (target instanceof QueryBuilder) {
@@ -355,17 +456,21 @@ class QueryBuilder {
355
456
  getKnexQuery() {
356
457
  this.finalize();
357
458
  const qb = this.getQueryBase();
459
+ qb.__raw = true; // tag it as there is now way to check via `instanceof`
358
460
  core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type ?? enums_1.QueryType.SELECT, this._cond, qb), this._cond && !this._onConflict);
359
461
  core_1.Utils.runIfNotEmpty(() => qb.groupBy(this.prepareFields(this._groupBy, 'groupBy')), this._groupBy);
360
462
  core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type ?? enums_1.QueryType.SELECT, this._having, qb, undefined, 'having'), this._having);
361
463
  core_1.Utils.runIfNotEmpty(() => {
362
464
  const queryOrder = this.helper.getQueryOrder(this.type ?? enums_1.QueryType.SELECT, this._orderBy, this._populateMap);
363
465
  if (queryOrder) {
364
- return qb.orderByRaw(queryOrder);
466
+ qb.orderByRaw(queryOrder);
467
+ return;
365
468
  }
366
469
  }, this._orderBy);
367
470
  core_1.Utils.runIfNotEmpty(() => qb.limit(this._limit), this._limit != null);
368
471
  core_1.Utils.runIfNotEmpty(() => qb.offset(this._offset), this._offset);
472
+ core_1.Utils.runIfNotEmpty(() => this._comments.forEach(comment => qb.comment(comment)), this._comments);
473
+ core_1.Utils.runIfNotEmpty(() => this._hintComments.forEach(comment => qb.hintComment(comment)), this._hintComments);
369
474
  core_1.Utils.runIfNotEmpty(() => this.helper.appendOnConflictClause(this.type ?? enums_1.QueryType.SELECT, this._onConflict, qb), this._onConflict);
370
475
  if (this.type === enums_1.QueryType.TRUNCATE && this.platform.usesCascadeStatement()) {
371
476
  return this.knex.raw(qb.toSQL().toNative().sql + ' cascade');
@@ -373,26 +478,35 @@ class QueryBuilder {
373
478
  if (this.lockMode) {
374
479
  this.helper.getLockSQL(qb, this.lockMode, this.lockTables);
375
480
  }
376
- this.helper.finalize(this.type ?? enums_1.QueryType.SELECT, qb, this.mainAlias.metadata);
481
+ this.helper.finalize(this.type ?? enums_1.QueryType.SELECT, qb, this.mainAlias.metadata, this._data, this._returning);
377
482
  return qb;
378
483
  }
379
484
  /**
380
485
  * Returns the query with parameters as wildcards.
381
486
  */
382
487
  getQuery() {
383
- return this.getKnexQuery().toSQL().toNative().sql;
488
+ return this.toQuery().sql;
489
+ }
490
+ #query;
491
+ toQuery() {
492
+ if (this.#query) {
493
+ return this.#query;
494
+ }
495
+ const sql = this.getKnexQuery().toSQL();
496
+ const query = sql.toNative();
497
+ return this.#query = { sql: query.sql, params: query.bindings ?? [], _sql: sql };
384
498
  }
385
499
  /**
386
500
  * Returns the list of all parameters for this query.
387
501
  */
388
502
  getParams() {
389
- return this.getKnexQuery().toSQL().toNative().bindings;
503
+ return this.toQuery().params;
390
504
  }
391
505
  /**
392
506
  * Returns raw interpolated query string with all the parameters inlined.
393
507
  */
394
508
  getFormattedQuery() {
395
- const query = this.getKnexQuery().toSQL();
509
+ const query = this.toQuery()._sql;
396
510
  return this.platform.formatQuery(query.sql, query.bindings);
397
511
  }
398
512
  /**
@@ -424,7 +538,10 @@ class QueryBuilder {
424
538
  * Executes this QB and returns the raw results, mapped to the property names (unless disabled via last parameter).
425
539
  * Use `method` to specify what kind of result you want to get (array/single/meta).
426
540
  */
427
- async execute(method = 'all', mapResults = true) {
541
+ async execute(method = 'all', options) {
542
+ options = typeof options === 'boolean' ? { mapResults: options } : (options ?? {});
543
+ options.mergeResults ??= true;
544
+ options.mapResults ??= true;
428
545
  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
546
  this.connectionType = 'write';
430
547
  }
@@ -433,21 +550,33 @@ class QueryBuilder {
433
550
  if (cached?.data) {
434
551
  return cached.data;
435
552
  }
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);
553
+ const write = method === 'run' || !this.platform.getConfig().get('preferReadReplicas');
554
+ const type = this.connectionType || (write ? 'write' : 'read');
555
+ const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context, this.logging);
438
556
  const meta = this.mainAlias.metadata;
439
- if (!mapResults || !meta) {
557
+ if (!options.mapResults || !meta) {
440
558
  await this.em?.storeCache(this._cache, cached, res);
441
559
  return res;
442
560
  }
443
- if (method === 'all' && Array.isArray(res)) {
561
+ if (method === 'run') {
562
+ return res;
563
+ }
564
+ const joinedProps = this.driver.joinedProps(meta, this._populate);
565
+ let mapped;
566
+ if (Array.isArray(res)) {
444
567
  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;
568
+ mapped = res.map(r => this.driver.mapResult(r, meta, this._populate, this, map));
569
+ if (options.mergeResults && joinedProps.length > 0) {
570
+ mapped = this.driver.mergeJoinedResult(mapped, this.mainAlias.metadata, joinedProps);
571
+ }
572
+ }
573
+ else {
574
+ mapped = [this.driver.mapResult(res, meta, joinedProps, this)];
448
575
  }
449
- const mapped = this.driver.mapResult(res, meta, this._populate, this);
450
576
  await this.em?.storeCache(this._cache, cached, mapped);
577
+ if (method === 'get') {
578
+ return mapped[0];
579
+ }
451
580
  return mapped;
452
581
  }
453
582
  /**
@@ -461,11 +590,28 @@ class QueryBuilder {
461
590
  */
462
591
  async getResultList() {
463
592
  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);
593
+ const res = await this.execute('all', true);
594
+ const entities = [];
595
+ function propagatePopulateHint(entity, hint) {
596
+ (0, core_1.helper)(entity).__serializationContext.populate ??= hint;
597
+ hint.forEach(pop => {
598
+ const value = entity[pop.field];
599
+ if (core_1.Utils.isEntity(value, true)) {
600
+ (0, core_1.helper)(value).populated();
601
+ propagatePopulateHint(value, pop.children ?? []);
602
+ }
603
+ else if (core_1.Utils.isCollection(value)) {
604
+ value.populated();
605
+ value.getItems(false).forEach(item => propagatePopulateHint(item, pop.children ?? []));
606
+ }
607
+ });
467
608
  }
468
- return res.map(r => this.em.map(this.mainAlias.entityName, r, { schema: this._schema }));
609
+ for (const r of res) {
610
+ const entity = this.em.map(this.mainAlias.entityName, r, { schema: this._schema });
611
+ propagatePopulateHint(entity, this._populate);
612
+ entities.push(entity);
613
+ }
614
+ return entities;
469
615
  }
470
616
  /**
471
617
  * Executes the query, returning the first result or null
@@ -483,7 +629,7 @@ class QueryBuilder {
483
629
  res = await this.execute('get', false);
484
630
  }
485
631
  else {
486
- const qb = this.clone();
632
+ const qb = this.type === undefined ? this : this.clone();
487
633
  qb.count(field, distinct ?? qb.hasToManyJoins()).limit(undefined).offset(undefined).orderBy([]);
488
634
  res = await qb.execute('get', false);
489
635
  }
@@ -502,7 +648,11 @@ class QueryBuilder {
502
648
  * Provides promise-like interface so we can await the QB instance.
503
649
  */
504
650
  then(onfulfilled, onrejected) {
505
- switch (this.type ?? enums_1.QueryType.SELECT) {
651
+ let type = this.type ?? enums_1.QueryType.SELECT;
652
+ if (this.flags.has(core_1.QueryFlag.UPDATE_SUB_QUERY) || this.flags.has(core_1.QueryFlag.DELETE_SUB_QUERY)) {
653
+ type = enums_1.QueryType.UPDATE;
654
+ }
655
+ switch (type) {
506
656
  case enums_1.QueryType.INSERT:
507
657
  case enums_1.QueryType.UPDATE:
508
658
  case enums_1.QueryType.DELETE:
@@ -525,15 +675,22 @@ class QueryBuilder {
525
675
  /* istanbul ignore next */
526
676
  alias = meta?.properties[f]?.fieldNames[0] ?? alias;
527
677
  }
528
- return qb.as(alias);
678
+ const ret = qb.as(alias);
679
+ // tag the instance, so it is possible to detect it easily
680
+ Object.defineProperty(ret, '__as', { enumerable: false, value: alias });
681
+ return ret;
529
682
  }
530
- clone() {
683
+ clone(reset) {
531
684
  const qb = new QueryBuilder(this.mainAlias.entityName, this.metadata, this.driver, this.context, this.mainAlias.aliasName, this.connectionType, this.em);
685
+ if (reset) {
686
+ return qb;
687
+ }
532
688
  Object.assign(qb, this);
533
689
  // clone array/object properties
534
690
  const properties = [
535
691
  'flags', '_populate', '_populateWhere', '_populateMap', '_joins', '_joinedProps', '_cond', '_data', '_orderBy',
536
- '_schema', '_indexHint', '_cache', 'subQueries', 'lockMode', 'lockTables',
692
+ '_schema', '_indexHint', '_cache', 'subQueries', 'lockMode', 'lockTables', '_groupBy', '_having', '_returning',
693
+ '_comments', '_hintComments',
537
694
  ];
538
695
  properties.forEach(prop => qb[prop] = core_1.Utils.copy(this[prop]));
539
696
  /* istanbul ignore else */
@@ -561,14 +718,42 @@ class QueryBuilder {
561
718
  }
562
719
  return qb;
563
720
  }
564
- joinReference(field, alias, cond, type, path) {
721
+ joinReference(field, alias, cond, type, path, schema, subquery) {
565
722
  this.ensureNotFinalized();
723
+ if (typeof field === 'object') {
724
+ const prop = {
725
+ name: '__subquery__',
726
+ kind: core_1.ReferenceKind.MANY_TO_ONE,
727
+ };
728
+ if (field instanceof QueryBuilder) {
729
+ prop.type = field.mainAlias.entityName;
730
+ prop.targetMeta = field.mainAlias.metadata;
731
+ field = field.getKnexQuery();
732
+ }
733
+ this._joins[`${this.alias}.${prop.name}#${alias}`] = {
734
+ prop,
735
+ alias,
736
+ type,
737
+ cond,
738
+ schema,
739
+ subquery: field.toString(),
740
+ ownerAlias: this.alias,
741
+ };
742
+ return prop;
743
+ }
744
+ if (!subquery && type.includes('lateral')) {
745
+ throw new Error(`Lateral join can be used only with a sub-query.`);
746
+ }
566
747
  const [fromAlias, fromField] = this.helper.splitField(field);
567
- const entityName = this._aliases[fromAlias]?.entityName;
748
+ const q = (str) => `'${str}'`;
749
+ if (!this._aliases[fromAlias]) {
750
+ 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(', ')}.`);
751
+ }
752
+ const entityName = this._aliases[fromAlias].entityName;
568
753
  const meta = this.metadata.get(entityName);
569
754
  const prop = meta.properties[fromField];
570
755
  if (!prop) {
571
- throw new Error(`Trying to join ${field}, but ${fromField} is not a defined relation on ${meta.className}`);
756
+ throw new Error(`Trying to join ${q(field)}, but ${q(fromField)} is not a defined relation on ${meta.className}.`);
572
757
  }
573
758
  this.createAlias(prop.type, alias);
574
759
  cond = core_1.QueryHelper.processWhere({
@@ -580,26 +765,26 @@ class QueryBuilder {
580
765
  aliased: !this.type || [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(this.type),
581
766
  });
582
767
  let aliasedName = `${fromAlias}.${prop.name}#${alias}`;
583
- path ?? (path = `${(Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? entityName)}.${prop.name}`);
584
- if (prop.reference === core_1.ReferenceType.ONE_TO_MANY) {
585
- this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
768
+ path ??= `${(Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? entityName)}.${prop.name}`;
769
+ if (prop.kind === core_1.ReferenceKind.ONE_TO_MANY) {
770
+ this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond, schema);
586
771
  }
587
- else if (prop.reference === core_1.ReferenceType.MANY_TO_MANY) {
772
+ else if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY) {
588
773
  let pivotAlias = alias;
589
- if (type !== 'pivotJoin') {
774
+ if (type !== enums_1.JoinType.pivotJoin) {
590
775
  const oldPivotAlias = this.getAliasForJoinPath(path + '[pivot]');
591
776
  pivotAlias = oldPivotAlias ?? this.getNextAlias(prop.pivotEntity);
592
777
  aliasedName = `${fromAlias}.${prop.name}#${pivotAlias}`;
593
778
  }
594
- const joins = this.helper.joinManyToManyReference(prop, fromAlias, alias, pivotAlias, type, cond, path);
779
+ const joins = this.helper.joinManyToManyReference(prop, fromAlias, alias, pivotAlias, type, cond, path, schema);
595
780
  Object.assign(this._joins, joins);
596
781
  this.createAlias(prop.pivotEntity, pivotAlias);
597
782
  }
598
- else if (prop.reference === core_1.ReferenceType.ONE_TO_ONE) {
599
- this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
783
+ else if (prop.kind === core_1.ReferenceKind.ONE_TO_ONE) {
784
+ this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond, schema);
600
785
  }
601
786
  else { // MANY_TO_ONE
602
- this._joins[aliasedName] = this.helper.joinManyToOneReference(prop, fromAlias, alias, type, cond);
787
+ this._joins[aliasedName] = this.helper.joinManyToOneReference(prop, fromAlias, alias, type, cond, schema);
603
788
  }
604
789
  if (!this._joins[aliasedName].path && path) {
605
790
  this._joins[aliasedName].path = path;
@@ -608,34 +793,49 @@ class QueryBuilder {
608
793
  }
609
794
  prepareFields(fields, type = 'where') {
610
795
  const ret = [];
796
+ const getFieldName = (name) => {
797
+ if (type === 'groupBy') {
798
+ return this.helper.mapper(name, this.type, undefined, null);
799
+ }
800
+ return this.helper.mapper(name, this.type);
801
+ };
611
802
  fields.forEach(field => {
803
+ const rawField = core_1.RawQueryFragment.getKnownFragment(field);
804
+ if (rawField) {
805
+ const sql = this.platform.formatQuery(rawField.sql, rawField.params);
806
+ ret.push(this.knex.raw(sql));
807
+ return;
808
+ }
612
809
  if (!core_1.Utils.isString(field)) {
613
- return ret.push(field);
810
+ ret.push(field);
811
+ return;
614
812
  }
615
813
  const join = Object.keys(this._joins).find(k => field === k.substring(0, k.indexOf('#')));
616
814
  if (join && type === 'where') {
617
- return ret.push(...this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[join]));
815
+ ret.push(...this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[join]));
816
+ return;
618
817
  }
619
818
  const [a, f] = this.helper.splitField(field);
620
819
  const prop = this.helper.getProperty(f, a);
621
820
  /* istanbul ignore next */
622
- if (prop && [core_1.ReferenceType.ONE_TO_MANY, core_1.ReferenceType.MANY_TO_MANY].includes(prop.reference)) {
821
+ if (prop && [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
623
822
  return;
624
823
  }
625
824
  if (prop?.embedded) {
626
- const fieldName = this.helper.mapper(prop.fieldNames[0], this.type);
627
- ret.push(fieldName);
825
+ const name = prop.embeddedPath?.join('.') ?? prop.fieldNames[0];
826
+ const aliased = this._aliases[a] ? `${a}.${name}` : name;
827
+ ret.push(getFieldName(aliased));
628
828
  return;
629
829
  }
630
- if (prop?.reference === core_1.ReferenceType.EMBEDDED) {
830
+ if (prop?.kind === core_1.ReferenceKind.EMBEDDED) {
631
831
  if (prop.object) {
632
- ret.push(this.helper.mapper(prop.fieldNames[0], this.type));
832
+ ret.push(getFieldName(prop.fieldNames[0]));
633
833
  }
634
834
  else {
635
835
  const nest = (prop) => {
636
836
  for (const childProp of Object.values(prop.embeddedProps)) {
637
- if (childProp.fieldNames) {
638
- ret.push(this.helper.mapper(childProp.fieldNames[0], this.type));
837
+ if (childProp.fieldNames && (childProp.kind !== core_1.ReferenceKind.EMBEDDED || childProp.object) && childProp.persist !== false) {
838
+ ret.push(getFieldName(childProp.fieldNames[0]));
639
839
  }
640
840
  else {
641
841
  nest(childProp);
@@ -646,11 +846,15 @@ class QueryBuilder {
646
846
  }
647
847
  return;
648
848
  }
649
- ret.push(this.helper.mapper(field, this.type));
849
+ if (prop && prop.fieldNames.length > 1) {
850
+ ret.push(...prop.fieldNames.map(f => getFieldName(f)));
851
+ return;
852
+ }
853
+ ret.push(getFieldName(field));
650
854
  });
651
855
  const meta = this.mainAlias.metadata;
652
856
  /* istanbul ignore next */
653
- const requiresSQLConversion = meta?.props.filter(p => p.customType?.convertToJSValueSQL) ?? [];
857
+ const requiresSQLConversion = meta?.props.filter(p => p.hasConvertToJSValueSQL && p.persist !== false) ?? [];
654
858
  if (this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES) && (fields.includes('*') || fields.includes(`${this.mainAlias.aliasName}.*`)) && requiresSQLConversion.length > 0) {
655
859
  requiresSQLConversion.forEach(p => ret.push(this.helper.mapper(p.name, this.type)));
656
860
  }
@@ -659,7 +863,7 @@ class QueryBuilder {
659
863
  const cols = this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[f]);
660
864
  ret.push(...cols);
661
865
  }
662
- if (this._joins[f].prop.reference !== core_1.ReferenceType.ONE_TO_ONE && this._joins[f].inverseJoinColumns) {
866
+ if (this._joins[f].prop.kind !== core_1.ReferenceKind.ONE_TO_ONE && this._joins[f].inverseJoinColumns) {
663
867
  this._joins[f].inverseJoinColumns.forEach(inverseJoinColumn => {
664
868
  core_1.Utils.renameKey(this._cond, inverseJoinColumn, `${this._joins[f].alias}.${inverseJoinColumn}`);
665
869
  });
@@ -678,10 +882,9 @@ class QueryBuilder {
678
882
  }
679
883
  if (data) {
680
884
  if (core_1.Utils.isEntity(data)) {
681
- // data = helper(data).toJSON();
682
885
  data = this.em?.getComparator().prepareEntity(data) ?? (0, core_1.serialize)(data);
683
886
  }
684
- this._data = this.helper.processData(data, this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES));
887
+ this._data = this.helper.processData(data, this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES), false);
685
888
  }
686
889
  if (cond) {
687
890
  this.where(cond);
@@ -691,6 +894,8 @@ class QueryBuilder {
691
894
  getQueryBase() {
692
895
  const qb = this.getKnex();
693
896
  const schema = this.getSchema(this.mainAlias);
897
+ // Joined tables doesn't need to belong to the same schema as the main table
898
+ const joinSchema = this._schema ?? this.em?.schema ?? schema;
694
899
  if (schema) {
695
900
  qb.withSchema(schema);
696
901
  }
@@ -709,12 +914,12 @@ class QueryBuilder {
709
914
  else if (this.flags.has(core_1.QueryFlag.DISTINCT)) {
710
915
  qb.distinct();
711
916
  }
712
- this.helper.processJoins(qb, this._joins, schema);
917
+ this.helper.processJoins(qb, this._joins, joinSchema);
713
918
  break;
714
919
  case enums_1.QueryType.COUNT: {
715
920
  const m = this.flags.has(core_1.QueryFlag.DISTINCT) ? 'countDistinct' : 'count';
716
921
  qb[m]({ count: this._fields.map(f => this.helper.mapper(f, this.type)) });
717
- this.helper.processJoins(qb, this._joins, schema);
922
+ this.helper.processJoins(qb, this._joins, joinSchema);
718
923
  break;
719
924
  }
720
925
  case enums_1.QueryType.INSERT:
@@ -733,6 +938,24 @@ class QueryBuilder {
733
938
  }
734
939
  return qb;
735
940
  }
941
+ applyDiscriminatorCondition() {
942
+ const meta = this.mainAlias.metadata;
943
+ if (!meta?.discriminatorValue) {
944
+ return;
945
+ }
946
+ const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.find(cls));
947
+ const children = [];
948
+ const lookUpChildren = (ret, type) => {
949
+ const children = types.filter(meta2 => meta2.extends === type);
950
+ children.forEach(m => lookUpChildren(ret, m.className));
951
+ ret.push(...children.filter(c => c.discriminatorValue));
952
+ return children;
953
+ };
954
+ lookUpChildren(children, meta.className);
955
+ this.andWhere({
956
+ [meta.root.discriminatorColumn]: children.length > 0 ? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] } : meta.discriminatorValue,
957
+ });
958
+ }
736
959
  finalize() {
737
960
  if (this.finalized) {
738
961
  return;
@@ -741,10 +964,11 @@ class QueryBuilder {
741
964
  this.select('*');
742
965
  }
743
966
  const meta = this.mainAlias.metadata;
967
+ this.applyDiscriminatorCondition();
744
968
  if (meta && this.flags.has(core_1.QueryFlag.AUTO_JOIN_ONE_TO_ONE_OWNER)) {
745
969
  const relationsToPopulate = this._populate.map(({ field }) => field);
746
970
  meta.relations
747
- .filter(prop => prop.reference === core_1.ReferenceType.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
971
+ .filter(prop => prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
748
972
  .map(prop => ({ field: prop.name }))
749
973
  .forEach(item => this._populate.push(item));
750
974
  }
@@ -753,16 +977,14 @@ class QueryBuilder {
753
977
  const aliasedField = `${fromAlias}.${fromField}`;
754
978
  const join = Object.keys(this._joins).find(k => `${aliasedField}#${this._joins[k].alias}` === k);
755
979
  if (join && this._joins[join] && this.helper.isOneToOneInverse(fromField)) {
756
- return this._populateMap[join] = this._joins[join].alias;
757
- }
758
- if (this.metadata.find(field)?.pivotTable) { // pivot table entity
759
- this.autoJoinPivotTable(field);
980
+ this._populateMap[join] = this._joins[join].alias;
981
+ return;
760
982
  }
761
- else if (meta && this.helper.isOneToOneInverse(fromField)) {
983
+ if (meta && this.helper.isOneToOneInverse(fromField)) {
762
984
  const prop = meta.properties[fromField];
763
985
  const alias = this.getNextAlias(prop.pivotEntity ?? prop.type);
764
986
  const aliasedName = `${fromAlias}.${prop.name}#${alias}`;
765
- this._joins[aliasedName] = this.helper.joinOneToReference(prop, this.mainAlias.aliasName, alias, 'leftJoin');
987
+ this._joins[aliasedName] = this.helper.joinOneToReference(prop, this.mainAlias.aliasName, alias, enums_1.JoinType.leftJoin);
766
988
  this._populateMap[aliasedName] = this._joins[aliasedName].alias;
767
989
  }
768
990
  });
@@ -774,14 +996,20 @@ class QueryBuilder {
774
996
  const aliased = this.knex.ref(prop.fieldNames[0]).toString();
775
997
  return `${prop.formula(alias)} as ${aliased}`;
776
998
  })
777
- .filter(field => !this._fields.includes(field))
778
- .forEach(field => this.addSelect(field));
999
+ .filter(field => !this._fields.some(f => {
1000
+ if (f instanceof core_1.RawQueryFragment) {
1001
+ return f.sql === field && f.params.length === 0;
1002
+ }
1003
+ return f === field;
1004
+ }))
1005
+ .forEach(field => this._fields.push((0, core_1.raw)(field)));
779
1006
  }
1007
+ this.processPopulateWhere();
780
1008
  core_1.QueryHelper.processObjectParams(this._data);
781
1009
  core_1.QueryHelper.processObjectParams(this._cond);
782
1010
  core_1.QueryHelper.processObjectParams(this._having);
783
- // automatically enable paginate flag when we detect to-many joins
784
- if (!this.flags.has(core_1.QueryFlag.DISABLE_PAGINATE) && this.hasToManyJoins()) {
1011
+ // automatically enable paginate flag when we detect to-many joins, but only if there is no `group by` clause
1012
+ if (!this.flags.has(core_1.QueryFlag.DISABLE_PAGINATE) && this._groupBy.length === 0 && this.hasToManyJoins()) {
785
1013
  this.flags.add(core_1.QueryFlag.PAGINATE);
786
1014
  }
787
1015
  if (meta && this.flags.has(core_1.QueryFlag.PAGINATE) && (this._limit > 0 || this._offset > 0)) {
@@ -792,17 +1020,54 @@ class QueryBuilder {
792
1020
  }
793
1021
  this.finalized = true;
794
1022
  }
1023
+ processPopulateWhere() {
1024
+ if (this._populateWhere == null || this._populateWhere === core_1.PopulateHint.ALL) {
1025
+ return;
1026
+ }
1027
+ const joins = Object.values(this._joins);
1028
+ joins.forEach(join => {
1029
+ join.cond_ = join.cond;
1030
+ join.cond = {};
1031
+ });
1032
+ const replaceOnConditions = (cond) => {
1033
+ Object.keys(cond).forEach(k => {
1034
+ if (core_1.Utils.isOperator(k)) {
1035
+ if (Array.isArray(cond[k])) {
1036
+ return cond[k].forEach((c) => replaceOnConditions(c));
1037
+ }
1038
+ return replaceOnConditions(cond[k]);
1039
+ }
1040
+ const [a] = this.helper.splitField(k);
1041
+ const join = joins.find(j => j.alias === a);
1042
+ if (join) {
1043
+ join.cond = { ...join.cond, [k]: cond[k] };
1044
+ }
1045
+ });
1046
+ };
1047
+ if (this._populateWhere === core_1.PopulateHint.INFER) {
1048
+ replaceOnConditions(this._cond);
1049
+ }
1050
+ else if (typeof this._populateWhere === 'object') {
1051
+ const cond = CriteriaNodeFactory_1.CriteriaNodeFactory
1052
+ .createNode(this.metadata, this.mainAlias.entityName, this._populateWhere)
1053
+ .process(this);
1054
+ replaceOnConditions(cond);
1055
+ }
1056
+ }
795
1057
  hasToManyJoins() {
796
1058
  return Object.values(this._joins).some(join => {
797
- return [core_1.ReferenceType.ONE_TO_MANY, core_1.ReferenceType.MANY_TO_MANY].includes(join.prop.reference);
1059
+ return [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(join.prop.kind);
798
1060
  });
799
1061
  }
800
1062
  wrapPaginateSubQuery(meta) {
801
1063
  const pks = this.prepareFields(meta.primaryKeys, 'sub-query');
802
1064
  const subQuery = this.clone().select(pks).groupBy(pks).limit(this._limit);
1065
+ // revert the on conditions added via populateWhere, we want to apply those only once
1066
+ Object.values(subQuery._joins).forEach(join => join.cond = join.cond_ ?? {});
803
1067
  if (this._offset) {
804
1068
  subQuery.offset(this._offset);
805
1069
  }
1070
+ const addToSelect = [];
806
1071
  if (this._orderBy.length > 0) {
807
1072
  const orderBy = [];
808
1073
  for (const orderMap of this._orderBy) {
@@ -810,28 +1075,38 @@ class QueryBuilder {
810
1075
  const [a, f] = this.helper.splitField(field);
811
1076
  const prop = this.helper.getProperty(f, a);
812
1077
  const type = this.platform.castColumn(prop);
813
- orderBy.push({
814
- [`min(${this.ref(this.helper.mapper(field, this.type, undefined, null))}${type})`]: direction,
815
- });
1078
+ const fieldName = this.helper.mapper(field, this.type, undefined, null);
1079
+ if (!prop?.persist && !prop?.formula) {
1080
+ addToSelect.push(fieldName);
1081
+ }
1082
+ orderBy.push({ [(0, core_1.raw)(`min(${this.knex.ref(fieldName)}${type})`)]: direction });
816
1083
  }
817
1084
  }
818
1085
  subQuery.orderBy(orderBy);
819
1086
  }
820
1087
  subQuery.finalized = true;
821
1088
  const knexQuery = subQuery.as(this.mainAlias.aliasName).clearSelect().select(pks);
1089
+ if (addToSelect.length > 0) {
1090
+ addToSelect.forEach(prop => {
1091
+ const field = this._fields.find(field => {
1092
+ if (typeof field === 'object' && field && '__as' in field) {
1093
+ return field.__as === prop;
1094
+ }
1095
+ // not perfect, but should work most of the time, ideally we should check only the alias (`... as alias`)
1096
+ return field.toString().includes(prop);
1097
+ });
1098
+ if (field) {
1099
+ knexQuery.select(field);
1100
+ }
1101
+ });
1102
+ }
822
1103
  // multiple sub-queries are needed to get around mysql limitations with order by + limit + where in + group by (o.O)
823
1104
  // https://stackoverflow.com/questions/17892762/mysql-this-version-of-mysql-doesnt-yet-support-limit-in-all-any-some-subqu
824
1105
  const subSubQuery = this.getKnex().select(pks).from(knexQuery);
1106
+ subSubQuery.__raw = true; // tag it as there is now way to check via `instanceof`
825
1107
  this._limit = undefined;
826
1108
  this._offset = undefined;
827
- const cond = this._cond;
828
1109
  this.select(this._fields).where({ [core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: { $in: subSubQuery } });
829
- if (this._populateWhere === core_1.PopulateHint.INFER) {
830
- this.andWhere(cond);
831
- }
832
- else if (typeof this._populateWhere === 'object') {
833
- this.andWhere(this._populateWhere);
834
- }
835
1110
  }
836
1111
  wrapModifySubQuery(meta) {
837
1112
  const subQuery = this.clone();
@@ -845,21 +1120,10 @@ class QueryBuilder {
845
1120
  [core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: { $in: subSubQuery },
846
1121
  });
847
1122
  }
848
- autoJoinPivotTable(field) {
849
- const pivotMeta = this.metadata.find(field);
850
- const owner = pivotMeta.relations[0];
851
- const inverse = pivotMeta.relations[1];
852
- const prop = this._cond[pivotMeta.name + '.' + owner.name] || this._orderBy[pivotMeta.name + '.' + owner.name] ? inverse : owner;
853
- const pivotAlias = this.getNextAlias(pivotMeta.name);
854
- this._joins[field] = this.helper.joinPivotTable(field, prop, this.mainAlias.aliasName, pivotAlias, 'leftJoin');
855
- core_1.Utils.renameKey(this._cond, `${field}.${owner.name}`, core_1.Utils.getPrimaryKeyHash(owner.fieldNames.map(fieldName => `${pivotAlias}.${fieldName}`)));
856
- core_1.Utils.renameKey(this._cond, `${field}.${inverse.name}`, core_1.Utils.getPrimaryKeyHash(inverse.fieldNames.map(fieldName => `${pivotAlias}.${fieldName}`)));
857
- this._populateMap[field] = this._joins[field].alias;
858
- }
859
1123
  getSchema(alias) {
860
1124
  const { metadata } = alias;
861
1125
  const metaSchema = metadata?.schema && metadata.schema !== '*' ? metadata.schema : undefined;
862
- return this._schema ?? metaSchema ?? this.em?.config.get('schema');
1126
+ return this._schema ?? metaSchema ?? this.em?.schema ?? this.em?.config.get('schema');
863
1127
  }
864
1128
  createAlias(entityName, aliasName, subQuery) {
865
1129
  const metadata = this.metadata.find(entityName);
@@ -875,11 +1139,11 @@ class QueryBuilder {
875
1139
  fromSubQuery(target, aliasName) {
876
1140
  const subQuery = target.getKnexQuery();
877
1141
  const { entityName } = target.mainAlias;
878
- aliasName ?? (aliasName = this.getNextAlias(entityName));
1142
+ aliasName ??= this.getNextAlias(entityName);
879
1143
  this.createMainAlias(entityName, aliasName, subQuery);
880
1144
  }
881
1145
  fromEntityName(entityName, aliasName) {
882
- aliasName ?? (aliasName = this._mainAlias?.aliasName ?? this.getNextAlias(entityName));
1146
+ aliasName ??= this._mainAlias?.aliasName ?? this.getNextAlias(entityName);
883
1147
  this.createMainAlias(entityName, aliasName);
884
1148
  }
885
1149
  createQueryBuilderHelper() {
@@ -897,6 +1161,7 @@ class QueryBuilder {
897
1161
  }
898
1162
  }
899
1163
  /* istanbul ignore next */
1164
+ /** @ignore */
900
1165
  [util_1.inspect.custom](depth) {
901
1166
  const object = { ...this };
902
1167
  const hidden = ['metadata', 'driver', 'context', 'platform', 'knex', 'type'];
@@ -907,6 +1172,9 @@ class QueryBuilder {
907
1172
  if (this._data) {
908
1173
  object.data = this._data;
909
1174
  }
1175
+ if (this._schema) {
1176
+ object.schema = this._schema;
1177
+ }
910
1178
  if (!core_1.Utils.isEmpty(this._cond)) {
911
1179
  object.where = this._cond;
912
1180
  }