@mikro-orm/knex 7.0.0-dev.0 → 7.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.
- package/AbstractSqlConnection.d.ts +2 -2
- package/AbstractSqlConnection.js +36 -31
- package/AbstractSqlDriver.d.ts +14 -12
- package/AbstractSqlDriver.js +198 -186
- package/AbstractSqlPlatform.d.ts +4 -4
- package/AbstractSqlPlatform.js +17 -21
- package/PivotCollectionPersister.d.ts +1 -1
- package/PivotCollectionPersister.js +5 -8
- package/SqlEntityManager.d.ts +4 -3
- package/SqlEntityManager.js +2 -6
- package/SqlEntityRepository.d.ts +2 -2
- package/SqlEntityRepository.js +2 -6
- package/dialects/index.d.ts +4 -4
- package/dialects/index.js +4 -20
- package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +1 -1
- package/dialects/mssql/MsSqlNativeQueryBuilder.js +29 -29
- package/dialects/mssql/index.d.ts +1 -1
- package/dialects/mssql/index.js +1 -17
- package/dialects/mysql/MySqlExceptionConverter.js +16 -19
- package/dialects/mysql/MySqlNativeQueryBuilder.d.ts +1 -1
- package/dialects/mysql/MySqlNativeQueryBuilder.js +13 -17
- package/dialects/mysql/MySqlPlatform.d.ts +10 -6
- package/dialects/mysql/MySqlPlatform.js +30 -20
- package/dialects/mysql/MySqlSchemaHelper.d.ts +5 -5
- package/dialects/mysql/MySqlSchemaHelper.js +8 -12
- package/dialects/mysql/index.d.ts +4 -4
- package/dialects/mysql/index.js +4 -20
- package/dialects/postgresql/PostgreSqlNativeQueryBuilder.d.ts +1 -1
- package/dialects/postgresql/PostgreSqlNativeQueryBuilder.js +2 -6
- package/dialects/postgresql/index.d.ts +1 -1
- package/dialects/postgresql/index.js +1 -17
- package/dialects/sqlite/BaseSqliteConnection.d.ts +2 -2
- package/dialects/sqlite/BaseSqliteConnection.js +14 -12
- package/dialects/sqlite/BaseSqlitePlatform.d.ts +4 -5
- package/dialects/sqlite/BaseSqlitePlatform.js +11 -19
- package/dialects/sqlite/SqliteExceptionConverter.js +16 -19
- package/dialects/sqlite/SqliteNativeQueryBuilder.d.ts +1 -1
- package/dialects/sqlite/SqliteNativeQueryBuilder.js +2 -6
- package/dialects/sqlite/SqliteSchemaHelper.d.ts +5 -5
- package/dialects/sqlite/SqliteSchemaHelper.js +22 -26
- package/dialects/sqlite/index.d.ts +5 -5
- package/dialects/sqlite/index.js +5 -21
- package/index.d.ts +11 -11
- package/index.js +13 -34
- package/package.json +7 -16
- package/query/ArrayCriteriaNode.d.ts +2 -2
- package/query/ArrayCriteriaNode.js +2 -6
- package/query/CriteriaNode.d.ts +1 -1
- package/query/CriteriaNode.js +25 -33
- package/query/CriteriaNodeFactory.d.ts +1 -1
- package/query/CriteriaNodeFactory.js +17 -21
- package/query/NativeQueryBuilder.d.ts +2 -2
- package/query/NativeQueryBuilder.js +33 -37
- package/query/ObjectCriteriaNode.d.ts +2 -2
- package/query/ObjectCriteriaNode.js +43 -43
- package/query/QueryBuilder.d.ts +17 -15
- package/query/QueryBuilder.js +204 -193
- package/query/QueryBuilderHelper.d.ts +4 -4
- package/query/QueryBuilderHelper.js +86 -96
- package/query/ScalarCriteriaNode.d.ts +2 -2
- package/query/ScalarCriteriaNode.js +12 -16
- package/query/enums.js +4 -7
- package/query/index.d.ts +9 -9
- package/query/index.js +9 -25
- package/schema/DatabaseSchema.d.ts +3 -3
- package/schema/DatabaseSchema.js +7 -11
- package/schema/DatabaseTable.d.ts +3 -3
- package/schema/DatabaseTable.js +44 -33
- package/schema/SchemaComparator.d.ts +4 -4
- package/schema/SchemaComparator.js +15 -19
- package/schema/SchemaHelper.d.ts +5 -5
- package/schema/SchemaHelper.js +22 -26
- package/schema/SqlSchemaGenerator.d.ts +4 -4
- package/schema/SqlSchemaGenerator.js +23 -36
- package/schema/index.d.ts +5 -5
- package/schema/index.js +5 -21
- package/typings.d.ts +7 -4
- package/typings.js +1 -2
- package/index.mjs +0 -232
package/query/QueryBuilder.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const QueryBuilderHelper_1 = require("./QueryBuilderHelper");
|
|
8
|
-
const CriteriaNodeFactory_1 = require("./CriteriaNodeFactory");
|
|
9
|
-
const NativeQueryBuilder_1 = require("./NativeQueryBuilder");
|
|
1
|
+
import { inspect } from 'node:util';
|
|
2
|
+
import { helper, isRaw, LoadStrategy, LockMode, PopulateHint, QueryFlag, QueryHelper, raw, RawQueryFragment, Reference, ReferenceKind, serialize, Utils, ValidationError, } from '@mikro-orm/core';
|
|
3
|
+
import { JoinType, QueryType } from './enums.js';
|
|
4
|
+
import { QueryBuilderHelper } from './QueryBuilderHelper.js';
|
|
5
|
+
import { CriteriaNodeFactory } from './CriteriaNodeFactory.js';
|
|
6
|
+
import { NativeQueryBuilder } from './NativeQueryBuilder.js';
|
|
10
7
|
/**
|
|
11
8
|
* SQL query builder with fluent interface.
|
|
12
9
|
*
|
|
@@ -26,7 +23,7 @@ const NativeQueryBuilder_1 = require("./NativeQueryBuilder");
|
|
|
26
23
|
* const publisher = await qb.getSingleResult();
|
|
27
24
|
* ```
|
|
28
25
|
*/
|
|
29
|
-
class QueryBuilder {
|
|
26
|
+
export class QueryBuilder {
|
|
30
27
|
metadata;
|
|
31
28
|
driver;
|
|
32
29
|
context;
|
|
@@ -45,7 +42,7 @@ class QueryBuilder {
|
|
|
45
42
|
return this._helper;
|
|
46
43
|
}
|
|
47
44
|
get type() {
|
|
48
|
-
return this._type ??
|
|
45
|
+
return this._type ?? QueryType.SELECT;
|
|
49
46
|
}
|
|
50
47
|
/** @internal */
|
|
51
48
|
_populate = [];
|
|
@@ -54,7 +51,7 @@ class QueryBuilder {
|
|
|
54
51
|
/** @internal */
|
|
55
52
|
rawFragments = new Set();
|
|
56
53
|
aliasCounter = 0;
|
|
57
|
-
flags = new Set([
|
|
54
|
+
flags = new Set([QueryFlag.CONVERT_CUSTOM_TYPES]);
|
|
58
55
|
finalized = false;
|
|
59
56
|
populateHintFinalized = false;
|
|
60
57
|
_joins = {};
|
|
@@ -104,75 +101,75 @@ class QueryBuilder {
|
|
|
104
101
|
}
|
|
105
102
|
select(fields, distinct = false) {
|
|
106
103
|
this.ensureNotFinalized();
|
|
107
|
-
this._fields =
|
|
104
|
+
this._fields = Utils.asArray(fields);
|
|
108
105
|
if (distinct) {
|
|
109
|
-
this.flags.add(
|
|
106
|
+
this.flags.add(QueryFlag.DISTINCT);
|
|
110
107
|
}
|
|
111
|
-
return this.init(
|
|
108
|
+
return this.init(QueryType.SELECT);
|
|
112
109
|
}
|
|
113
110
|
addSelect(fields) {
|
|
114
111
|
this.ensureNotFinalized();
|
|
115
|
-
if (this._type && this._type !==
|
|
112
|
+
if (this._type && this._type !== QueryType.SELECT) {
|
|
116
113
|
return this;
|
|
117
114
|
}
|
|
118
|
-
return this.select([...
|
|
115
|
+
return this.select([...Utils.asArray(this._fields), ...Utils.asArray(fields)]);
|
|
119
116
|
}
|
|
120
117
|
distinct() {
|
|
121
118
|
this.ensureNotFinalized();
|
|
122
|
-
return this.setFlag(
|
|
119
|
+
return this.setFlag(QueryFlag.DISTINCT);
|
|
123
120
|
}
|
|
124
121
|
/** postgres only */
|
|
125
122
|
distinctOn(fields) {
|
|
126
123
|
this.ensureNotFinalized();
|
|
127
|
-
this._distinctOn =
|
|
124
|
+
this._distinctOn = Utils.asArray(fields);
|
|
128
125
|
return this;
|
|
129
126
|
}
|
|
130
127
|
insert(data) {
|
|
131
|
-
return this.init(
|
|
128
|
+
return this.init(QueryType.INSERT, data);
|
|
132
129
|
}
|
|
133
130
|
update(data) {
|
|
134
|
-
return this.init(
|
|
131
|
+
return this.init(QueryType.UPDATE, data);
|
|
135
132
|
}
|
|
136
133
|
delete(cond) {
|
|
137
|
-
return this.init(
|
|
134
|
+
return this.init(QueryType.DELETE, undefined, cond);
|
|
138
135
|
}
|
|
139
136
|
truncate() {
|
|
140
|
-
return this.init(
|
|
137
|
+
return this.init(QueryType.TRUNCATE);
|
|
141
138
|
}
|
|
142
139
|
count(field, distinct = false) {
|
|
143
140
|
if (field) {
|
|
144
|
-
this._fields =
|
|
141
|
+
this._fields = Utils.asArray(field);
|
|
145
142
|
}
|
|
146
143
|
else if (distinct || this.hasToManyJoins()) {
|
|
147
144
|
this._fields = this.mainAlias.metadata.primaryKeys;
|
|
148
145
|
}
|
|
149
146
|
else {
|
|
150
|
-
this._fields = [
|
|
147
|
+
this._fields = [raw('*')];
|
|
151
148
|
}
|
|
152
149
|
if (distinct) {
|
|
153
|
-
this.flags.add(
|
|
150
|
+
this.flags.add(QueryFlag.DISTINCT);
|
|
154
151
|
}
|
|
155
|
-
return this.init(
|
|
152
|
+
return this.init(QueryType.COUNT);
|
|
156
153
|
}
|
|
157
|
-
join(field, alias, cond = {}, type =
|
|
154
|
+
join(field, alias, cond = {}, type = JoinType.innerJoin, path, schema) {
|
|
158
155
|
this.joinReference(field, alias, cond, type, path, schema);
|
|
159
156
|
return this;
|
|
160
157
|
}
|
|
161
158
|
innerJoin(field, alias, cond = {}, schema) {
|
|
162
|
-
this.join(field, alias, cond,
|
|
159
|
+
this.join(field, alias, cond, JoinType.innerJoin, undefined, schema);
|
|
163
160
|
return this;
|
|
164
161
|
}
|
|
165
162
|
innerJoinLateral(field, alias, cond, schema) {
|
|
166
|
-
this.join(field, alias, cond,
|
|
163
|
+
this.join(field, alias, cond, JoinType.innerJoinLateral, undefined, schema);
|
|
167
164
|
return this;
|
|
168
165
|
}
|
|
169
166
|
leftJoin(field, alias, cond = {}, schema) {
|
|
170
|
-
return this.join(field, alias, cond,
|
|
167
|
+
return this.join(field, alias, cond, JoinType.leftJoin, undefined, schema);
|
|
171
168
|
}
|
|
172
169
|
leftJoinLateral(field, alias, cond, schema) {
|
|
173
|
-
return this.join(field, alias, cond,
|
|
170
|
+
return this.join(field, alias, cond, JoinType.leftJoinLateral, undefined, schema);
|
|
174
171
|
}
|
|
175
|
-
joinAndSelect(field, alias, cond = {}, type =
|
|
172
|
+
joinAndSelect(field, alias, cond = {}, type = JoinType.innerJoin, path, fields, schema) {
|
|
176
173
|
if (!this._type) {
|
|
177
174
|
this.select('*');
|
|
178
175
|
}
|
|
@@ -188,7 +185,7 @@ class QueryBuilder {
|
|
|
188
185
|
this._joins[`${fromAlias}.${prop.name}#${alias}`].subquery = subquery;
|
|
189
186
|
}
|
|
190
187
|
const populate = this._joinedProps.get(fromAlias);
|
|
191
|
-
const item = { field: prop.name, strategy:
|
|
188
|
+
const item = { field: prop.name, strategy: LoadStrategy.JOINED, children: [] };
|
|
192
189
|
if (populate) {
|
|
193
190
|
populate.children.push(item);
|
|
194
191
|
}
|
|
@@ -200,16 +197,16 @@ class QueryBuilder {
|
|
|
200
197
|
return this;
|
|
201
198
|
}
|
|
202
199
|
leftJoinAndSelect(field, alias, cond = {}, fields, schema) {
|
|
203
|
-
return this.joinAndSelect(field, alias, cond,
|
|
200
|
+
return this.joinAndSelect(field, alias, cond, JoinType.leftJoin, undefined, fields, schema);
|
|
204
201
|
}
|
|
205
202
|
leftJoinLateralAndSelect(field, alias, cond = {}, fields, schema) {
|
|
206
|
-
return this.joinAndSelect(field, alias, cond,
|
|
203
|
+
return this.joinAndSelect(field, alias, cond, JoinType.leftJoinLateral, undefined, fields, schema);
|
|
207
204
|
}
|
|
208
205
|
innerJoinAndSelect(field, alias, cond = {}, fields, schema) {
|
|
209
|
-
return this.joinAndSelect(field, alias, cond,
|
|
206
|
+
return this.joinAndSelect(field, alias, cond, JoinType.innerJoin, undefined, fields, schema);
|
|
210
207
|
}
|
|
211
208
|
innerJoinLateralAndSelect(field, alias, cond = {}, fields, schema) {
|
|
212
|
-
return this.joinAndSelect(field, alias, cond,
|
|
209
|
+
return this.joinAndSelect(field, alias, cond, JoinType.innerJoinLateral, undefined, fields, schema);
|
|
213
210
|
}
|
|
214
211
|
getFieldsForJoinedLoad(prop, alias, explicitFields) {
|
|
215
212
|
const fields = [];
|
|
@@ -243,9 +240,12 @@ class QueryBuilder {
|
|
|
243
240
|
}
|
|
244
241
|
}
|
|
245
242
|
prop.targetMeta.props
|
|
246
|
-
.filter(prop =>
|
|
247
|
-
|
|
248
|
-
|
|
243
|
+
.filter(prop => {
|
|
244
|
+
if (!explicitFields) {
|
|
245
|
+
return this.platform.shouldHaveColumn(prop, populate);
|
|
246
|
+
}
|
|
247
|
+
return prop.primary && !explicitFields.includes(prop.name) && !explicitFields.includes(`${alias}.${prop.name}`);
|
|
248
|
+
})
|
|
249
249
|
.forEach(prop => fields.push(...this.driver.mapPropToFieldNames(this, prop, alias)));
|
|
250
250
|
return fields;
|
|
251
251
|
}
|
|
@@ -253,16 +253,50 @@ class QueryBuilder {
|
|
|
253
253
|
* Apply filters to the QB where condition.
|
|
254
254
|
*/
|
|
255
255
|
async applyFilters(filterOptions = {}) {
|
|
256
|
-
/*
|
|
256
|
+
/* v8 ignore next 3 */
|
|
257
257
|
if (!this.em) {
|
|
258
258
|
throw new Error('Cannot apply filters, this QueryBuilder is not attached to an EntityManager');
|
|
259
259
|
}
|
|
260
260
|
const cond = await this.em.applyFilters(this.mainAlias.entityName, {}, filterOptions, 'read');
|
|
261
261
|
this.andWhere(cond);
|
|
262
262
|
}
|
|
263
|
+
autoJoinedPaths = [];
|
|
264
|
+
/**
|
|
265
|
+
* @internal
|
|
266
|
+
*/
|
|
267
|
+
scheduleFilterCheck(path) {
|
|
268
|
+
this.autoJoinedPaths.push(path);
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* @internal
|
|
272
|
+
*/
|
|
273
|
+
async applyJoinedFilters(em, filterOptions = {}) {
|
|
274
|
+
for (const path of this.autoJoinedPaths) {
|
|
275
|
+
const join = this.getJoinForPath(path);
|
|
276
|
+
if (join.type === JoinType.pivotJoin) {
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
const cond = await em.applyFilters(join.prop.type, join.cond, filterOptions, 'read');
|
|
280
|
+
if (Utils.hasObjectKeys(cond)) {
|
|
281
|
+
// remove nested filters, we only care about scalars here, nesting would require another join branch
|
|
282
|
+
for (const key of Object.keys(cond)) {
|
|
283
|
+
if (Utils.isPlainObject(cond[key]) && Object.keys(cond[key]).every(k => !(Utils.isOperator(k) && !['$some', '$none', '$every'].includes(k)))) {
|
|
284
|
+
delete cond[key];
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (Utils.hasObjectKeys(join.cond)) {
|
|
288
|
+
/* istanbul ignore next */
|
|
289
|
+
join.cond = { $and: [join.cond, cond] };
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
join.cond = { ...cond };
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
263
297
|
withSubQuery(subQuery, alias) {
|
|
264
298
|
this.ensureNotFinalized();
|
|
265
|
-
if (subQuery instanceof
|
|
299
|
+
if (subQuery instanceof RawQueryFragment) {
|
|
266
300
|
this.subQueries[alias] = this.platform.formatQuery(subQuery.sql, subQuery.params);
|
|
267
301
|
}
|
|
268
302
|
else {
|
|
@@ -272,34 +306,34 @@ class QueryBuilder {
|
|
|
272
306
|
}
|
|
273
307
|
where(cond, params, operator) {
|
|
274
308
|
this.ensureNotFinalized();
|
|
275
|
-
const rawField =
|
|
309
|
+
const rawField = RawQueryFragment.getKnownFragment(cond);
|
|
276
310
|
if (rawField) {
|
|
277
311
|
const sql = this.platform.formatQuery(rawField.sql, rawField.params);
|
|
278
|
-
cond = { [
|
|
312
|
+
cond = { [raw(`(${sql})`)]: Utils.asArray(params) };
|
|
279
313
|
operator ??= '$and';
|
|
280
314
|
}
|
|
281
|
-
else if (
|
|
282
|
-
cond = { [
|
|
315
|
+
else if (Utils.isString(cond)) {
|
|
316
|
+
cond = { [raw(`(${cond})`, Utils.asArray(params))]: [] };
|
|
283
317
|
operator ??= '$and';
|
|
284
318
|
}
|
|
285
319
|
else {
|
|
286
|
-
cond =
|
|
320
|
+
cond = QueryHelper.processWhere({
|
|
287
321
|
where: cond,
|
|
288
322
|
entityName: this.mainAlias.entityName,
|
|
289
323
|
metadata: this.metadata,
|
|
290
324
|
platform: this.platform,
|
|
291
325
|
aliasMap: this.getAliasMap(),
|
|
292
|
-
aliased: [
|
|
293
|
-
convertCustomTypes: this.flags.has(
|
|
326
|
+
aliased: [QueryType.SELECT, QueryType.COUNT].includes(this.type),
|
|
327
|
+
convertCustomTypes: this.flags.has(QueryFlag.CONVERT_CUSTOM_TYPES),
|
|
294
328
|
});
|
|
295
329
|
}
|
|
296
330
|
const op = operator || params;
|
|
297
|
-
const topLevel = !op || !
|
|
298
|
-
const criteriaNode =
|
|
331
|
+
const topLevel = !op || !Utils.hasObjectKeys(this._cond);
|
|
332
|
+
const criteriaNode = CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, cond);
|
|
299
333
|
const ignoreBranching = this.__populateWhere === 'infer';
|
|
300
|
-
if ([
|
|
334
|
+
if ([QueryType.UPDATE, QueryType.DELETE].includes(this.type) && criteriaNode.willAutoJoin(this, undefined, { ignoreBranching })) {
|
|
301
335
|
// use sub-query to support joining
|
|
302
|
-
this.setFlag(this.type ===
|
|
336
|
+
this.setFlag(this.type === QueryType.UPDATE ? QueryFlag.UPDATE_SUB_QUERY : QueryFlag.DELETE_SUB_QUERY);
|
|
303
337
|
this.select(this.mainAlias.metadata.primaryKeys, true);
|
|
304
338
|
}
|
|
305
339
|
if (topLevel) {
|
|
@@ -327,32 +361,32 @@ class QueryBuilder {
|
|
|
327
361
|
orderBy(orderBy) {
|
|
328
362
|
this.ensureNotFinalized();
|
|
329
363
|
this._orderBy = [];
|
|
330
|
-
|
|
331
|
-
const processed =
|
|
364
|
+
Utils.asArray(orderBy).forEach(o => {
|
|
365
|
+
const processed = QueryHelper.processWhere({
|
|
332
366
|
where: o,
|
|
333
367
|
entityName: this.mainAlias.entityName,
|
|
334
368
|
metadata: this.metadata,
|
|
335
369
|
platform: this.platform,
|
|
336
370
|
aliasMap: this.getAliasMap(),
|
|
337
|
-
aliased: [
|
|
371
|
+
aliased: [QueryType.SELECT, QueryType.COUNT].includes(this.type),
|
|
338
372
|
convertCustomTypes: false,
|
|
339
373
|
type: 'orderBy',
|
|
340
374
|
});
|
|
341
|
-
this._orderBy.push(
|
|
375
|
+
this._orderBy.push(CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, processed).process(this, { matchPopulateJoins: true, type: 'orderBy' }));
|
|
342
376
|
});
|
|
343
377
|
return this;
|
|
344
378
|
}
|
|
345
379
|
groupBy(fields) {
|
|
346
380
|
this.ensureNotFinalized();
|
|
347
|
-
this._groupBy =
|
|
381
|
+
this._groupBy = Utils.asArray(fields);
|
|
348
382
|
return this;
|
|
349
383
|
}
|
|
350
384
|
having(cond = {}, params, operator) {
|
|
351
385
|
this.ensureNotFinalized();
|
|
352
|
-
if (
|
|
353
|
-
cond = { [
|
|
386
|
+
if (Utils.isString(cond)) {
|
|
387
|
+
cond = { [raw(`(${cond})`, params)]: [] };
|
|
354
388
|
}
|
|
355
|
-
cond =
|
|
389
|
+
cond = CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, cond).process(this);
|
|
356
390
|
if (!this._having || !operator) {
|
|
357
391
|
this._having = cond;
|
|
358
392
|
}
|
|
@@ -373,11 +407,11 @@ class QueryBuilder {
|
|
|
373
407
|
this.ensureNotFinalized();
|
|
374
408
|
this._onConflict ??= [];
|
|
375
409
|
this._onConflict.push({
|
|
376
|
-
fields:
|
|
410
|
+
fields: isRaw(fields)
|
|
377
411
|
? fields
|
|
378
|
-
:
|
|
412
|
+
: Utils.asArray(fields).flatMap(f => {
|
|
379
413
|
const key = f.toString();
|
|
380
|
-
/*
|
|
414
|
+
/* v8 ignore next */
|
|
381
415
|
return meta.properties[key]?.fieldNames ?? [key];
|
|
382
416
|
}),
|
|
383
417
|
});
|
|
@@ -401,7 +435,7 @@ class QueryBuilder {
|
|
|
401
435
|
return this;
|
|
402
436
|
}
|
|
403
437
|
returning(fields) {
|
|
404
|
-
this._returning =
|
|
438
|
+
this._returning = Utils.asArray(fields);
|
|
405
439
|
return this;
|
|
406
440
|
}
|
|
407
441
|
/**
|
|
@@ -434,8 +468,8 @@ class QueryBuilder {
|
|
|
434
468
|
}
|
|
435
469
|
setLockMode(mode, tables) {
|
|
436
470
|
this.ensureNotFinalized();
|
|
437
|
-
if (mode != null && mode !==
|
|
438
|
-
throw
|
|
471
|
+
if (mode != null && mode !== LockMode.OPTIMISTIC && !this.context) {
|
|
472
|
+
throw ValidationError.transactionRequired();
|
|
439
473
|
}
|
|
440
474
|
this.lockMode = mode;
|
|
441
475
|
this.lockTables = tables;
|
|
@@ -477,7 +511,7 @@ class QueryBuilder {
|
|
|
477
511
|
*/
|
|
478
512
|
comment(comment) {
|
|
479
513
|
this.ensureNotFinalized();
|
|
480
|
-
this._comments.push(...
|
|
514
|
+
this._comments.push(...Utils.asArray(comment));
|
|
481
515
|
return this;
|
|
482
516
|
}
|
|
483
517
|
/**
|
|
@@ -487,7 +521,7 @@ class QueryBuilder {
|
|
|
487
521
|
*/
|
|
488
522
|
hintComment(comment) {
|
|
489
523
|
this.ensureNotFinalized();
|
|
490
|
-
this._hintComments.push(...
|
|
524
|
+
this._hintComments.push(...Utils.asArray(comment));
|
|
491
525
|
return this;
|
|
492
526
|
}
|
|
493
527
|
from(target, aliasName) {
|
|
@@ -496,7 +530,7 @@ class QueryBuilder {
|
|
|
496
530
|
this.fromSubQuery(target, aliasName);
|
|
497
531
|
}
|
|
498
532
|
else {
|
|
499
|
-
const entityName =
|
|
533
|
+
const entityName = Utils.className(target);
|
|
500
534
|
if (aliasName && this._mainAlias && entityName !== this._mainAlias.aliasName) {
|
|
501
535
|
throw new Error(`Cannot override the alias to '${aliasName}' since a query already contains references to '${this._mainAlias.aliasName}'`);
|
|
502
536
|
}
|
|
@@ -511,22 +545,22 @@ class QueryBuilder {
|
|
|
511
545
|
this._query = {};
|
|
512
546
|
this.finalize();
|
|
513
547
|
const qb = this.getQueryBase(processVirtualEntity);
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
548
|
+
Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type, this._cond, qb), this._cond && !this._onConflict);
|
|
549
|
+
Utils.runIfNotEmpty(() => qb.groupBy(this.prepareFields(this._groupBy, 'groupBy')), this._groupBy);
|
|
550
|
+
Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type, this._having, qb, undefined, 'having'), this._having);
|
|
551
|
+
Utils.runIfNotEmpty(() => {
|
|
518
552
|
const queryOrder = this.helper.getQueryOrder(this.type, this._orderBy, this._populateMap);
|
|
519
553
|
if (queryOrder.length > 0) {
|
|
520
|
-
const sql =
|
|
554
|
+
const sql = Utils.unique(queryOrder).join(', ');
|
|
521
555
|
qb.orderBy(sql);
|
|
522
556
|
return;
|
|
523
557
|
}
|
|
524
558
|
}, this._orderBy);
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
559
|
+
Utils.runIfNotEmpty(() => qb.limit(this._limit), this._limit != null);
|
|
560
|
+
Utils.runIfNotEmpty(() => qb.offset(this._offset), this._offset);
|
|
561
|
+
Utils.runIfNotEmpty(() => qb.comment(this._comments), this._comments);
|
|
562
|
+
Utils.runIfNotEmpty(() => qb.hintComment(this._hintComments), this._hintComments);
|
|
563
|
+
Utils.runIfNotEmpty(() => this.helper.appendOnConflictClause(QueryType.UPSERT, this._onConflict, qb), this._onConflict);
|
|
530
564
|
if (this.lockMode) {
|
|
531
565
|
this.helper.getLockSQL(qb, this.lockMode, this.lockTables);
|
|
532
566
|
}
|
|
@@ -538,7 +572,7 @@ class QueryBuilder {
|
|
|
538
572
|
* @internal
|
|
539
573
|
*/
|
|
540
574
|
clearRawFragmentsCache() {
|
|
541
|
-
this.rawFragments.forEach(key =>
|
|
575
|
+
this.rawFragments.forEach(key => RawQueryFragment.remove(key));
|
|
542
576
|
this.rawFragments.clear();
|
|
543
577
|
}
|
|
544
578
|
/**
|
|
@@ -552,7 +586,7 @@ class QueryBuilder {
|
|
|
552
586
|
*/
|
|
553
587
|
toRaw() {
|
|
554
588
|
const { sql, params } = this.toQuery();
|
|
555
|
-
return
|
|
589
|
+
return raw(sql, params);
|
|
556
590
|
}
|
|
557
591
|
toQuery() {
|
|
558
592
|
if (this._query?.sql) {
|
|
@@ -640,12 +674,12 @@ class QueryBuilder {
|
|
|
640
674
|
options = typeof options === 'boolean' ? { mapResults: options } : (options ?? {});
|
|
641
675
|
options.mergeResults ??= true;
|
|
642
676
|
options.mapResults ??= true;
|
|
643
|
-
const isRunType = [
|
|
677
|
+
const isRunType = [QueryType.INSERT, QueryType.UPDATE, QueryType.DELETE, QueryType.TRUNCATE].includes(this.type);
|
|
644
678
|
method ??= isRunType ? 'run' : 'all';
|
|
645
679
|
if (!this.connectionType && isRunType) {
|
|
646
680
|
this.connectionType = 'write';
|
|
647
681
|
}
|
|
648
|
-
if (!this.finalized && method === 'get' && this.type ===
|
|
682
|
+
if (!this.finalized && method === 'get' && this.type === QueryType.SELECT) {
|
|
649
683
|
this.limit(1);
|
|
650
684
|
}
|
|
651
685
|
const query = this.toQuery();
|
|
@@ -698,14 +732,14 @@ class QueryBuilder {
|
|
|
698
732
|
const res = await this.execute('all', true);
|
|
699
733
|
const entities = [];
|
|
700
734
|
function propagatePopulateHint(entity, hint) {
|
|
701
|
-
|
|
735
|
+
helper(entity).__serializationContext.populate = hint.concat(helper(entity).__serializationContext.populate ?? []);
|
|
702
736
|
hint.forEach(hint => {
|
|
703
737
|
const [propName] = hint.field.split(':', 2);
|
|
704
|
-
const value =
|
|
705
|
-
if (
|
|
738
|
+
const value = Reference.unwrapReference(entity[propName]);
|
|
739
|
+
if (Utils.isEntity(value)) {
|
|
706
740
|
propagatePopulateHint(value, hint.children ?? []);
|
|
707
741
|
}
|
|
708
|
-
else if (
|
|
742
|
+
else if (Utils.isCollection(value)) {
|
|
709
743
|
value.populated();
|
|
710
744
|
value.getItems(false).forEach(item => propagatePopulateHint(item, hint.children ?? []));
|
|
711
745
|
}
|
|
@@ -719,7 +753,7 @@ class QueryBuilder {
|
|
|
719
753
|
break;
|
|
720
754
|
}
|
|
721
755
|
}
|
|
722
|
-
return
|
|
756
|
+
return Utils.unique(entities);
|
|
723
757
|
}
|
|
724
758
|
/**
|
|
725
759
|
* Executes the query, returning the first result or null
|
|
@@ -736,7 +770,7 @@ class QueryBuilder {
|
|
|
736
770
|
*/
|
|
737
771
|
async getCount(field, distinct) {
|
|
738
772
|
let res;
|
|
739
|
-
if (this.type ===
|
|
773
|
+
if (this.type === QueryType.COUNT) {
|
|
740
774
|
res = await this.execute('get', false);
|
|
741
775
|
}
|
|
742
776
|
else {
|
|
@@ -756,26 +790,6 @@ class QueryBuilder {
|
|
|
756
790
|
await this.clone().getCount(),
|
|
757
791
|
];
|
|
758
792
|
}
|
|
759
|
-
/**
|
|
760
|
-
* Provides promise-like interface so we can await the QB instance.
|
|
761
|
-
*/
|
|
762
|
-
then(onfulfilled, onrejected) {
|
|
763
|
-
let type = this.type;
|
|
764
|
-
if (this.flags.has(core_1.QueryFlag.UPDATE_SUB_QUERY) || this.flags.has(core_1.QueryFlag.DELETE_SUB_QUERY)) {
|
|
765
|
-
type = enums_1.QueryType.UPDATE;
|
|
766
|
-
}
|
|
767
|
-
switch (type) {
|
|
768
|
-
case enums_1.QueryType.INSERT:
|
|
769
|
-
case enums_1.QueryType.UPDATE:
|
|
770
|
-
case enums_1.QueryType.DELETE:
|
|
771
|
-
case enums_1.QueryType.UPSERT:
|
|
772
|
-
case enums_1.QueryType.TRUNCATE:
|
|
773
|
-
return this.execute('run').then(onfulfilled, onrejected);
|
|
774
|
-
case enums_1.QueryType.COUNT:
|
|
775
|
-
return this.getCount().then(onfulfilled, onrejected);
|
|
776
|
-
case enums_1.QueryType.SELECT: return this.getResultList().then(onfulfilled, onrejected);
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
793
|
/**
|
|
780
794
|
* Returns native query builder instance with sub-query aliased with given alias.
|
|
781
795
|
* You can provide `EntityName.propName` as alias, then the field name will be used based on the metadata
|
|
@@ -785,7 +799,7 @@ class QueryBuilder {
|
|
|
785
799
|
if (alias.includes('.')) {
|
|
786
800
|
const [a, f] = alias.split('.');
|
|
787
801
|
const meta = this.metadata.find(a);
|
|
788
|
-
/*
|
|
802
|
+
/* v8 ignore next */
|
|
789
803
|
alias = meta?.properties[f]?.fieldNames[0] ?? alias;
|
|
790
804
|
}
|
|
791
805
|
qb.as(alias);
|
|
@@ -805,15 +819,15 @@ class QueryBuilder {
|
|
|
805
819
|
'_schema', '_indexHint', '_cache', 'subQueries', 'lockMode', 'lockTables', '_groupBy', '_having', '_returning',
|
|
806
820
|
'_comments', '_hintComments', 'rawFragments', 'aliasCounter',
|
|
807
821
|
];
|
|
808
|
-
|
|
822
|
+
RawQueryFragment.cloneRegistry = this.rawFragments;
|
|
809
823
|
for (const prop of Object.keys(this)) {
|
|
810
824
|
if (reset.includes(prop) || ['_helper', '_query'].includes(prop)) {
|
|
811
825
|
continue;
|
|
812
826
|
}
|
|
813
|
-
qb[prop] = properties.includes(prop) ?
|
|
827
|
+
qb[prop] = properties.includes(prop) ? Utils.copy(this[prop]) : this[prop];
|
|
814
828
|
}
|
|
815
|
-
delete
|
|
816
|
-
/*
|
|
829
|
+
delete RawQueryFragment.cloneRegistry;
|
|
830
|
+
/* v8 ignore next 3 */
|
|
817
831
|
if (this._fields && !reset.includes('_fields')) {
|
|
818
832
|
qb._fields = [...this._fields];
|
|
819
833
|
}
|
|
@@ -846,11 +860,11 @@ class QueryBuilder {
|
|
|
846
860
|
if (res instanceof QueryBuilder) {
|
|
847
861
|
return `(${res.getFormattedQuery()}) as ${this.platform.quoteIdentifier(this.alias)}`;
|
|
848
862
|
}
|
|
849
|
-
if (res instanceof
|
|
863
|
+
if (res instanceof RawQueryFragment) {
|
|
850
864
|
const query = this.platform.formatQuery(res.sql, res.params);
|
|
851
865
|
return `(${query}) as ${this.platform.quoteIdentifier(this.alias)}`;
|
|
852
866
|
}
|
|
853
|
-
/*
|
|
867
|
+
/* v8 ignore next */
|
|
854
868
|
return res;
|
|
855
869
|
}
|
|
856
870
|
joinReference(field, alias, cond, type, path, schema, subquery) {
|
|
@@ -858,14 +872,14 @@ class QueryBuilder {
|
|
|
858
872
|
if (typeof field === 'object') {
|
|
859
873
|
const prop = {
|
|
860
874
|
name: '__subquery__',
|
|
861
|
-
kind:
|
|
875
|
+
kind: ReferenceKind.MANY_TO_ONE,
|
|
862
876
|
};
|
|
863
877
|
if (field instanceof QueryBuilder) {
|
|
864
878
|
prop.type = field.mainAlias.entityName;
|
|
865
879
|
prop.targetMeta = field.mainAlias.metadata;
|
|
866
880
|
field = field.getNativeQuery();
|
|
867
881
|
}
|
|
868
|
-
if (field instanceof
|
|
882
|
+
if (field instanceof RawQueryFragment) {
|
|
869
883
|
field = this.platform.formatQuery(field.sql, field.params);
|
|
870
884
|
}
|
|
871
885
|
this._joins[`${this.alias}.${prop.name}#${alias}`] = {
|
|
@@ -894,22 +908,22 @@ class QueryBuilder {
|
|
|
894
908
|
throw new Error(`Trying to join ${q(field)}, but ${q(fromField)} is not a defined relation on ${meta.className}.`);
|
|
895
909
|
}
|
|
896
910
|
this.createAlias(prop.type, alias);
|
|
897
|
-
cond =
|
|
911
|
+
cond = QueryHelper.processWhere({
|
|
898
912
|
where: cond,
|
|
899
913
|
entityName: this.mainAlias.entityName,
|
|
900
914
|
metadata: this.metadata,
|
|
901
915
|
platform: this.platform,
|
|
902
916
|
aliasMap: this.getAliasMap(),
|
|
903
|
-
aliased: [
|
|
917
|
+
aliased: [QueryType.SELECT, QueryType.COUNT].includes(this.type),
|
|
904
918
|
});
|
|
905
919
|
let aliasedName = `${fromAlias}.${prop.name}#${alias}`;
|
|
906
920
|
path ??= `${(Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? entityName)}.${prop.name}`;
|
|
907
|
-
if (prop.kind ===
|
|
921
|
+
if (prop.kind === ReferenceKind.ONE_TO_MANY) {
|
|
908
922
|
this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond, schema);
|
|
909
923
|
}
|
|
910
|
-
else if (prop.kind ===
|
|
924
|
+
else if (prop.kind === ReferenceKind.MANY_TO_MANY) {
|
|
911
925
|
let pivotAlias = alias;
|
|
912
|
-
if (type !==
|
|
926
|
+
if (type !== JoinType.pivotJoin) {
|
|
913
927
|
const oldPivotAlias = this.getAliasForJoinPath(path + '[pivot]');
|
|
914
928
|
pivotAlias = oldPivotAlias ?? this.getNextAlias(prop.pivotEntity);
|
|
915
929
|
aliasedName = `${fromAlias}.${prop.name}#${pivotAlias}`;
|
|
@@ -918,7 +932,7 @@ class QueryBuilder {
|
|
|
918
932
|
Object.assign(this._joins, joins);
|
|
919
933
|
this.createAlias(prop.pivotEntity, pivotAlias);
|
|
920
934
|
}
|
|
921
|
-
else if (prop.kind ===
|
|
935
|
+
else if (prop.kind === ReferenceKind.ONE_TO_ONE) {
|
|
922
936
|
this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond, schema);
|
|
923
937
|
}
|
|
924
938
|
else { // MANY_TO_ONE
|
|
@@ -935,12 +949,12 @@ class QueryBuilder {
|
|
|
935
949
|
return this.helper.mapper(name, this.type, undefined, type === 'groupBy' ? null : undefined);
|
|
936
950
|
};
|
|
937
951
|
fields.forEach(field => {
|
|
938
|
-
const rawField =
|
|
952
|
+
const rawField = RawQueryFragment.getKnownFragment(field, false);
|
|
939
953
|
if (rawField) {
|
|
940
954
|
ret.push(rawField);
|
|
941
955
|
return;
|
|
942
956
|
}
|
|
943
|
-
if (!
|
|
957
|
+
if (!Utils.isString(field)) {
|
|
944
958
|
ret.push(field);
|
|
945
959
|
return;
|
|
946
960
|
}
|
|
@@ -951,23 +965,23 @@ class QueryBuilder {
|
|
|
951
965
|
}
|
|
952
966
|
const [a, f] = this.helper.splitField(field);
|
|
953
967
|
const prop = this.helper.getProperty(f, a);
|
|
954
|
-
/*
|
|
955
|
-
if (prop && [
|
|
968
|
+
/* v8 ignore next 3 */
|
|
969
|
+
if (prop && [ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
|
|
956
970
|
return;
|
|
957
971
|
}
|
|
958
972
|
if (prop?.persist === false && !prop.embedded && !prop.formula && type === 'where') {
|
|
959
973
|
return;
|
|
960
974
|
}
|
|
961
|
-
if (prop?.embedded || (prop?.kind ===
|
|
975
|
+
if (prop?.embedded || (prop?.kind === ReferenceKind.EMBEDDED && prop.object)) {
|
|
962
976
|
const name = prop.embeddedPath?.join('.') ?? prop.fieldNames[0];
|
|
963
977
|
const aliased = this._aliases[a] ? `${a}.${name}` : name;
|
|
964
978
|
ret.push(getFieldName(aliased));
|
|
965
979
|
return;
|
|
966
980
|
}
|
|
967
|
-
if (prop?.kind ===
|
|
981
|
+
if (prop?.kind === ReferenceKind.EMBEDDED) {
|
|
968
982
|
const nest = (prop) => {
|
|
969
983
|
for (const childProp of Object.values(prop.embeddedProps)) {
|
|
970
|
-
if (childProp.fieldNames && (childProp.kind !==
|
|
984
|
+
if (childProp.fieldNames && (childProp.kind !== ReferenceKind.EMBEDDED || childProp.object) && childProp.persist !== false) {
|
|
971
985
|
ret.push(getFieldName(childProp.fieldNames[0]));
|
|
972
986
|
}
|
|
973
987
|
else {
|
|
@@ -985,9 +999,9 @@ class QueryBuilder {
|
|
|
985
999
|
ret.push(getFieldName(field));
|
|
986
1000
|
});
|
|
987
1001
|
const meta = this.mainAlias.metadata;
|
|
988
|
-
/*
|
|
1002
|
+
/* v8 ignore next */
|
|
989
1003
|
const requiresSQLConversion = meta?.props.filter(p => p.hasConvertToJSValueSQL && p.persist !== false) ?? [];
|
|
990
|
-
if (this.flags.has(
|
|
1004
|
+
if (this.flags.has(QueryFlag.CONVERT_CUSTOM_TYPES) && (fields.includes('*') || fields.includes(`${this.mainAlias.aliasName}.*`)) && requiresSQLConversion.length > 0) {
|
|
991
1005
|
for (const p of requiresSQLConversion) {
|
|
992
1006
|
ret.push(this.helper.mapper(p.name, this.type));
|
|
993
1007
|
}
|
|
@@ -1000,22 +1014,22 @@ class QueryBuilder {
|
|
|
1000
1014
|
}
|
|
1001
1015
|
}
|
|
1002
1016
|
}
|
|
1003
|
-
return
|
|
1017
|
+
return Utils.unique(ret);
|
|
1004
1018
|
}
|
|
1005
1019
|
init(type, data, cond) {
|
|
1006
1020
|
this.ensureNotFinalized();
|
|
1007
1021
|
this._type = type;
|
|
1008
|
-
if ([
|
|
1022
|
+
if ([QueryType.UPDATE, QueryType.DELETE].includes(type) && Utils.hasObjectKeys(this._cond)) {
|
|
1009
1023
|
throw new Error(`You are trying to call \`qb.where().${type.toLowerCase()}()\`. Calling \`qb.${type.toLowerCase()}()\` before \`qb.where()\` is required.`);
|
|
1010
1024
|
}
|
|
1011
1025
|
if (!this.helper.isTableNameAliasRequired(type)) {
|
|
1012
1026
|
delete this._fields;
|
|
1013
1027
|
}
|
|
1014
1028
|
if (data) {
|
|
1015
|
-
if (
|
|
1016
|
-
data = this.em?.getComparator().prepareEntity(data) ??
|
|
1029
|
+
if (Utils.isEntity(data)) {
|
|
1030
|
+
data = this.em?.getComparator().prepareEntity(data) ?? serialize(data);
|
|
1017
1031
|
}
|
|
1018
|
-
this._data = this.helper.processData(data, this.flags.has(
|
|
1032
|
+
this._data = this.helper.processData(data, this.flags.has(QueryFlag.CONVERT_CUSTOM_TYPES), false);
|
|
1019
1033
|
}
|
|
1020
1034
|
if (cond) {
|
|
1021
1035
|
this.where(cond);
|
|
@@ -1031,7 +1045,7 @@ class QueryBuilder {
|
|
|
1031
1045
|
const tableName = subQuery ? subQuery.as(aliasName) : this.helper.getTableName(entityName);
|
|
1032
1046
|
const joinSchema = this._schema ?? this.em?.schema ?? schema;
|
|
1033
1047
|
if (metadata?.virtual && processVirtualEntity) {
|
|
1034
|
-
qb.from(
|
|
1048
|
+
qb.from(raw(this.fromVirtual(metadata)), { indexHint: this._indexHint });
|
|
1035
1049
|
}
|
|
1036
1050
|
else {
|
|
1037
1051
|
qb.from(tableName, {
|
|
@@ -1041,34 +1055,34 @@ class QueryBuilder {
|
|
|
1041
1055
|
});
|
|
1042
1056
|
}
|
|
1043
1057
|
switch (this.type) {
|
|
1044
|
-
case
|
|
1058
|
+
case QueryType.SELECT:
|
|
1045
1059
|
qb.select(this.prepareFields(this._fields));
|
|
1046
1060
|
if (this._distinctOn) {
|
|
1047
1061
|
qb.distinctOn(this.prepareFields(this._distinctOn));
|
|
1048
1062
|
}
|
|
1049
|
-
else if (this.flags.has(
|
|
1063
|
+
else if (this.flags.has(QueryFlag.DISTINCT)) {
|
|
1050
1064
|
qb.distinct();
|
|
1051
1065
|
}
|
|
1052
1066
|
this.helper.processJoins(qb, this._joins, joinSchema);
|
|
1053
1067
|
break;
|
|
1054
|
-
case
|
|
1068
|
+
case QueryType.COUNT: {
|
|
1055
1069
|
const fields = this._fields.map(f => this.helper.mapper(f, this.type));
|
|
1056
|
-
qb.count(fields, this.flags.has(
|
|
1070
|
+
qb.count(fields, this.flags.has(QueryFlag.DISTINCT));
|
|
1057
1071
|
this.helper.processJoins(qb, this._joins, joinSchema);
|
|
1058
1072
|
break;
|
|
1059
1073
|
}
|
|
1060
|
-
case
|
|
1074
|
+
case QueryType.INSERT:
|
|
1061
1075
|
qb.insert(this._data);
|
|
1062
1076
|
break;
|
|
1063
|
-
case
|
|
1077
|
+
case QueryType.UPDATE:
|
|
1064
1078
|
qb.update(this._data);
|
|
1065
1079
|
this.helper.processJoins(qb, this._joins, joinSchema);
|
|
1066
1080
|
this.helper.updateVersionProperty(qb, this._data);
|
|
1067
1081
|
break;
|
|
1068
|
-
case
|
|
1082
|
+
case QueryType.DELETE:
|
|
1069
1083
|
qb.delete();
|
|
1070
1084
|
break;
|
|
1071
|
-
case
|
|
1085
|
+
case QueryType.TRUNCATE:
|
|
1072
1086
|
qb.truncate();
|
|
1073
1087
|
break;
|
|
1074
1088
|
}
|
|
@@ -1104,31 +1118,31 @@ class QueryBuilder {
|
|
|
1104
1118
|
this.processPopulateHint();
|
|
1105
1119
|
if (meta && (this._fields?.includes('*') || this._fields?.includes(`${this.mainAlias.aliasName}.*`))) {
|
|
1106
1120
|
meta.props
|
|
1107
|
-
.filter(prop => prop.formula && (!prop.lazy || this.flags.has(
|
|
1121
|
+
.filter(prop => prop.formula && (!prop.lazy || this.flags.has(QueryFlag.INCLUDE_LAZY_FORMULAS)))
|
|
1108
1122
|
.map(prop => {
|
|
1109
1123
|
const alias = this.platform.quoteIdentifier(this.mainAlias.aliasName);
|
|
1110
1124
|
const aliased = this.platform.quoteIdentifier(prop.fieldNames[0]);
|
|
1111
1125
|
return `${prop.formula(alias)} as ${aliased}`;
|
|
1112
1126
|
})
|
|
1113
1127
|
.filter(field => !this._fields.some(f => {
|
|
1114
|
-
if (f instanceof
|
|
1128
|
+
if (f instanceof RawQueryFragment) {
|
|
1115
1129
|
return f.sql === field && f.params.length === 0;
|
|
1116
1130
|
}
|
|
1117
1131
|
return f === field;
|
|
1118
1132
|
}))
|
|
1119
|
-
.forEach(field => this._fields.push(
|
|
1133
|
+
.forEach(field => this._fields.push(raw(field)));
|
|
1120
1134
|
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1135
|
+
QueryHelper.processObjectParams(this._data);
|
|
1136
|
+
QueryHelper.processObjectParams(this._cond);
|
|
1137
|
+
QueryHelper.processObjectParams(this._having);
|
|
1124
1138
|
// automatically enable paginate flag when we detect to-many joins, but only if there is no `group by` clause
|
|
1125
|
-
if (!this.flags.has(
|
|
1126
|
-
this.flags.add(
|
|
1139
|
+
if (!this.flags.has(QueryFlag.DISABLE_PAGINATE) && this._groupBy.length === 0 && this.hasToManyJoins()) {
|
|
1140
|
+
this.flags.add(QueryFlag.PAGINATE);
|
|
1127
1141
|
}
|
|
1128
|
-
if (meta && this.flags.has(
|
|
1142
|
+
if (meta && this.flags.has(QueryFlag.PAGINATE) && (this._limit > 0 || this._offset > 0)) {
|
|
1129
1143
|
this.wrapPaginateSubQuery(meta);
|
|
1130
1144
|
}
|
|
1131
|
-
if (meta && (this.flags.has(
|
|
1145
|
+
if (meta && (this.flags.has(QueryFlag.UPDATE_SUB_QUERY) || this.flags.has(QueryFlag.DELETE_SUB_QUERY))) {
|
|
1132
1146
|
this.wrapModifySubQuery(meta);
|
|
1133
1147
|
}
|
|
1134
1148
|
this.finalized = true;
|
|
@@ -1139,10 +1153,10 @@ class QueryBuilder {
|
|
|
1139
1153
|
return;
|
|
1140
1154
|
}
|
|
1141
1155
|
const meta = this.mainAlias.metadata;
|
|
1142
|
-
if (meta && this.flags.has(
|
|
1156
|
+
if (meta && this.flags.has(QueryFlag.AUTO_JOIN_ONE_TO_ONE_OWNER)) {
|
|
1143
1157
|
const relationsToPopulate = this._populate.map(({ field }) => field);
|
|
1144
1158
|
meta.relations
|
|
1145
|
-
.filter(prop => prop.kind ===
|
|
1159
|
+
.filter(prop => prop.kind === ReferenceKind.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name) && !relationsToPopulate.includes(`${prop.name}:ref`))
|
|
1146
1160
|
.map(prop => ({ field: `${prop.name}:ref` }))
|
|
1147
1161
|
.forEach(item => this._populate.push(item));
|
|
1148
1162
|
}
|
|
@@ -1158,7 +1172,7 @@ class QueryBuilder {
|
|
|
1158
1172
|
const prop = meta.properties[fromField];
|
|
1159
1173
|
const alias = this.getNextAlias(prop.pivotEntity ?? prop.type);
|
|
1160
1174
|
const aliasedName = `${fromAlias}.${prop.name}#${alias}`;
|
|
1161
|
-
this._joins[aliasedName] = this.helper.joinOneToReference(prop, this.mainAlias.aliasName, alias,
|
|
1175
|
+
this._joins[aliasedName] = this.helper.joinOneToReference(prop, this.mainAlias.aliasName, alias, JoinType.leftJoin);
|
|
1162
1176
|
this._joins[aliasedName].path = `${(Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? meta.className)}.${prop.name}`;
|
|
1163
1177
|
this._populateMap[aliasedName] = this._joins[aliasedName].alias;
|
|
1164
1178
|
}
|
|
@@ -1169,16 +1183,16 @@ class QueryBuilder {
|
|
|
1169
1183
|
}
|
|
1170
1184
|
processPopulateWhere(filter) {
|
|
1171
1185
|
const key = filter ? '_populateFilter' : '_populateWhere';
|
|
1172
|
-
if (this[key] == null || this[key] ===
|
|
1186
|
+
if (this[key] == null || this[key] === PopulateHint.ALL) {
|
|
1173
1187
|
return;
|
|
1174
1188
|
}
|
|
1175
1189
|
let joins = Object.values(this._joins);
|
|
1176
1190
|
for (const join of joins) {
|
|
1177
1191
|
join.cond_ ??= join.cond;
|
|
1178
|
-
join.cond =
|
|
1192
|
+
join.cond = { ...join.cond };
|
|
1179
1193
|
}
|
|
1180
1194
|
if (typeof this[key] === 'object') {
|
|
1181
|
-
const cond =
|
|
1195
|
+
const cond = CriteriaNodeFactory
|
|
1182
1196
|
.createNode(this.metadata, this.mainAlias.entityName, this[key])
|
|
1183
1197
|
.process(this, { matchPopulateJoins: true, ignoreBranching: true, preferNoBranch: true });
|
|
1184
1198
|
// there might be new joins created by processing the `populateWhere` object
|
|
@@ -1188,11 +1202,11 @@ class QueryBuilder {
|
|
|
1188
1202
|
}
|
|
1189
1203
|
mergeOnConditions(joins, cond, filter, op) {
|
|
1190
1204
|
for (const k of Object.keys(cond)) {
|
|
1191
|
-
if (
|
|
1205
|
+
if (Utils.isOperator(k)) {
|
|
1192
1206
|
if (Array.isArray(cond[k])) {
|
|
1193
1207
|
cond[k].forEach((c) => this.mergeOnConditions(joins, c, filter, k));
|
|
1194
1208
|
}
|
|
1195
|
-
/*
|
|
1209
|
+
/* v8 ignore next */
|
|
1196
1210
|
this.mergeOnConditions(joins, cond[k], filter, k);
|
|
1197
1211
|
}
|
|
1198
1212
|
const [alias] = this.helper.splitField(k);
|
|
@@ -1202,13 +1216,13 @@ class QueryBuilder {
|
|
|
1202
1216
|
// https://stackoverflow.com/a/56815807/3665878
|
|
1203
1217
|
if (parentJoin && !filter) {
|
|
1204
1218
|
const nested = (parentJoin.nested ??= new Set());
|
|
1205
|
-
join.type = join.type ===
|
|
1206
|
-
?
|
|
1207
|
-
:
|
|
1219
|
+
join.type = join.type === JoinType.innerJoin || ([ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(parentJoin.prop.kind))
|
|
1220
|
+
? JoinType.nestedInnerJoin
|
|
1221
|
+
: JoinType.nestedLeftJoin;
|
|
1208
1222
|
nested.add(join);
|
|
1209
1223
|
}
|
|
1210
1224
|
if (join.cond[k]) {
|
|
1211
|
-
/*
|
|
1225
|
+
/* v8 ignore next */
|
|
1212
1226
|
join.cond = { [op ?? '$and']: [join.cond, { [k]: cond[k] }] };
|
|
1213
1227
|
}
|
|
1214
1228
|
else if (op === '$or') {
|
|
@@ -1222,10 +1236,8 @@ class QueryBuilder {
|
|
|
1222
1236
|
}
|
|
1223
1237
|
}
|
|
1224
1238
|
hasToManyJoins() {
|
|
1225
|
-
// console.log(this._joins);
|
|
1226
1239
|
return Object.values(this._joins).some(join => {
|
|
1227
|
-
|
|
1228
|
-
return [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(join.prop.kind);
|
|
1240
|
+
return [ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(join.prop.kind);
|
|
1229
1241
|
});
|
|
1230
1242
|
}
|
|
1231
1243
|
wrapPaginateSubQuery(meta) {
|
|
@@ -1245,8 +1257,8 @@ class QueryBuilder {
|
|
|
1245
1257
|
const orderBy = [];
|
|
1246
1258
|
for (const orderMap of this._orderBy) {
|
|
1247
1259
|
for (const [field, direction] of Object.entries(orderMap)) {
|
|
1248
|
-
if (
|
|
1249
|
-
const rawField =
|
|
1260
|
+
if (RawQueryFragment.isKnownFragment(field)) {
|
|
1261
|
+
const rawField = RawQueryFragment.getKnownFragment(field, false);
|
|
1250
1262
|
this.rawFragments.add(field);
|
|
1251
1263
|
orderBy.push({ [rawField.clone()]: direction });
|
|
1252
1264
|
continue;
|
|
@@ -1259,7 +1271,7 @@ class QueryBuilder {
|
|
|
1259
1271
|
addToSelect.push(fieldName);
|
|
1260
1272
|
}
|
|
1261
1273
|
const quoted = this.platform.quoteIdentifier(fieldName);
|
|
1262
|
-
const key =
|
|
1274
|
+
const key = raw(`min(${quoted}${type})`);
|
|
1263
1275
|
orderBy.push({ [key]: direction });
|
|
1264
1276
|
}
|
|
1265
1277
|
}
|
|
@@ -1273,17 +1285,17 @@ class QueryBuilder {
|
|
|
1273
1285
|
if (typeof field === 'object' && field && '__as' in field) {
|
|
1274
1286
|
return field.__as === prop;
|
|
1275
1287
|
}
|
|
1276
|
-
if (field instanceof
|
|
1288
|
+
if (field instanceof RawQueryFragment) {
|
|
1277
1289
|
// not perfect, but should work most of the time, ideally we should check only the alias (`... as alias`)
|
|
1278
1290
|
return field.sql.includes(prop);
|
|
1279
1291
|
}
|
|
1280
1292
|
return false;
|
|
1281
1293
|
});
|
|
1282
|
-
/*
|
|
1283
|
-
if (field instanceof
|
|
1294
|
+
/* v8 ignore next 3 */
|
|
1295
|
+
if (field instanceof RawQueryFragment) {
|
|
1284
1296
|
innerQuery.select(field);
|
|
1285
1297
|
}
|
|
1286
|
-
else if (field instanceof
|
|
1298
|
+
else if (field instanceof NativeQueryBuilder) {
|
|
1287
1299
|
innerQuery.select(field.toRaw());
|
|
1288
1300
|
}
|
|
1289
1301
|
else if (field) {
|
|
@@ -1297,11 +1309,11 @@ class QueryBuilder {
|
|
|
1297
1309
|
subSubQuery.select(pks).from(innerQuery);
|
|
1298
1310
|
this._limit = undefined;
|
|
1299
1311
|
this._offset = undefined;
|
|
1300
|
-
if (!this._fields.some(f =>
|
|
1312
|
+
if (!this._fields.some(f => RawQueryFragment.isKnownFragment(f))) {
|
|
1301
1313
|
this.pruneExtraJoins(meta);
|
|
1302
1314
|
}
|
|
1303
1315
|
const { sql, params } = subSubQuery.compile();
|
|
1304
|
-
this.select(this._fields).where({ [
|
|
1316
|
+
this.select(this._fields).where({ [Utils.getPrimaryKeyHash(meta.primaryKeys)]: { $in: raw(sql, params) } });
|
|
1305
1317
|
}
|
|
1306
1318
|
pruneExtraJoins(meta) {
|
|
1307
1319
|
// remove joins that are not used for population or ordering to improve performance
|
|
@@ -1344,14 +1356,14 @@ class QueryBuilder {
|
|
|
1344
1356
|
// wrap one more time to get around MySQL limitations
|
|
1345
1357
|
// https://stackoverflow.com/questions/45494/mysql-error-1093-cant-specify-target-table-for-update-in-from-clause
|
|
1346
1358
|
const subSubQuery = this.platform.createNativeQueryBuilder();
|
|
1347
|
-
const method = this.flags.has(
|
|
1359
|
+
const method = this.flags.has(QueryFlag.UPDATE_SUB_QUERY) ? 'update' : 'delete';
|
|
1348
1360
|
const pks = this.prepareFields(meta.primaryKeys, 'sub-query');
|
|
1349
1361
|
this._cond = {}; // otherwise we would trigger validation error
|
|
1350
1362
|
this._joins = {}; // included in the subquery
|
|
1351
1363
|
subSubQuery.select(pks).from(subQuery.as(this.mainAlias.aliasName));
|
|
1352
1364
|
const { sql, params } = subSubQuery.compile();
|
|
1353
1365
|
this[method](this._data).where({
|
|
1354
|
-
[
|
|
1366
|
+
[Utils.getPrimaryKeyHash(meta.primaryKeys)]: { $in: raw(sql, params) },
|
|
1355
1367
|
});
|
|
1356
1368
|
}
|
|
1357
1369
|
getSchema(alias) {
|
|
@@ -1385,10 +1397,10 @@ class QueryBuilder {
|
|
|
1385
1397
|
this.createMainAlias(entityName, aliasName);
|
|
1386
1398
|
}
|
|
1387
1399
|
createQueryBuilderHelper() {
|
|
1388
|
-
return new
|
|
1400
|
+
return new QueryBuilderHelper(this.mainAlias.entityName, this.mainAlias.aliasName, this._aliases, this.subQueries, this.driver);
|
|
1389
1401
|
}
|
|
1390
1402
|
ensureFromClause() {
|
|
1391
|
-
/*
|
|
1403
|
+
/* v8 ignore next 3 */
|
|
1392
1404
|
if (!this._mainAlias) {
|
|
1393
1405
|
throw new Error(`Cannot proceed to build a query because the main alias is not set.`);
|
|
1394
1406
|
}
|
|
@@ -1398,9 +1410,9 @@ class QueryBuilder {
|
|
|
1398
1410
|
throw new Error('This QueryBuilder instance is already finalized, clone it first if you want to modify it.');
|
|
1399
1411
|
}
|
|
1400
1412
|
}
|
|
1401
|
-
/*
|
|
1413
|
+
/* v8 ignore start */
|
|
1402
1414
|
/** @ignore */
|
|
1403
|
-
[
|
|
1415
|
+
[inspect.custom](depth = 2) {
|
|
1404
1416
|
const object = { ...this };
|
|
1405
1417
|
const hidden = ['metadata', 'driver', 'context', 'platform', 'type'];
|
|
1406
1418
|
Object.keys(object).filter(k => k.startsWith('_')).forEach(k => delete object[k]);
|
|
@@ -1413,19 +1425,18 @@ class QueryBuilder {
|
|
|
1413
1425
|
if (this._schema) {
|
|
1414
1426
|
object.schema = this._schema;
|
|
1415
1427
|
}
|
|
1416
|
-
if (!
|
|
1428
|
+
if (!Utils.isEmpty(this._cond)) {
|
|
1417
1429
|
object.where = this._cond;
|
|
1418
1430
|
}
|
|
1419
1431
|
if (this._onConflict?.[0]) {
|
|
1420
1432
|
prefix = 'Upsert';
|
|
1421
1433
|
object.onConflict = this._onConflict[0];
|
|
1422
1434
|
}
|
|
1423
|
-
if (!
|
|
1435
|
+
if (!Utils.isEmpty(this._orderBy)) {
|
|
1424
1436
|
object.orderBy = this._orderBy;
|
|
1425
1437
|
}
|
|
1426
1438
|
const name = this._mainAlias ? `${prefix}QueryBuilder<${this._mainAlias?.entityName}>` : 'QueryBuilder';
|
|
1427
|
-
const ret =
|
|
1439
|
+
const ret = inspect(object, { depth });
|
|
1428
1440
|
return ret === '[Object]' ? `[${name}]` : name + ' ' + ret;
|
|
1429
1441
|
}
|
|
1430
1442
|
}
|
|
1431
|
-
exports.QueryBuilder = QueryBuilder;
|