@mikro-orm/knex 6.0.0-dev.1 → 6.0.0-dev.11

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 (54) hide show
  1. package/{dist/AbstractSqlConnection.d.ts → AbstractSqlConnection.d.ts} +0 -0
  2. package/{dist/AbstractSqlConnection.js → AbstractSqlConnection.js} +5 -0
  3. package/AbstractSqlDriver.d.ts +66 -0
  4. package/{dist/AbstractSqlDriver.js → AbstractSqlDriver.js} +226 -70
  5. package/{dist/AbstractSqlPlatform.d.ts → AbstractSqlPlatform.d.ts} +7 -7
  6. package/{dist/AbstractSqlPlatform.js → AbstractSqlPlatform.js} +12 -14
  7. package/{dist/MonkeyPatchable.d.ts → MonkeyPatchable.d.ts} +0 -0
  8. package/{dist/MonkeyPatchable.js → MonkeyPatchable.js} +0 -0
  9. package/{dist/README.md → README.md} +2 -2
  10. package/{dist/SqlEntityManager.d.ts → SqlEntityManager.d.ts} +5 -5
  11. package/{dist/SqlEntityManager.js → SqlEntityManager.js} +0 -1
  12. package/{dist/SqlEntityRepository.d.ts → SqlEntityRepository.d.ts} +3 -3
  13. package/{dist/SqlEntityRepository.js → SqlEntityRepository.js} +1 -1
  14. package/{dist/index.d.ts → index.d.ts} +1 -0
  15. package/{dist/index.js → index.js} +6 -1
  16. package/index.mjs +192 -0
  17. package/package.json +18 -47
  18. package/{dist/query → query}/ArrayCriteriaNode.d.ts +0 -0
  19. package/{dist/query → query}/ArrayCriteriaNode.js +0 -0
  20. package/{dist/query → query}/CriteriaNode.d.ts +0 -0
  21. package/{dist/query → query}/CriteriaNode.js +7 -4
  22. package/{dist/query → query}/CriteriaNodeFactory.d.ts +0 -0
  23. package/{dist/query → query}/CriteriaNodeFactory.js +0 -0
  24. package/{dist/query → query}/ObjectCriteriaNode.d.ts +0 -0
  25. package/{dist/query → query}/ObjectCriteriaNode.js +14 -1
  26. package/{dist/query → query}/QueryBuilder.d.ts +61 -24
  27. package/{dist/query → query}/QueryBuilder.js +308 -84
  28. package/{dist/query → query}/QueryBuilderHelper.d.ts +19 -11
  29. package/{dist/query → query}/QueryBuilderHelper.js +206 -108
  30. package/{dist/query → query}/ScalarCriteriaNode.d.ts +0 -0
  31. package/{dist/query → query}/ScalarCriteriaNode.js +0 -0
  32. package/{dist/query → query}/enums.d.ts +0 -0
  33. package/{dist/query → query}/enums.js +0 -0
  34. package/{dist/query → query}/index.d.ts +0 -0
  35. package/{dist/query → query}/index.js +5 -1
  36. package/{dist/schema → schema}/DatabaseSchema.d.ts +1 -1
  37. package/{dist/schema → schema}/DatabaseSchema.js +8 -16
  38. package/{dist/schema → schema}/DatabaseTable.d.ts +8 -8
  39. package/{dist/schema → schema}/DatabaseTable.js +2 -1
  40. package/{dist/schema → schema}/SchemaComparator.d.ts +7 -5
  41. package/{dist/schema → schema}/SchemaComparator.js +58 -10
  42. package/{dist/schema → schema}/SchemaHelper.d.ts +15 -10
  43. package/{dist/schema → schema}/SchemaHelper.js +46 -8
  44. package/{dist/schema/SchemaGenerator.d.ts → schema/SqlSchemaGenerator.d.ts} +12 -12
  45. package/{dist/schema/SchemaGenerator.js → schema/SqlSchemaGenerator.js} +85 -34
  46. package/{dist/schema → schema}/index.d.ts +1 -1
  47. package/{dist/schema → schema}/index.js +6 -2
  48. package/{dist/typings.d.ts → typings.d.ts} +16 -12
  49. package/{dist/typings.js → typings.js} +0 -0
  50. package/CHANGELOG.md +0 -661
  51. package/dist/AbstractSqlDriver.d.ts +0 -55
  52. package/dist/LICENSE +0 -21
  53. package/dist/index.mjs +0 -26
  54. package/dist/package.json +0 -99
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.QueryBuilder = void 0;
4
+ const util_1 = require("util");
4
5
  const core_1 = require("@mikro-orm/core");
5
6
  const enums_1 = require("./enums");
6
7
  const QueryBuilderHelper_1 = require("./QueryBuilderHelper");
