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

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