@@ -25,11 +26,21 @@ const CriteriaNodeFactory_1 = require("./CriteriaNodeFactory");
25
26
  * ```
26
27
  */
27
28
  class QueryBuilder {
29
+ get mainAlias() {
30
+ this.ensureFromClause();
31
+ return this._mainAlias;
32
+ }
33
+ get alias() {
34
+ return this.mainAlias.aliasName;
35
+ }
36
+ get helper() {
37
+ this.ensureFromClause();
38
+ return this._helper;
39
+ }
28
40
  /**
29
41
  * @internal
30
42
  */
31
43
  constructor(entityName, metadata, driver, context, alias, connectionType, em) {
32
- this.entityName = entityName;
33
44
  this.metadata = metadata;
34
45
  this.driver = driver;
35
46
  this.context = context;
@@ -43,23 +54,25 @@ class QueryBuilder {
43
54
  this.flags = new Set([core_1.QueryFlag.CONVERT_CUSTOM_TYPES]);
44
55
  this.finalized = false;
45
56
  this._joins = {};
46
- this._aliasMap = {};
57
+ this._explicitAlias = false;
47
58
  this._cond = {};
48
59
  this._orderBy = [];
49
60
  this._groupBy = [];
50
61
  this._having = {};
51
62
  this._joinedProps = new Map();
52
63
  this.subQueries = {};
64
+ this._aliases = {};
53
65
  this.platform = this.driver.getPlatform();
54
66
  this.knex = this.driver.getConnection(this.connectionType).getKnex();
55
67
  if (alias) {
56
68
  this.aliasCounter++;
69
+ this._explicitAlias = true;
57
70
  }
58
- this.alias = alias ?? this.getNextAlias(this.entityName);
59
- this._aliasMap[this.alias] = this.entityName;
60
- this.helper = new QueryBuilderHelper_1.QueryBuilderHelper(this.entityName, this.alias, this._aliasMap, this.subQueries, this.metadata, this.knex, this.platform);
71
+ // @ts-expect-error union type does not match the overloaded method signature
72
+ this.from(entityName, alias);
61
73
  }
62
74
  select(fields, distinct = false) {
75
+ this.ensureNotFinalized();
63
76
  this._fields = core_1.Utils.asArray(fields);
64
77
  if (distinct) {
65
78
  this.flags.add(core_1.QueryFlag.DISTINCT);
@@ -67,11 +80,22 @@ class QueryBuilder {
67
80
  return this.init(enums_1.QueryType.SELECT);
68
81
  }
69
82
  addSelect(fields) {
83
+ this.ensureNotFinalized();
70
84
  if (this.type && this.type !== enums_1.QueryType.SELECT) {
71
85
  return this;
72
86
  }
73
87
  return this.select([...core_1.Utils.asArray(this._fields), ...core_1.Utils.asArray(fields)]);
74
88
  }
89
+ distinct() {
90
+ this.ensureNotFinalized();
91
+ return this.setFlag(core_1.QueryFlag.DISTINCT);
92
+ }
93
+ /** postgres only */
94
+ distinctOn(fields) {
95
+ this.ensureNotFinalized();
96
+ this._distinctOn = core_1.Utils.asArray(fields);
97
+ return this;
98
+ }
75
99
  insert(data) {
76
100
  return this.init(enums_1.QueryType.INSERT, data);
77
101
  }
@@ -85,7 +109,15 @@ class QueryBuilder {
85
109
  return this.init(enums_1.QueryType.TRUNCATE);
86
110
  }
87
111
  count(field, distinct = false) {
88
- this._fields = [...(field ? core_1.Utils.asArray(field) : this.metadata.find(this.entityName).primaryKeys)];
112
+ if (field) {
113
+ this._fields = core_1.Utils.asArray(field);
114
+ }
115
+ else if (this.hasToManyJoins()) {
116
+ this._fields = this.mainAlias.metadata.primaryKeys;
117
+ }
118
+ else {
119
+ this._fields = [this.raw('*')];
120
+ }
89
121
  if (distinct) {
90
122
  this.flags.add(core_1.QueryFlag.DISTINCT);
91
123
  }
@@ -99,6 +131,9 @@ class QueryBuilder {
99
131
  return this.join(field, alias, cond, 'leftJoin');
100
132
  }
101
133
  joinAndSelect(field, alias, cond = {}, type = 'innerJoin', path) {
134
+ if (!this.type) {
135
+ this.select('*');
136
+ }
102
137
  const prop = this.joinReference(field, alias, cond, type, path);
103
138
  this.addSelect(this.getFieldsForJoinedLoad(prop, alias));
104
139
  const [fromAlias] = this.helper.splitField(field);
@@ -124,24 +159,34 @@ class QueryBuilder {
124
159
  return fields;
125
160
  }
126
161
  withSubQuery(subQuery, alias) {
162
+ this.ensureNotFinalized();
127
163
  this.subQueries[alias] = subQuery.toString();
128
164
  return this;
129
165
  }
130
166
  where(cond, params, operator) {
167
+ this.ensureNotFinalized();
131
168
  if (core_1.Utils.isString(cond)) {
132
169
  cond = { [`(${cond})`]: core_1.Utils.asArray(params) };
133
170
  operator = operator || '$and';
134
171
  }
135
172
  else {
136
- cond = core_1.QueryHelper.processWhere(cond, this.entityName, this.metadata, this.platform, this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES));
173
+ cond = core_1.QueryHelper.processWhere({
174
+ where: cond,
175
+ entityName: this.mainAlias.entityName,
176
+ metadata: this.metadata,
177
+ platform: this.platform,
178
+ aliasMap: this.getAliasMap(),
179
+ aliased: !this.type || [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(this.type),
180
+ convertCustomTypes: this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES),
181
+ });
137
182
  }
138
183
  const op = operator || params;
139
184
  const topLevel = !op || !core_1.Utils.hasObjectKeys(this._cond);
140
- const criteriaNode = CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.entityName, cond);
185
+ const criteriaNode = CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, cond);
141
186
  if ([enums_1.QueryType.UPDATE, enums_1.QueryType.DELETE].includes(this.type) && criteriaNode.willAutoJoin(this)) {
142
187
  // use sub-query to support joining
143
188
  this.setFlag(this.type === enums_1.QueryType.UPDATE ? core_1.QueryFlag.UPDATE_SUB_QUERY : core_1.QueryFlag.DELETE_SUB_QUERY);
144
- this.select(this.metadata.find(this.entityName).primaryKeys, true);
189
+ this.select(this.mainAlias.metadata.primaryKeys, true);
145
190
  }
146
191
  if (topLevel) {
147
192
  this._cond = criteriaNode.process(this);
@@ -166,27 +211,39 @@ class QueryBuilder {
166
211
  return this.where(cond, params, '$or');
167
212
  }
168
213
  orderBy(orderBy) {
214
+ this.ensureNotFinalized();
169
215
  this._orderBy = [];
170
216
  core_1.Utils.asArray(orderBy).forEach(o => {
171
- const processed = core_1.QueryHelper.processWhere(o, this.entityName, this.metadata, this.platform, false);
172
- this._orderBy.push(CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.entityName, processed).process(this));
217
+ const processed = core_1.QueryHelper.processWhere({
218
+ where: o,
219
+ entityName: this.mainAlias.entityName,
220
+ metadata: this.metadata,
221
+ platform: this.platform,
222
+ aliasMap: this.getAliasMap(),
223
+ aliased: !this.type || [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(this.type),
224
+ convertCustomTypes: false,
225
+ });
226
+ this._orderBy.push(CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, processed).process(this));
173
227
  });
174
228
  return this;
175
229
  }
176
230
  groupBy(fields) {
231
+ this.ensureNotFinalized();
177
232
  this._groupBy = core_1.Utils.asArray(fields);
178
233
  return this;
179
234
  }
180
235
  having(cond = {}, params) {
236
+ this.ensureNotFinalized();
181
237
  if (core_1.Utils.isString(cond)) {
182
238
  cond = { [`(${cond})`]: core_1.Utils.asArray(params) };
183
239
  }
184
- this._having = CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.entityName, cond).process(this);
240
+ this._having = CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, cond).process(this);
185
241
  return this;
186
242
  }
187
243
  onConflict(fields = []) {
244
+ this.ensureNotFinalized();
188
245
  this._onConflict = this._onConflict || [];
189
- this._onConflict.push({ fields: core_1.Utils.asArray(fields) });
246
+ this._onConflict.push({ fields: core_1.Utils.asArray(fields).map(f => f.toString()) });
190
247
  return this;
191
248
  }
192
249
  ignore() {
@@ -200,14 +257,19 @@ class QueryBuilder {
200
257
  if (!this._onConflict) {
201
258
  throw new Error('You need to call `qb.onConflict()` first to use `qb.merge()`');
202
259
  }
260
+ if (Array.isArray(data) && data.length === 0) {
261
+ return this.ignore();
262
+ }
203
263
  this._onConflict[this._onConflict.length - 1].merge = data;
204
264
  return this;
205
265
  }
206
266
  /**
207
267
  * @internal
208
268
  */
209
- populate(populate) {
269
+ populate(populate, populateWhere) {
270
+ this.ensureNotFinalized();
210
271
  this._populate = populate;
272
+ this._populateWhere = populateWhere;
211
273
  return this;
212
274
  }
213
275
  /**
@@ -222,6 +284,7 @@ class QueryBuilder {
222
284
  return raw;
223
285
  }
224
286
  limit(limit, offset = 0) {
287
+ this.ensureNotFinalized();
225
288
  this._limit = limit;
226
289
  if (offset) {
227
290
  this.offset(offset);
@@ -229,14 +292,17 @@ class QueryBuilder {
229
292
  return this;
230
293
  }
231
294
  offset(offset) {
295
+ this.ensureNotFinalized();
232
296
  this._offset = offset;
233
297
  return this;
234
298
  }
235
299
  withSchema(schema) {
300
+ this.ensureNotFinalized();
236
301
  this._schema = schema;
237
302
  return this;
238
303
  }
239
304
  setLockMode(mode, tables) {
305
+ this.ensureNotFinalized();
240
306
  if (mode != null && mode !== core_1.LockMode.OPTIMISTIC && !this.context) {
241
307
  throw core_1.ValidationError.transactionRequired();
242
308
  }
@@ -245,18 +311,22 @@ class QueryBuilder {
245
311
  return this;
246
312
  }
247
313
  setFlushMode(flushMode) {
314
+ this.ensureNotFinalized();
248
315
  this.flushMode = flushMode;
249
316
  return this;
250
317
  }
251
318
  setFlag(flag) {
319
+ this.ensureNotFinalized();
252
320
  this.flags.add(flag);
253
321
  return this;
254
322
  }
255
323
  unsetFlag(flag) {
324
+ this.ensureNotFinalized();
256
325
  this.flags.delete(flag);
257
326
  return this;
258
327
  }
259
328
  cache(config = true) {
329
+ this.ensureNotFinalized();
260
330
  this._cache = config;
261
331
  return this;
262
332
  }
@@ -264,31 +334,46 @@ class QueryBuilder {
264
334
  * Adds index hint to the FROM clause.
265
335
  */
266
336
  indexHint(sql) {
337
+ this.ensureNotFinalized();
267
338
  this._indexHint = sql;
268
339
  return this;
269
340
  }
341
+ from(target, aliasName) {
342
+ this.ensureNotFinalized();
343
+ if (target instanceof QueryBuilder) {
344
+ this.fromSubQuery(target, aliasName);
345
+ }
346
+ else {
347
+ const entityName = core_1.Utils.className(target);
348
+ if (aliasName && this._mainAlias && entityName !== this._mainAlias.aliasName) {
349
+ throw new Error(`Cannot override the alias to '${aliasName}' since a query already contains references to '${this._mainAlias.aliasName}'`);
350
+ }
351
+ this.fromEntityName(entityName, aliasName);
352
+ }
353
+ return this;
354
+ }
270
355
  getKnexQuery() {
271
356
  this.finalize();
272
357
  const qb = this.getQueryBase();
273
- core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type, this._cond, qb), this._cond && !this._onConflict);
358
+ core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type ?? enums_1.QueryType.SELECT, this._cond, qb), this._cond && !this._onConflict);
274
359
  core_1.Utils.runIfNotEmpty(() => qb.groupBy(this.prepareFields(this._groupBy, 'groupBy')), this._groupBy);
275
- core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type, this._having, qb, undefined, 'having'), this._having);
360
+ core_1.Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type ?? enums_1.QueryType.SELECT, this._having, qb, undefined, 'having'), this._having);
276
361
  core_1.Utils.runIfNotEmpty(() => {
277
- const queryOrder = this.helper.getQueryOrder(this.type, this._orderBy, this._populateMap);
362
+ const queryOrder = this.helper.getQueryOrder(this.type ?? enums_1.QueryType.SELECT, this._orderBy, this._populateMap);
278
363
  if (queryOrder) {
279
364
  return qb.orderByRaw(queryOrder);
280
365
  }
281
366
  }, this._orderBy);
282
367
  core_1.Utils.runIfNotEmpty(() => qb.limit(this._limit), this._limit != null);
283
368
  core_1.Utils.runIfNotEmpty(() => qb.offset(this._offset), this._offset);
284
- core_1.Utils.runIfNotEmpty(() => this.helper.appendOnConflictClause(this.type, this._onConflict, qb), this._onConflict);
369
+ core_1.Utils.runIfNotEmpty(() => this.helper.appendOnConflictClause(this.type ?? enums_1.QueryType.SELECT, this._onConflict, qb), this._onConflict);
285
370
  if (this.type === enums_1.QueryType.TRUNCATE && this.platform.usesCascadeStatement()) {
286
371
  return this.knex.raw(qb.toSQL().toNative().sql + ' cascade');
287
372
  }
288
373
  if (this.lockMode) {
289
374
  this.helper.getLockSQL(qb, this.lockMode, this.lockTables);
290
375
  }
291
- this.helper.finalize(this.type, qb, this.metadata.find(this.entityName));
376
+ this.helper.finalize(this.type ?? enums_1.QueryType.SELECT, qb, this.mainAlias.metadata);
292
377
  return qb;
293
378
  }
294
379
  /**
@@ -310,9 +395,12 @@ class QueryBuilder {
310
395
  const query = this.getKnexQuery().toSQL();
311
396
  return this.platform.formatQuery(query.sql, query.bindings);
312
397
  }
398
+ /**
399
+ * @internal
400
+ */
313
401
  getAliasForJoinPath(path) {
314
- if (!path || path === this.entityName) {
315
- return this.alias;
402
+ if (!path || path === this.mainAlias.entityName) {
403
+ return this.mainAlias.aliasName;
316
404
  }
317
405
  const join = Object.values(this._joins).find(j => j.path === path);
318
406
  if (path.endsWith('[pivot]') && join) {
@@ -320,25 +408,34 @@ class QueryBuilder {
320
408
  }
321
409
  return join?.inverseAlias || join?.alias;
322
410
  }
411
+ /**
412
+ * @internal
413
+ */
323
414
  getNextAlias(entityName = 'e') {
324
415
  return this.driver.config.getNamingStrategy().aliasName(entityName, this.aliasCounter++);
325
416
  }
417
+ /**
418
+ * @internal
419
+ */
420
+ getAliasMap() {
421
+ return Object.fromEntries(Object.entries(this._aliases).map(([key, value]) => [key, value.entityName]));
422
+ }
326
423
  /**
327
424
  * Executes this QB and returns the raw results, mapped to the property names (unless disabled via last parameter).
328
425
  * Use `method` to specify what kind of result you want to get (array/single/meta).
329
426
  */
330
427
  async execute(method = 'all', mapResults = true) {
331
- if (!this.connectionType && method !== 'run' && [enums_1.QueryType.INSERT, enums_1.QueryType.UPDATE, enums_1.QueryType.DELETE, enums_1.QueryType.TRUNCATE].includes(this.type)) {
428
+ 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)) {
332
429
  this.connectionType = 'write';
333
430
  }
334
431
  const query = this.getKnexQuery().toSQL();
335
- const cached = await this.em?.tryCache(this.entityName, this._cache, ['qb.execute', query.sql, query.bindings, method]);
432
+ const cached = await this.em?.tryCache(this.mainAlias.entityName, this._cache, ['qb.execute', query.sql, query.bindings, method]);
336
433
  if (cached?.data) {
337
434
  return cached.data;
338
435
  }
339
436
  const type = this.connectionType || (method === 'run' ? 'write' : 'read');
340
437
  const res = await this.driver.getConnection(type).execute(query.sql, query.bindings, method, this.context);
341
- const meta = this.metadata.find(this.entityName);
438
+ const meta = this.mainAlias.metadata;
342
439
  if (!mapResults || !meta) {
343
440
  await this.em?.storeCache(this._cache, cached, res);
344
441
  return res;
@@ -363,12 +460,12 @@ class QueryBuilder {
363
460
  * Executes the query, returning array of results
364
461
  */
365
462
  async getResultList() {
366
- await this.em.tryFlush(this.entityName, { flushMode: this.flushMode });
463
+ await this.em.tryFlush(this.mainAlias.entityName, { flushMode: this.flushMode });
367
464
  let res = await this.execute('all', true);
368
465
  if (this._joinedProps.size > 0) {
369
- res = this.driver.mergeJoinedResult(res, this.metadata.find(this.entityName));
466
+ res = this.driver.mergeJoinedResult(res, this.mainAlias.metadata);
370
467
  }
371
- return res.map(r => this.em.map(this.entityName, r, { schema: this._schema }));
468
+ return res.map(r => this.em.map(this.mainAlias.entityName, r, { schema: this._schema }));
372
469
  }
373
470
  /**
374
471
  * Executes the query, returning the first result or null
@@ -380,35 +477,32 @@ class QueryBuilder {
380
477
  /**
381
478
  * Executes count query (without offset and limit), returning total count of results
382
479
  */
383
- async getCount(field, distinct = false) {
384
- const qb = this.clone();
385
- qb.count(field, distinct).limit(undefined).offset(undefined);
386
- const res = await qb.execute('get', false);
480
+ async getCount(field, distinct) {
481
+ let res;
482
+ if (this.type === enums_1.QueryType.COUNT) {
483
+ res = await this.execute('get', false);
484
+ }
485
+ else {
486
+ const qb = this.clone();
487
+ qb.count(field, distinct ?? qb.hasToManyJoins()).limit(undefined).offset(undefined).orderBy([]);
488
+ res = await qb.execute('get', false);
489
+ }
387
490
  return res ? +res.count : 0;
388
491
  }
389
492
  /**
390
493
  * Provides promise-like interface so we can await the QB instance.
391
494
  */
392
495
  then(onfulfilled, onrejected) {
393
- return this.getInnerPromise().then(onfulfilled, onrejected);
394
- }
395
- getInnerPromise() {
396
- if (!this.innerPromise) {
397
- this.innerPromise = (async () => {
398
- switch (this.type) {
399
- case enums_1.QueryType.INSERT:
400
- case enums_1.QueryType.UPDATE:
401
- case enums_1.QueryType.DELETE:
402
- case enums_1.QueryType.TRUNCATE:
403
- return this.execute('run');
404
- case enums_1.QueryType.SELECT:
405
- return this.getResultList();
406
- case enums_1.QueryType.COUNT:
407
- return this.getCount();
408
- }
409
- })();
496
+ switch (this.type ?? enums_1.QueryType.SELECT) {
497
+ case enums_1.QueryType.INSERT:
498
+ case enums_1.QueryType.UPDATE:
499
+ case enums_1.QueryType.DELETE:
500
+ case enums_1.QueryType.TRUNCATE:
501
+ return this.execute('run').then(onfulfilled, onrejected);
502
+ case enums_1.QueryType.COUNT:
503
+ return this.getCount().then(onfulfilled, onrejected);
504
+ case enums_1.QueryType.SELECT: return this.getResultList().then(onfulfilled, onrejected);
410
505
  }
411
- return this.innerPromise;
412
506
  }
413
507
  /**
414
508
  * Returns knex instance with sub-query aliased with given alias.
@@ -425,11 +519,11 @@ class QueryBuilder {
425
519
  return qb.as(alias);
426
520
  }
427
521
  clone() {
428
- const qb = new QueryBuilder(this.entityName, this.metadata, this.driver, this.context, this.alias, this.connectionType, this.em);
522
+ const qb = new QueryBuilder(this.mainAlias.entityName, this.metadata, this.driver, this.context, this.mainAlias.aliasName, this.connectionType, this.em);
429
523
  Object.assign(qb, this);
430
524
  // clone array/object properties
431
525
  const properties = [
432
- 'flags', '_populate', '_populateMap', '_joins', '_joinedProps', '_aliasMap', '_cond', '_data', '_orderBy',
526
+ 'flags', '_populate', '_populateWhere', '_populateMap', '_joins', '_joinedProps', '_cond', '_data', '_orderBy',
433
527
  '_schema', '_indexHint', '_cache', 'subQueries', 'lockMode', 'lockTables',
434
528
  ];
435
529
  properties.forEach(prop => qb[prop] = core_1.Utils.copy(this[prop]));
@@ -437,27 +531,45 @@ class QueryBuilder {
437
531
  if (this._fields) {
438
532
  qb._fields = [...this._fields];
439
533
  }
534
+ qb._aliases = { ...this._aliases };
440
535
  qb.finalized = false;
441
536
  return qb;
442
537
  }
443
538
  getKnex() {
444
- const tableName = this.helper.getTableName(this.entityName) + (this.finalized && this.helper.isTableNameAliasRequired(this.type) ? ` as ${this.alias}` : '');
445
- const qb = this.knex(tableName);
539
+ const qb = this.knex.queryBuilder();
540
+ const { subQuery, aliasName, entityName } = this.mainAlias;
541
+ const ref = subQuery ? subQuery : this.knex.ref(this.helper.getTableName(entityName));
542
+ if (this.finalized && (this._explicitAlias || this.helper.isTableNameAliasRequired(this.type))) {
543
+ ref.as(aliasName);
544
+ }
545
+ const schema = this.getSchema(this.mainAlias);
546
+ if (schema) {
547
+ ref.withSchema(schema);
548
+ }
549
+ qb.from(ref);
446
550
  if (this.context) {
447
551
  qb.transacting(this.context);
448
552
  }
449
553
  return qb;
450
554
  }
451
555
  joinReference(field, alias, cond, type, path) {
556
+ this.ensureNotFinalized();
452
557
  const [fromAlias, fromField] = this.helper.splitField(field);
453
- const entityName = this._aliasMap[fromAlias];
558
+ const entityName = this._aliases[fromAlias]?.entityName;
454
559
  const meta = this.metadata.get(entityName);
455
560
  const prop = meta.properties[fromField];
456
561
  if (!prop) {
457
562
  throw new Error(`Trying to join ${field}, but ${fromField} is not a defined relation on ${meta.className}`);
458
563
  }
459
- this._aliasMap[alias] = prop.type;
460
- cond = core_1.QueryHelper.processWhere(cond, this.entityName, this.metadata, this.platform);
564
+ this.createAlias(prop.type, alias);
565
+ cond = core_1.QueryHelper.processWhere({
566
+ where: cond,
567
+ entityName: this.mainAlias.entityName,
568
+ metadata: this.metadata,
569
+ platform: this.platform,
570
+ aliasMap: this.getAliasMap(),
571
+ aliased: !this.type || [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(this.type),
572
+ });
461
573
  let aliasedName = `${fromAlias}.${prop.name}#${alias}`;
462
574
  path ?? (path = `${(Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? entityName)}.${prop.name}`);
463
575
  if (prop.reference === core_1.ReferenceType.ONE_TO_MANY) {
@@ -467,12 +579,12 @@ class QueryBuilder {
467
579
  let pivotAlias = alias;
468
580
  if (type !== 'pivotJoin') {
469
581
  const oldPivotAlias = this.getAliasForJoinPath(path + '[pivot]');
470
- pivotAlias = oldPivotAlias ?? this.getNextAlias(prop.pivotTable);
582
+ pivotAlias = oldPivotAlias ?? this.getNextAlias(prop.pivotEntity);
471
583
  aliasedName = `${fromAlias}.${prop.name}#${pivotAlias}`;
472
584
  }
473
585
  const joins = this.helper.joinManyToManyReference(prop, fromAlias, alias, pivotAlias, type, cond, path);
474
586
  Object.assign(this._joins, joins);
475
- this._aliasMap[pivotAlias] = prop.pivotTable;
587
+ this.createAlias(prop.pivotEntity, pivotAlias);
476
588
  }
477
589
  else if (prop.reference === core_1.ReferenceType.ONE_TO_ONE) {
478
590
  this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond);
@@ -493,24 +605,50 @@ class QueryBuilder {
493
605
  }
494
606
  const join = Object.keys(this._joins).find(k => field === k.substring(0, k.indexOf('#')));
495
607
  if (join && type === 'where') {
496
- return ret.push(...this.helper.mapJoinColumns(this.type, this._joins[join]));
608
+ return ret.push(...this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[join]));
497
609
  }
498
610
  const [a, f] = this.helper.splitField(field);
499
611
  const prop = this.helper.getProperty(f, a);
612
+ /* istanbul ignore next */
500
613
  if (prop && [core_1.ReferenceType.ONE_TO_MANY, core_1.ReferenceType.MANY_TO_MANY].includes(prop.reference)) {
501
614
  return;
502
615
  }
616
+ if (prop?.embedded) {
617
+ const fieldName = this.helper.mapper(prop.fieldNames[0], this.type);
618
+ ret.push(fieldName);
619
+ return;
620
+ }
621
+ if (prop?.reference === core_1.ReferenceType.EMBEDDED) {
622
+ if (prop.object) {
623
+ ret.push(this.helper.mapper(prop.fieldNames[0], this.type));
624
+ }
625
+ else {
626
+ const nest = (prop) => {
627
+ for (const childProp of Object.values(prop.embeddedProps)) {
628
+ if (childProp.fieldNames) {
629
+ ret.push(this.helper.mapper(childProp.fieldNames[0], this.type));
630
+ }
631
+ else {
632
+ nest(childProp);
633
+ }
634
+ }
635
+ };
636
+ nest(prop);
637
+ }
638
+ return;
639
+ }
503
640
  ret.push(this.helper.mapper(field, this.type));
504
641
  });
505
- const meta = this.metadata.find(this.entityName);
642
+ const meta = this.mainAlias.metadata;
506
643
  /* istanbul ignore next */
507
644
  const requiresSQLConversion = meta?.props.filter(p => p.customType?.convertToJSValueSQL) ?? [];
508
- if (this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES) && (fields.includes('*') || fields.includes(`${this.alias}.*`)) && requiresSQLConversion.length > 0) {
645
+ if (this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES) && (fields.includes('*') || fields.includes(`${this.mainAlias.aliasName}.*`)) && requiresSQLConversion.length > 0) {
509
646
  requiresSQLConversion.forEach(p => ret.push(this.helper.mapper(p.name, this.type)));
510
647
  }
511
648
  Object.keys(this._populateMap).forEach(f => {
512
649
  if (!fields.includes(f.replace(/#\w+$/, '')) && type === 'where') {
513
- ret.push(...this.helper.mapJoinColumns(this.type, this._joins[f]));
650
+ const cols = this.helper.mapJoinColumns(this.type ?? enums_1.QueryType.SELECT, this._joins[f]);
651
+ ret.push(...cols);
514
652
  }
515
653
  if (this._joins[f].prop.reference !== core_1.ReferenceType.ONE_TO_ONE && this._joins[f].inverseJoinColumns) {
516
654
  this._joins[f].inverseJoinColumns.forEach(inverseJoinColumn => {
@@ -521,8 +659,8 @@ class QueryBuilder {
521
659
  return ret;
522
660
  }
523
661
  init(type, data, cond) {
662
+ this.ensureNotFinalized();
524
663
  this.type = type;
525
- this._aliasMap[this.alias] = this.entityName;
526
664
  if ([enums_1.QueryType.UPDATE, enums_1.QueryType.DELETE].includes(type) && core_1.Utils.hasObjectKeys(this._cond)) {
527
665
  throw new Error(`You are trying to call \`qb.where().${type.toLowerCase()}()\`. Calling \`qb.${type.toLowerCase()}()\` before \`qb.where()\` is required.`);
528
666
  }
@@ -530,6 +668,9 @@ class QueryBuilder {
530
668
  delete this._fields;
531
669
  }
532
670
  if (data) {
671
+ if (core_1.Utils.isEntity(data)) {
672
+ data = (0, core_1.helper)(data).toJSON();
673
+ }
533
674
  this._data = this.helper.processData(data, this.flags.has(core_1.QueryFlag.CONVERT_CUSTOM_TYPES));
534
675
  }
535
676
  if (cond) {
@@ -539,22 +680,23 @@ class QueryBuilder {
539
680
  }
540
681
  getQueryBase() {
541
682
  const qb = this.getKnex();
542
- const meta = this.metadata.find(this.entityName);
543
- const metaSchema = meta?.schema && meta.schema !== '*' ? meta.schema : undefined;
544
- const schema = this._schema ?? metaSchema ?? this.em?.config.get('schema');
683
+ const schema = this.getSchema(this.mainAlias);
545
684
  if (schema) {
546
685
  qb.withSchema(schema);
547
686
  }
548
687
  if (this._indexHint) {
549
- const alias = this.helper.isTableNameAliasRequired(this.type) ? ` as ${this.platform.quoteIdentifier(this.alias)}` : '';
688
+ const alias = this.helper.isTableNameAliasRequired(this.type) ? ` as ${this.platform.quoteIdentifier(this.mainAlias.aliasName)}` : '';
550
689
  const schemaQuoted = schema ? this.platform.quoteIdentifier(schema) + '.' : '';
551
- const tableName = schemaQuoted + this.platform.quoteIdentifier(this.helper.getTableName(this.entityName)) + alias;
690
+ const tableName = schemaQuoted + this.platform.quoteIdentifier(this.helper.getTableName(this.mainAlias.entityName)) + alias;
552
691
  qb.from(this.knex.raw(`${tableName} ${this._indexHint}`));
553
692
  }
554
693
  switch (this.type) {
555
694
  case enums_1.QueryType.SELECT:
556
695
  qb.select(this.prepareFields(this._fields));
557
- if (this.flags.has(core_1.QueryFlag.DISTINCT)) {
696
+ if (this._distinctOn) {
697
+ qb.distinctOn(this._distinctOn);
698
+ }
699
+ else if (this.flags.has(core_1.QueryFlag.DISTINCT)) {
558
700
  qb.distinct();
559
701
  }
560
702
  this.helper.processJoins(qb, this._joins, schema);
@@ -588,7 +730,7 @@ class QueryBuilder {
588
730
  if (!this.type) {
589
731
  this.select('*');
590
732
  }
591
- const meta = this.metadata.find(this.entityName);
733
+ const meta = this.mainAlias.metadata;
592
734
  if (meta && this.flags.has(core_1.QueryFlag.AUTO_JOIN_ONE_TO_ONE_OWNER)) {
593
735
  const relationsToPopulate = this._populate.map(({ field }) => field);
594
736
  meta.relations
@@ -600,7 +742,7 @@ class QueryBuilder {
600
742
  const [fromAlias, fromField] = this.helper.splitField(field);
601
743
  const aliasedField = `${fromAlias}.${fromField}`;
602
744
  const join = Object.keys(this._joins).find(k => `${aliasedField}#${this._joins[k].alias}` === k);
603
- if (this._joins[join] && this.helper.isOneToOneInverse(fromField)) {
745
+ if (join && this._joins[join] && this.helper.isOneToOneInverse(fromField)) {
604
746
  return this._populateMap[join] = this._joins[join].alias;
605
747
  }
606
748
  if (this.metadata.find(field)?.pivotTable) { // pivot table entity
@@ -608,16 +750,17 @@ class QueryBuilder {
608
750
  }
609
751
  else if (meta && this.helper.isOneToOneInverse(fromField)) {
610
752
  const prop = meta.properties[fromField];
611
- const alias = this.getNextAlias(prop.pivotTable ?? prop.type);
612
- this._joins[join] = this.helper.joinOneToReference(prop, this.alias, alias, 'leftJoin');
613
- this._populateMap[join] = this._joins[join].alias;
753
+ const alias = this.getNextAlias(prop.pivotEntity ?? prop.type);
754
+ const aliasedName = `${fromAlias}.${prop.name}#${alias}`;
755
+ this._joins[aliasedName] = this.helper.joinOneToReference(prop, this.mainAlias.aliasName, alias, 'leftJoin');
756
+ this._populateMap[aliasedName] = this._joins[aliasedName].alias;
614
757
  }
615
758
  });
616
- if (meta && (this._fields?.includes('*') || this._fields?.includes(`${this.alias}.*`))) {
759
+ if (meta && (this._fields?.includes('*') || this._fields?.includes(`${this.mainAlias.aliasName}.*`))) {
617
760
  meta.props
618
761
  .filter(prop => prop.formula && (!prop.lazy || this.flags.has(core_1.QueryFlag.INCLUDE_LAZY_FORMULAS)))
619
762
  .map(prop => {
620
- const alias = this.knex.ref(this.alias).toString();
763
+ const alias = this.knex.ref(this.mainAlias.aliasName).toString();
621
764
  const aliased = this.knex.ref(prop.fieldNames[0]).toString();
622
765
  return `${prop.formula(alias)} as ${aliased}`;
623
766
  })
@@ -627,13 +770,22 @@ class QueryBuilder {
627
770
  core_1.QueryHelper.processObjectParams(this._data);
628
771
  core_1.QueryHelper.processObjectParams(this._cond);
629
772
  core_1.QueryHelper.processObjectParams(this._having);
630
- this.finalized = true;
631
- if (meta && this.flags.has(core_1.QueryFlag.PAGINATE) && this._limit > 0) {
773
+ // automatically enable paginate flag when we detect to-many joins
774
+ if (!this.flags.has(core_1.QueryFlag.DISABLE_PAGINATE) && this.hasToManyJoins()) {
775
+ this.flags.add(core_1.QueryFlag.PAGINATE);
776
+ }
777
+ if (meta && this.flags.has(core_1.QueryFlag.PAGINATE) && (this._limit > 0 || this._offset > 0)) {
632
778
  this.wrapPaginateSubQuery(meta);
633
779
  }
634
780
  if (meta && (this.flags.has(core_1.QueryFlag.UPDATE_SUB_QUERY) || this.flags.has(core_1.QueryFlag.DELETE_SUB_QUERY))) {
635
781
  this.wrapModifySubQuery(meta);
636
782
  }
783
+ this.finalized = true;
784
+ }
785
+ hasToManyJoins() {
786
+ return Object.values(this._joins).some(join => {
787
+ return [core_1.ReferenceType.ONE_TO_MANY, core_1.ReferenceType.MANY_TO_MANY].includes(join.prop.reference);
788
+ });
637
789
  }
638
790
  wrapPaginateSubQuery(meta) {
639
791
  const pks = this.prepareFields(meta.primaryKeys, 'sub-query');
@@ -645,28 +797,38 @@ class QueryBuilder {
645
797
  const orderBy = [];
646
798
  for (const orderMap of this._orderBy) {
647
799
  for (const [field, direction] of Object.entries(orderMap)) {
800
+ const [a, f] = this.helper.splitField(field);
801
+ const prop = this.helper.getProperty(f, a);
802
+ const type = this.platform.castColumn(prop);
648
803
  orderBy.push({
649
- [`min(${this.ref(field)})`]: direction,
804
+ [`min(${this.ref(this.helper.mapper(field, this.type, undefined, null))}${type})`]: direction,
650
805
  });
651
806
  }
652
807
  }
653
808
  subQuery.orderBy(orderBy);
654
809
  }
655
810
  subQuery.finalized = true;
656
- const knexQuery = subQuery.as(this.alias).clearSelect().select(pks);
811
+ const knexQuery = subQuery.as(this.mainAlias.aliasName).clearSelect().select(pks);
657
812
  // multiple sub-queries are needed to get around mysql limitations with order by + limit + where in + group by (o.O)
658
813
  // https://stackoverflow.com/questions/17892762/mysql-this-version-of-mysql-doesnt-yet-support-limit-in-all-any-some-subqu
659
814
  const subSubQuery = this.getKnex().select(pks).from(knexQuery);
660
815
  this._limit = undefined;
661
816
  this._offset = undefined;
817
+ const cond = this._cond;
662
818
  this.select(this._fields).where({ [core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: { $in: subSubQuery } });
819
+ if (this._populateWhere === core_1.PopulateHint.INFER) {
820
+ this.andWhere(cond);
821
+ }
822
+ else if (typeof this._populateWhere === 'object') {
823
+ this.andWhere(this._populateWhere);
824
+ }
663
825
  }
664
826
  wrapModifySubQuery(meta) {
665
827
  const subQuery = this.clone();
666
828
  subQuery.finalized = true;
667
829
  // wrap one more time to get around MySQL limitations
668
830
  // https://stackoverflow.com/questions/45494/mysql-error-1093-cant-specify-target-table-for-update-in-from-clause
669
- const subSubQuery = this.getKnex().select(this.prepareFields(meta.primaryKeys)).from(subQuery.as(this.alias));
831
+ const subSubQuery = this.getKnex().select(this.prepareFields(meta.primaryKeys)).from(subQuery.as(this.mainAlias.aliasName));
670
832
  const method = this.flags.has(core_1.QueryFlag.UPDATE_SUB_QUERY) ? 'update' : 'delete';
671
833
  this._cond = {}; // otherwise we would trigger validation error
672
834
  this[method](this._data).where({
@@ -675,14 +837,76 @@ class QueryBuilder {
675
837
  }
676
838
  autoJoinPivotTable(field) {
677
839
  const pivotMeta = this.metadata.find(field);
678
- const owner = pivotMeta.props.find(prop => prop.reference === core_1.ReferenceType.MANY_TO_ONE && prop.owner);
679
- const inverse = pivotMeta.props.find(prop => prop.reference === core_1.ReferenceType.MANY_TO_ONE && !prop.owner);
840
+ const owner = pivotMeta.relations[0];
841
+ const inverse = pivotMeta.relations[1];
680
842
  const prop = this._cond[pivotMeta.name + '.' + owner.name] || this._orderBy[pivotMeta.name + '.' + owner.name] ? inverse : owner;
681
843
  const pivotAlias = this.getNextAlias(pivotMeta.name);
682
- this._joins[field] = this.helper.joinPivotTable(field, prop, this.alias, pivotAlias, 'leftJoin');
844
+ this._joins[field] = this.helper.joinPivotTable(field, prop, this.mainAlias.aliasName, pivotAlias, 'leftJoin');
683
845
  core_1.Utils.renameKey(this._cond, `${field}.${owner.name}`, core_1.Utils.getPrimaryKeyHash(owner.fieldNames.map(fieldName => `${pivotAlias}.${fieldName}`)));
684
846
  core_1.Utils.renameKey(this._cond, `${field}.${inverse.name}`, core_1.Utils.getPrimaryKeyHash(inverse.fieldNames.map(fieldName => `${pivotAlias}.${fieldName}`)));
685
847
  this._populateMap[field] = this._joins[field].alias;
686
848
  }
849
+ getSchema(alias) {
850
+ const { metadata } = alias;
851
+ const metaSchema = metadata?.schema && metadata.schema !== '*' ? metadata.schema : undefined;
852
+ return this._schema ?? metaSchema ?? this.em?.config.get('schema');
853
+ }
854
+ createAlias(entityName, aliasName, subQuery) {
855
+ const metadata = this.metadata.find(entityName);
856
+ const alias = { aliasName, entityName, metadata, subQuery };
857
+ this._aliases[aliasName] = alias;
858
+ return alias;
859
+ }
860
+ createMainAlias(entityName, aliasName, subQuery) {
861
+ this._mainAlias = this.createAlias(entityName, aliasName, subQuery);
862
+ this._helper = this.createQueryBuilderHelper();
863
+ return this._mainAlias;
864
+ }
865
+ fromSubQuery(target, aliasName) {
866
+ const subQuery = target.getKnexQuery();
867
+ const { entityName } = target.mainAlias;
868
+ aliasName ?? (aliasName = this.getNextAlias(entityName));
869
+ this.createMainAlias(entityName, aliasName, subQuery);
870
+ }
871
+ fromEntityName(entityName, aliasName) {
872
+ aliasName ?? (aliasName = this._mainAlias?.aliasName ?? this.getNextAlias(entityName));
873
+ this.createMainAlias(entityName, aliasName);
874
+ }
875
+ createQueryBuilderHelper() {
876
+ return new QueryBuilderHelper_1.QueryBuilderHelper(this.mainAlias.entityName, this.mainAlias.aliasName, this._aliases, this.subQueries, this.knex, this.driver);
877
+ }
878
+ ensureFromClause() {
879
+ /* istanbul ignore next */
880
+ if (!this._mainAlias) {
881
+ throw new Error(`Cannot proceed to build a query because the main alias is not set.`);
882
+ }
883
+ }
884
+ ensureNotFinalized() {
885
+ if (this.finalized) {
886
+ throw new Error('This QueryBuilder instance is already finalized, clone it first if you want to modify it.');
887
+ }
888
+ }
889
+ /* istanbul ignore next */
890
+ [util_1.inspect.custom](depth) {
891
+ const object = { ...this };
892
+ const hidden = ['metadata', 'driver', 'context', 'platform', 'knex', 'type'];
893
+ Object.keys(object).filter(k => k.startsWith('_')).forEach(k => delete object[k]);
894
+ Object.keys(object).filter(k => object[k] == null).forEach(k => delete object[k]);
895
+ hidden.forEach(k => delete object[k]);
896
+ let prefix = this.type ? this.type.substring(0, 1) + this.type.toLowerCase().substring(1) : '';
897
+ if (this._data) {
898
+ object.data = this._data;
899
+ }
900
+ if (!core_1.Utils.isEmpty(this._cond)) {
901
+ object.where = this._cond;
902
+ }
903
+ if (this._onConflict?.[0]) {
904
+ prefix = 'Upsert';
905
+ object.onConflict = this._onConflict[0];
906
+ }
907
+ const name = this._mainAlias ? `${prefix}QueryBuilder<${this._mainAlias?.entityName}>` : 'QueryBuilder';
908
+ const ret = (0, util_1.inspect)(object, { depth });
909
+ return ret === '[Object]' ? `[${name}]` : name + ' ' + ret;
910
+ }
687
911
  }
688
912
  exports.QueryBuilder = QueryBuilder;