@mikro-orm/sql 7.0.0-rc.3 → 7.0.1-dev.0
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 +5 -4
- package/AbstractSqlConnection.js +18 -5
- package/AbstractSqlDriver.d.ts +1 -1
- package/AbstractSqlDriver.js +39 -10
- package/AbstractSqlPlatform.d.ts +34 -0
- package/AbstractSqlPlatform.js +47 -3
- package/PivotCollectionPersister.d.ts +2 -11
- package/PivotCollectionPersister.js +59 -59
- package/README.md +5 -4
- package/SqlEntityManager.d.ts +1 -1
- package/dialects/index.d.ts +1 -0
- package/dialects/index.js +1 -0
- package/dialects/mysql/BaseMySqlPlatform.d.ts +6 -0
- package/dialects/mysql/BaseMySqlPlatform.js +17 -0
- package/dialects/mysql/MySqlSchemaHelper.d.ts +1 -1
- package/dialects/mysql/MySqlSchemaHelper.js +6 -6
- package/dialects/oracledb/OracleDialect.d.ts +78 -0
- package/dialects/oracledb/OracleDialect.js +166 -0
- package/dialects/oracledb/OracleNativeQueryBuilder.d.ts +19 -0
- package/dialects/oracledb/OracleNativeQueryBuilder.js +249 -0
- package/dialects/oracledb/index.d.ts +2 -0
- package/dialects/oracledb/index.js +2 -0
- package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +6 -0
- package/dialects/postgresql/BasePostgreSqlPlatform.js +12 -8
- package/dialects/postgresql/PostgreSqlSchemaHelper.js +13 -13
- package/dialects/sqlite/SqlitePlatform.d.ts +1 -0
- package/dialects/sqlite/SqlitePlatform.js +3 -0
- package/dialects/sqlite/SqliteSchemaHelper.js +12 -8
- package/index.d.ts +1 -1
- package/index.js +0 -1
- package/package.json +3 -3
- package/plugin/index.d.ts +1 -14
- package/plugin/index.js +13 -13
- package/plugin/transformer.d.ts +6 -22
- package/plugin/transformer.js +81 -73
- package/query/ArrayCriteriaNode.d.ts +1 -1
- package/query/CriteriaNodeFactory.js +15 -3
- package/query/NativeQueryBuilder.d.ts +3 -3
- package/query/NativeQueryBuilder.js +4 -2
- package/query/ObjectCriteriaNode.js +4 -4
- package/query/QueryBuilder.d.ts +58 -62
- package/query/QueryBuilder.js +377 -370
- package/query/QueryBuilderHelper.d.ts +14 -11
- package/query/QueryBuilderHelper.js +324 -137
- package/query/ScalarCriteriaNode.js +3 -1
- package/query/enums.d.ts +2 -0
- package/query/enums.js +2 -0
- package/schema/DatabaseSchema.d.ts +7 -5
- package/schema/DatabaseSchema.js +50 -33
- package/schema/DatabaseTable.d.ts +8 -6
- package/schema/DatabaseTable.js +84 -60
- package/schema/SchemaComparator.d.ts +1 -3
- package/schema/SchemaComparator.js +22 -20
- package/schema/SchemaHelper.d.ts +2 -13
- package/schema/SchemaHelper.js +2 -1
- package/schema/SqlSchemaGenerator.d.ts +4 -14
- package/schema/SqlSchemaGenerator.js +15 -7
- package/typings.d.ts +4 -1
- package/tsconfig.build.tsbuildinfo +0 -1
package/query/QueryBuilder.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
var _a;
|
|
1
2
|
import { EntityMetadata, helper, inspect, isRaw, LoadStrategy, LockMode, PopulateHint, QueryFlag, QueryHelper, raw, RawQueryFragment, Reference, ReferenceKind, serialize, Utils, ValidationError, } from '@mikro-orm/core';
|
|
2
3
|
import { JoinType, QueryType } from './enums.js';
|
|
3
4
|
import { QueryBuilderHelper } from './QueryBuilderHelper.js';
|
|
@@ -31,61 +32,54 @@ export class QueryBuilder {
|
|
|
31
32
|
connectionType;
|
|
32
33
|
em;
|
|
33
34
|
loggerContext;
|
|
35
|
+
#state = _a.createDefaultState();
|
|
36
|
+
#helper;
|
|
37
|
+
#query;
|
|
38
|
+
/** @internal */
|
|
39
|
+
static createDefaultState() {
|
|
40
|
+
return {
|
|
41
|
+
aliasCounter: 0,
|
|
42
|
+
explicitAlias: false,
|
|
43
|
+
populateHintFinalized: false,
|
|
44
|
+
joins: {},
|
|
45
|
+
cond: {},
|
|
46
|
+
orderBy: [],
|
|
47
|
+
groupBy: [],
|
|
48
|
+
having: {},
|
|
49
|
+
comments: [],
|
|
50
|
+
hintComments: [],
|
|
51
|
+
subQueries: {},
|
|
52
|
+
aliases: {},
|
|
53
|
+
tptAlias: {},
|
|
54
|
+
ctes: [],
|
|
55
|
+
tptJoinsApplied: false,
|
|
56
|
+
autoJoinedPaths: [],
|
|
57
|
+
populate: [],
|
|
58
|
+
populateMap: {},
|
|
59
|
+
flags: new Set([QueryFlag.CONVERT_CUSTOM_TYPES]),
|
|
60
|
+
finalized: false,
|
|
61
|
+
joinedProps: new Map(),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
34
64
|
get mainAlias() {
|
|
35
65
|
this.ensureFromClause();
|
|
36
|
-
return this.
|
|
66
|
+
return this.#state.mainAlias;
|
|
37
67
|
}
|
|
38
68
|
get alias() {
|
|
39
69
|
return this.mainAlias.aliasName;
|
|
40
70
|
}
|
|
41
71
|
get helper() {
|
|
42
72
|
this.ensureFromClause();
|
|
43
|
-
return this
|
|
73
|
+
return this.#helper;
|
|
44
74
|
}
|
|
45
75
|
get type() {
|
|
46
|
-
return this.
|
|
76
|
+
return this.#state.type ?? QueryType.SELECT;
|
|
47
77
|
}
|
|
48
78
|
/** @internal */
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
aliasCounter = 0;
|
|
53
|
-
flags = new Set([QueryFlag.CONVERT_CUSTOM_TYPES]);
|
|
54
|
-
finalized = false;
|
|
55
|
-
populateHintFinalized = false;
|
|
56
|
-
_joins = {};
|
|
57
|
-
_explicitAlias = false;
|
|
58
|
-
_schema;
|
|
59
|
-
_cond = {};
|
|
60
|
-
_data;
|
|
61
|
-
_orderBy = [];
|
|
62
|
-
_groupBy = [];
|
|
63
|
-
_having = {};
|
|
64
|
-
_returning;
|
|
65
|
-
_onConflict;
|
|
66
|
-
_limit;
|
|
67
|
-
_offset;
|
|
68
|
-
_distinctOn;
|
|
69
|
-
_joinedProps = new Map();
|
|
70
|
-
_cache;
|
|
71
|
-
_indexHint;
|
|
72
|
-
_collation;
|
|
73
|
-
_comments = [];
|
|
74
|
-
_hintComments = [];
|
|
75
|
-
flushMode;
|
|
76
|
-
lockMode;
|
|
77
|
-
lockTables;
|
|
78
|
-
subQueries = {};
|
|
79
|
-
_mainAlias;
|
|
80
|
-
_aliases = {};
|
|
81
|
-
_tptAlias = {}; // maps entity className to alias for TPT parent tables
|
|
82
|
-
_helper;
|
|
83
|
-
_query;
|
|
84
|
-
_unionQuery;
|
|
85
|
-
_ctes = [];
|
|
79
|
+
get state() {
|
|
80
|
+
return this.#state;
|
|
81
|
+
}
|
|
86
82
|
platform;
|
|
87
|
-
tptJoinsApplied = false;
|
|
88
|
-
autoJoinedPaths = [];
|
|
89
83
|
/**
|
|
90
84
|
* @internal
|
|
91
85
|
*/
|
|
@@ -98,15 +92,15 @@ export class QueryBuilder {
|
|
|
98
92
|
this.loggerContext = loggerContext;
|
|
99
93
|
this.platform = this.driver.getPlatform();
|
|
100
94
|
if (alias) {
|
|
101
|
-
this.aliasCounter++;
|
|
102
|
-
this.
|
|
95
|
+
this.#state.aliasCounter++;
|
|
96
|
+
this.#state.explicitAlias = true;
|
|
103
97
|
}
|
|
104
98
|
// @ts-expect-error union type does not match the overloaded method signature
|
|
105
99
|
this.from(entityName, alias);
|
|
106
100
|
}
|
|
107
101
|
select(fields, distinct = false) {
|
|
108
102
|
this.ensureNotFinalized();
|
|
109
|
-
this.
|
|
103
|
+
this.#state.fields = Utils.asArray(fields).flatMap(f => {
|
|
110
104
|
if (typeof f !== 'string') {
|
|
111
105
|
// Normalize sql.ref('prop') and sql.ref('prop').as('alias') to string form
|
|
112
106
|
if (isRaw(f) && f.sql === '??' && f.params.length === 1) {
|
|
@@ -117,14 +111,14 @@ export class QueryBuilder {
|
|
|
117
111
|
}
|
|
118
112
|
return f;
|
|
119
113
|
}
|
|
120
|
-
const asMatch =
|
|
114
|
+
const asMatch = FIELD_ALIAS_RE.exec(f);
|
|
121
115
|
if (asMatch) {
|
|
122
116
|
return `${this.resolveNestedPath(asMatch[1].trim())} as ${asMatch[2]}`;
|
|
123
117
|
}
|
|
124
118
|
return this.resolveNestedPath(f);
|
|
125
119
|
});
|
|
126
120
|
if (distinct) {
|
|
127
|
-
this.flags.add(QueryFlag.DISTINCT);
|
|
121
|
+
this.#state.flags.add(QueryFlag.DISTINCT);
|
|
128
122
|
}
|
|
129
123
|
return this.init(QueryType.SELECT);
|
|
130
124
|
}
|
|
@@ -133,10 +127,10 @@ export class QueryBuilder {
|
|
|
133
127
|
*/
|
|
134
128
|
addSelect(fields) {
|
|
135
129
|
this.ensureNotFinalized();
|
|
136
|
-
if (this.
|
|
130
|
+
if (this.#state.type && this.#state.type !== QueryType.SELECT) {
|
|
137
131
|
return this;
|
|
138
132
|
}
|
|
139
|
-
return this.select([...Utils.asArray(this.
|
|
133
|
+
return this.select([...Utils.asArray(this.#state.fields), ...Utils.asArray(fields)]);
|
|
140
134
|
}
|
|
141
135
|
distinct() {
|
|
142
136
|
this.ensureNotFinalized();
|
|
@@ -144,7 +138,7 @@ export class QueryBuilder {
|
|
|
144
138
|
}
|
|
145
139
|
distinctOn(fields) {
|
|
146
140
|
this.ensureNotFinalized();
|
|
147
|
-
this.
|
|
141
|
+
this.#state.distinctOn = Utils.asArray(fields);
|
|
148
142
|
return this;
|
|
149
143
|
}
|
|
150
144
|
/**
|
|
@@ -219,16 +213,16 @@ export class QueryBuilder {
|
|
|
219
213
|
*/
|
|
220
214
|
count(field, distinct = false) {
|
|
221
215
|
if (field) {
|
|
222
|
-
this.
|
|
216
|
+
this.#state.fields = Utils.asArray(field);
|
|
223
217
|
}
|
|
224
218
|
else if (distinct || this.hasToManyJoins()) {
|
|
225
|
-
this.
|
|
219
|
+
this.#state.fields = this.mainAlias.meta.primaryKeys;
|
|
226
220
|
}
|
|
227
221
|
else {
|
|
228
|
-
this.
|
|
222
|
+
this.#state.fields = [raw('*')];
|
|
229
223
|
}
|
|
230
224
|
if (distinct) {
|
|
231
|
-
this.flags.add(QueryFlag.DISTINCT);
|
|
225
|
+
this.#state.flags.add(QueryFlag.DISTINCT);
|
|
232
226
|
}
|
|
233
227
|
return this.init(QueryType.COUNT);
|
|
234
228
|
}
|
|
@@ -262,30 +256,30 @@ export class QueryBuilder {
|
|
|
262
256
|
* ```
|
|
263
257
|
*/
|
|
264
258
|
joinAndSelect(field, alias, cond = {}, type = JoinType.innerJoin, path, fields, schema) {
|
|
265
|
-
if (!this.
|
|
259
|
+
if (!this.#state.type) {
|
|
266
260
|
this.select('*');
|
|
267
261
|
}
|
|
268
262
|
let subquery;
|
|
269
263
|
if (Array.isArray(field)) {
|
|
270
|
-
const rawFragment = field[1] instanceof
|
|
264
|
+
const rawFragment = field[1] instanceof _a ? field[1].toRaw() : field[1];
|
|
271
265
|
subquery = this.platform.formatQuery(rawFragment.sql, rawFragment.params);
|
|
272
266
|
field = field[0];
|
|
273
267
|
}
|
|
274
268
|
const { prop, key } = this.joinReference(field, alias, cond, type, path, schema, subquery);
|
|
275
269
|
const [fromAlias] = this.helper.splitField(field);
|
|
276
270
|
if (subquery) {
|
|
277
|
-
this.
|
|
271
|
+
this.#state.joins[key].subquery = subquery;
|
|
278
272
|
}
|
|
279
|
-
const populate = this.
|
|
273
|
+
const populate = this.#state.joinedProps.get(fromAlias);
|
|
280
274
|
const item = { field: prop.name, strategy: LoadStrategy.JOINED, children: [] };
|
|
281
275
|
if (populate) {
|
|
282
276
|
populate.children.push(item);
|
|
283
277
|
}
|
|
284
278
|
else {
|
|
285
279
|
// root entity
|
|
286
|
-
this.
|
|
280
|
+
this.#state.populate.push(item);
|
|
287
281
|
}
|
|
288
|
-
this.
|
|
282
|
+
this.#state.joinedProps.set(alias, item);
|
|
289
283
|
this.addSelect(this.getFieldsForJoinedLoad(prop, alias, fields));
|
|
290
284
|
return this;
|
|
291
285
|
}
|
|
@@ -306,12 +300,12 @@ export class QueryBuilder {
|
|
|
306
300
|
getFieldsForJoinedLoad(prop, alias, explicitFields) {
|
|
307
301
|
const fields = [];
|
|
308
302
|
const populate = [];
|
|
309
|
-
const joinKey = Object.keys(this.
|
|
303
|
+
const joinKey = Object.keys(this.#state.joins).find(join => join.endsWith(`#${alias}`));
|
|
310
304
|
const targetMeta = prop.targetMeta;
|
|
311
|
-
const schema = this.
|
|
305
|
+
const schema = this.#state.schema ?? (targetMeta.schema !== '*' ? targetMeta.schema : undefined);
|
|
312
306
|
if (joinKey) {
|
|
313
|
-
const path = this.
|
|
314
|
-
let children = this.
|
|
307
|
+
const path = this.#state.joins[joinKey].path.split('.').slice(1);
|
|
308
|
+
let children = this.#state.populate;
|
|
315
309
|
for (let i = 0; i < path.length; i++) {
|
|
316
310
|
const child = children.filter(hint => {
|
|
317
311
|
const [propName] = hint.field.split(':', 2);
|
|
@@ -361,13 +355,13 @@ export class QueryBuilder {
|
|
|
361
355
|
* @internal
|
|
362
356
|
*/
|
|
363
357
|
scheduleFilterCheck(path) {
|
|
364
|
-
this.autoJoinedPaths.push(path);
|
|
358
|
+
this.#state.autoJoinedPaths.push(path);
|
|
365
359
|
}
|
|
366
360
|
/**
|
|
367
361
|
* @internal
|
|
368
362
|
*/
|
|
369
363
|
async applyJoinedFilters(em, filterOptions) {
|
|
370
|
-
for (const path of this.autoJoinedPaths) {
|
|
364
|
+
for (const path of this.#state.autoJoinedPaths) {
|
|
371
365
|
const join = this.getJoinForPath(path);
|
|
372
366
|
if (join.type === JoinType.pivotJoin) {
|
|
373
367
|
continue;
|
|
@@ -403,10 +397,10 @@ export class QueryBuilder {
|
|
|
403
397
|
withSubQuery(subQuery, alias) {
|
|
404
398
|
this.ensureNotFinalized();
|
|
405
399
|
if (isRaw(subQuery)) {
|
|
406
|
-
this.subQueries[alias] = this.platform.formatQuery(subQuery.sql, subQuery.params);
|
|
400
|
+
this.#state.subQueries[alias] = this.platform.formatQuery(subQuery.sql, subQuery.params);
|
|
407
401
|
}
|
|
408
402
|
else {
|
|
409
|
-
this.subQueries[alias] = subQuery.toString();
|
|
403
|
+
this.#state.subQueries[alias] = subQuery.toString();
|
|
410
404
|
}
|
|
411
405
|
return this;
|
|
412
406
|
}
|
|
@@ -430,13 +424,13 @@ export class QueryBuilder {
|
|
|
430
424
|
platform: this.platform,
|
|
431
425
|
aliasMap: this.getAliasMap(),
|
|
432
426
|
aliased: [QueryType.SELECT, QueryType.COUNT].includes(this.type),
|
|
433
|
-
convertCustomTypes: this.flags.has(QueryFlag.CONVERT_CUSTOM_TYPES),
|
|
427
|
+
convertCustomTypes: this.#state.flags.has(QueryFlag.CONVERT_CUSTOM_TYPES),
|
|
434
428
|
});
|
|
435
429
|
}
|
|
436
430
|
const op = operator || params;
|
|
437
|
-
const topLevel = !op || !(Utils.hasObjectKeys(this.
|
|
431
|
+
const topLevel = !op || !(Utils.hasObjectKeys(this.#state.cond) || RawQueryFragment.hasObjectFragments(this.#state.cond));
|
|
438
432
|
const criteriaNode = CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, processedCond);
|
|
439
|
-
const ignoreBranching = this.
|
|
433
|
+
const ignoreBranching = this.#state.resolvedPopulateWhere === 'infer';
|
|
440
434
|
if ([QueryType.UPDATE, QueryType.DELETE].includes(this.type) &&
|
|
441
435
|
criteriaNode.willAutoJoin(this, undefined, { ignoreBranching })) {
|
|
442
436
|
// use sub-query to support joining
|
|
@@ -444,18 +438,18 @@ export class QueryBuilder {
|
|
|
444
438
|
this.select(this.mainAlias.meta.primaryKeys, true);
|
|
445
439
|
}
|
|
446
440
|
if (topLevel) {
|
|
447
|
-
this.
|
|
441
|
+
this.#state.cond = criteriaNode.process(this, { ignoreBranching });
|
|
448
442
|
}
|
|
449
|
-
else if (Array.isArray(this.
|
|
450
|
-
this.
|
|
443
|
+
else if (Array.isArray(this.#state.cond[op])) {
|
|
444
|
+
this.#state.cond[op].push(criteriaNode.process(this, { ignoreBranching }));
|
|
451
445
|
}
|
|
452
446
|
else {
|
|
453
|
-
const cond1 = [this.
|
|
454
|
-
this.
|
|
447
|
+
const cond1 = [this.#state.cond, criteriaNode.process(this, { ignoreBranching })];
|
|
448
|
+
this.#state.cond = { [op]: cond1 };
|
|
455
449
|
}
|
|
456
|
-
if (this.
|
|
457
|
-
this.
|
|
458
|
-
this.
|
|
450
|
+
if (this.#state.onConflict) {
|
|
451
|
+
this.#state.onConflict[this.#state.onConflict.length - 1].where = this.helper.processOnConflictCondition(this.#state.cond, this.#state.schema);
|
|
452
|
+
this.#state.cond = {};
|
|
459
453
|
}
|
|
460
454
|
return this;
|
|
461
455
|
}
|
|
@@ -474,7 +468,7 @@ export class QueryBuilder {
|
|
|
474
468
|
processOrderBy(orderBy, reset = true) {
|
|
475
469
|
this.ensureNotFinalized();
|
|
476
470
|
if (reset) {
|
|
477
|
-
this.
|
|
471
|
+
this.#state.orderBy = [];
|
|
478
472
|
}
|
|
479
473
|
const selectAliases = this.getSelectAliases();
|
|
480
474
|
Utils.asArray(orderBy).forEach(orig => {
|
|
@@ -499,7 +493,7 @@ export class QueryBuilder {
|
|
|
499
493
|
convertCustomTypes: false,
|
|
500
494
|
type: 'orderBy',
|
|
501
495
|
});
|
|
502
|
-
this.
|
|
496
|
+
this.#state.orderBy.push(CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, processed).process(this, {
|
|
503
497
|
matchPopulateJoins: true,
|
|
504
498
|
type: 'orderBy',
|
|
505
499
|
}));
|
|
@@ -509,9 +503,9 @@ export class QueryBuilder {
|
|
|
509
503
|
/** Collect custom aliases from select fields (stored as 'resolved as alias' strings by select()). */
|
|
510
504
|
getSelectAliases() {
|
|
511
505
|
const aliases = new Set();
|
|
512
|
-
for (const field of this.
|
|
506
|
+
for (const field of this.#state.fields ?? []) {
|
|
513
507
|
if (typeof field === 'string') {
|
|
514
|
-
const m =
|
|
508
|
+
const m = FIELD_ALIAS_RE.exec(field);
|
|
515
509
|
if (m) {
|
|
516
510
|
aliases.add(m[2]);
|
|
517
511
|
}
|
|
@@ -521,7 +515,7 @@ export class QueryBuilder {
|
|
|
521
515
|
}
|
|
522
516
|
groupBy(fields) {
|
|
523
517
|
this.ensureNotFinalized();
|
|
524
|
-
this.
|
|
518
|
+
this.#state.groupBy = Utils.asArray(fields).flatMap(f => {
|
|
525
519
|
if (typeof f !== 'string') {
|
|
526
520
|
// Normalize sql.ref('prop') to string for proper formula resolution
|
|
527
521
|
if (isRaw(f) && f.sql === '??' && f.params.length === 1) {
|
|
@@ -549,12 +543,12 @@ export class QueryBuilder {
|
|
|
549
543
|
cond = { [raw(`(${cond})`, params)]: [] };
|
|
550
544
|
}
|
|
551
545
|
const processed = CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, cond, undefined, undefined, false).process(this, { type: 'having' });
|
|
552
|
-
if (!this.
|
|
553
|
-
this.
|
|
546
|
+
if (!this.#state.having || !operator) {
|
|
547
|
+
this.#state.having = processed;
|
|
554
548
|
}
|
|
555
549
|
else {
|
|
556
|
-
const cond1 = [this.
|
|
557
|
-
this.
|
|
550
|
+
const cond1 = [this.#state.having, processed];
|
|
551
|
+
this.#state.having = { [operator]: cond1 };
|
|
558
552
|
}
|
|
559
553
|
return this;
|
|
560
554
|
}
|
|
@@ -567,8 +561,8 @@ export class QueryBuilder {
|
|
|
567
561
|
onConflict(fields = []) {
|
|
568
562
|
const meta = this.mainAlias.meta;
|
|
569
563
|
this.ensureNotFinalized();
|
|
570
|
-
this.
|
|
571
|
-
this.
|
|
564
|
+
this.#state.onConflict ??= [];
|
|
565
|
+
this.#state.onConflict.push({
|
|
572
566
|
fields: isRaw(fields)
|
|
573
567
|
? fields
|
|
574
568
|
: Utils.asArray(fields).flatMap(f => {
|
|
@@ -580,24 +574,24 @@ export class QueryBuilder {
|
|
|
580
574
|
return this;
|
|
581
575
|
}
|
|
582
576
|
ignore() {
|
|
583
|
-
if (!this.
|
|
577
|
+
if (!this.#state.onConflict) {
|
|
584
578
|
throw new Error('You need to call `qb.onConflict()` first to use `qb.ignore()`');
|
|
585
579
|
}
|
|
586
|
-
this.
|
|
580
|
+
this.#state.onConflict[this.#state.onConflict.length - 1].ignore = true;
|
|
587
581
|
return this;
|
|
588
582
|
}
|
|
589
583
|
merge(data) {
|
|
590
|
-
if (!this.
|
|
584
|
+
if (!this.#state.onConflict) {
|
|
591
585
|
throw new Error('You need to call `qb.onConflict()` first to use `qb.merge()`');
|
|
592
586
|
}
|
|
593
587
|
if (Array.isArray(data) && data.length === 0) {
|
|
594
588
|
return this.ignore();
|
|
595
589
|
}
|
|
596
|
-
this.
|
|
590
|
+
this.#state.onConflict[this.#state.onConflict.length - 1].merge = data;
|
|
597
591
|
return this;
|
|
598
592
|
}
|
|
599
593
|
returning(fields) {
|
|
600
|
-
this.
|
|
594
|
+
this.#state.returning = Utils.asArray(fields);
|
|
601
595
|
return this;
|
|
602
596
|
}
|
|
603
597
|
/**
|
|
@@ -605,9 +599,9 @@ export class QueryBuilder {
|
|
|
605
599
|
*/
|
|
606
600
|
populate(populate, populateWhere, populateFilter) {
|
|
607
601
|
this.ensureNotFinalized();
|
|
608
|
-
this.
|
|
609
|
-
this.
|
|
610
|
-
this.
|
|
602
|
+
this.#state.populate = populate;
|
|
603
|
+
this.#state.populateWhere = populateWhere;
|
|
604
|
+
this.#state.populateFilter = populateFilter;
|
|
611
605
|
return this;
|
|
612
606
|
}
|
|
613
607
|
/**
|
|
@@ -621,7 +615,7 @@ export class QueryBuilder {
|
|
|
621
615
|
*/
|
|
622
616
|
limit(limit, offset = 0) {
|
|
623
617
|
this.ensureNotFinalized();
|
|
624
|
-
this.
|
|
618
|
+
this.#state.limit = limit;
|
|
625
619
|
if (offset) {
|
|
626
620
|
this.offset(offset);
|
|
627
621
|
}
|
|
@@ -637,12 +631,12 @@ export class QueryBuilder {
|
|
|
637
631
|
*/
|
|
638
632
|
offset(offset) {
|
|
639
633
|
this.ensureNotFinalized();
|
|
640
|
-
this.
|
|
634
|
+
this.#state.offset = offset;
|
|
641
635
|
return this;
|
|
642
636
|
}
|
|
643
637
|
withSchema(schema) {
|
|
644
638
|
this.ensureNotFinalized();
|
|
645
|
-
this.
|
|
639
|
+
this.#state.schema = schema;
|
|
646
640
|
return this;
|
|
647
641
|
}
|
|
648
642
|
setLockMode(mode, tables) {
|
|
@@ -650,31 +644,31 @@ export class QueryBuilder {
|
|
|
650
644
|
if (mode != null && ![LockMode.OPTIMISTIC, LockMode.NONE].includes(mode) && !this.context) {
|
|
651
645
|
throw ValidationError.transactionRequired();
|
|
652
646
|
}
|
|
653
|
-
this.lockMode = mode;
|
|
654
|
-
this.lockTables = tables;
|
|
647
|
+
this.#state.lockMode = mode;
|
|
648
|
+
this.#state.lockTables = tables;
|
|
655
649
|
return this;
|
|
656
650
|
}
|
|
657
651
|
setFlushMode(flushMode) {
|
|
658
652
|
this.ensureNotFinalized();
|
|
659
|
-
this.flushMode = flushMode;
|
|
653
|
+
this.#state.flushMode = flushMode;
|
|
660
654
|
return this;
|
|
661
655
|
}
|
|
662
656
|
setFlag(flag) {
|
|
663
657
|
this.ensureNotFinalized();
|
|
664
|
-
this.flags.add(flag);
|
|
658
|
+
this.#state.flags.add(flag);
|
|
665
659
|
return this;
|
|
666
660
|
}
|
|
667
661
|
unsetFlag(flag) {
|
|
668
662
|
this.ensureNotFinalized();
|
|
669
|
-
this.flags.delete(flag);
|
|
663
|
+
this.#state.flags.delete(flag);
|
|
670
664
|
return this;
|
|
671
665
|
}
|
|
672
666
|
hasFlag(flag) {
|
|
673
|
-
return this.flags.has(flag);
|
|
667
|
+
return this.#state.flags.has(flag);
|
|
674
668
|
}
|
|
675
669
|
cache(config = true) {
|
|
676
670
|
this.ensureNotFinalized();
|
|
677
|
-
this.
|
|
671
|
+
this.#state.cache = config;
|
|
678
672
|
return this;
|
|
679
673
|
}
|
|
680
674
|
/**
|
|
@@ -682,7 +676,7 @@ export class QueryBuilder {
|
|
|
682
676
|
*/
|
|
683
677
|
indexHint(sql) {
|
|
684
678
|
this.ensureNotFinalized();
|
|
685
|
-
this.
|
|
679
|
+
this.#state.indexHint = sql;
|
|
686
680
|
return this;
|
|
687
681
|
}
|
|
688
682
|
/**
|
|
@@ -690,7 +684,7 @@ export class QueryBuilder {
|
|
|
690
684
|
*/
|
|
691
685
|
collation(collation) {
|
|
692
686
|
this.ensureNotFinalized();
|
|
693
|
-
this.
|
|
687
|
+
this.#state.collation = collation;
|
|
694
688
|
return this;
|
|
695
689
|
}
|
|
696
690
|
/**
|
|
@@ -698,7 +692,7 @@ export class QueryBuilder {
|
|
|
698
692
|
*/
|
|
699
693
|
comment(comment) {
|
|
700
694
|
this.ensureNotFinalized();
|
|
701
|
-
this.
|
|
695
|
+
this.#state.comments.push(...Utils.asArray(comment));
|
|
702
696
|
return this;
|
|
703
697
|
}
|
|
704
698
|
/**
|
|
@@ -708,43 +702,43 @@ export class QueryBuilder {
|
|
|
708
702
|
*/
|
|
709
703
|
hintComment(comment) {
|
|
710
704
|
this.ensureNotFinalized();
|
|
711
|
-
this.
|
|
705
|
+
this.#state.hintComments.push(...Utils.asArray(comment));
|
|
712
706
|
return this;
|
|
713
707
|
}
|
|
714
708
|
from(target, aliasName) {
|
|
715
709
|
this.ensureNotFinalized();
|
|
716
|
-
if (target instanceof
|
|
710
|
+
if (target instanceof _a) {
|
|
717
711
|
this.fromSubQuery(target, aliasName);
|
|
718
712
|
}
|
|
719
713
|
else if (typeof target === 'string' && !this.metadata.find(target)) {
|
|
720
714
|
this.fromRawTable(target, aliasName);
|
|
721
715
|
}
|
|
722
716
|
else {
|
|
723
|
-
if (aliasName && this.
|
|
724
|
-
throw new Error(`Cannot override the alias to '${aliasName}' since a query already contains references to '${this.
|
|
717
|
+
if (aliasName && this.#state.mainAlias && Utils.className(target) !== this.#state.mainAlias.aliasName) {
|
|
718
|
+
throw new Error(`Cannot override the alias to '${aliasName}' since a query already contains references to '${this.#state.mainAlias.aliasName}'`);
|
|
725
719
|
}
|
|
726
720
|
this.fromEntityName(target, aliasName);
|
|
727
721
|
}
|
|
728
722
|
return this;
|
|
729
723
|
}
|
|
730
724
|
getNativeQuery(processVirtualEntity = true) {
|
|
731
|
-
if (this.
|
|
732
|
-
if (!this
|
|
733
|
-
this
|
|
725
|
+
if (this.#state.unionQuery) {
|
|
726
|
+
if (!this.#query?.qb) {
|
|
727
|
+
this.#query = {};
|
|
734
728
|
const nqb = this.platform.createNativeQueryBuilder();
|
|
735
729
|
nqb.select('*');
|
|
736
|
-
nqb.from(raw(`(${this.
|
|
737
|
-
this.
|
|
730
|
+
nqb.from(raw(`(${this.#state.unionQuery.sql})`, this.#state.unionQuery.params));
|
|
731
|
+
this.#query.qb = nqb;
|
|
738
732
|
}
|
|
739
|
-
return this.
|
|
733
|
+
return this.#query.qb;
|
|
740
734
|
}
|
|
741
|
-
if (this
|
|
742
|
-
return this.
|
|
735
|
+
if (this.#query?.qb) {
|
|
736
|
+
return this.#query.qb;
|
|
743
737
|
}
|
|
744
|
-
this
|
|
738
|
+
this.#query = {};
|
|
745
739
|
this.finalize();
|
|
746
740
|
const qb = this.getQueryBase(processVirtualEntity);
|
|
747
|
-
for (const cte of this.
|
|
741
|
+
for (const cte of this.#state.ctes) {
|
|
748
742
|
const query = cte.query;
|
|
749
743
|
const opts = { columns: cte.columns, materialized: cte.materialized };
|
|
750
744
|
if (cte.recursive) {
|
|
@@ -756,27 +750,62 @@ export class QueryBuilder {
|
|
|
756
750
|
}
|
|
757
751
|
const schema = this.getSchema(this.mainAlias);
|
|
758
752
|
const isNotEmptyObject = (obj) => Utils.hasObjectKeys(obj) || RawQueryFragment.hasObjectFragments(obj);
|
|
759
|
-
Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type, this.
|
|
760
|
-
Utils.runIfNotEmpty(() => qb.groupBy(this.prepareFields(this.
|
|
761
|
-
Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type, this.
|
|
753
|
+
Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type, this.#state.cond, qb), this.#state.cond && !this.#state.onConflict);
|
|
754
|
+
Utils.runIfNotEmpty(() => qb.groupBy(this.prepareFields(this.#state.groupBy, 'groupBy', schema)), isNotEmptyObject(this.#state.groupBy));
|
|
755
|
+
Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type, this.#state.having, qb, undefined, 'having'), isNotEmptyObject(this.#state.having));
|
|
762
756
|
Utils.runIfNotEmpty(() => {
|
|
763
|
-
const queryOrder = this.helper.getQueryOrder(this.type, this.
|
|
757
|
+
const queryOrder = this.helper.getQueryOrder(this.type, this.#state.orderBy, this.#state.populateMap, this.#state.collation);
|
|
764
758
|
if (queryOrder.length > 0) {
|
|
765
759
|
const sql = Utils.unique(queryOrder).join(', ');
|
|
766
760
|
qb.orderBy(sql);
|
|
767
761
|
return;
|
|
768
762
|
}
|
|
769
|
-
}, isNotEmptyObject(this.
|
|
770
|
-
Utils.runIfNotEmpty(() => qb.limit(this.
|
|
771
|
-
Utils.runIfNotEmpty(() => qb.offset(this.
|
|
772
|
-
Utils.runIfNotEmpty(() => qb.comment(this.
|
|
773
|
-
Utils.runIfNotEmpty(() => qb.hintComment(this.
|
|
774
|
-
Utils.runIfNotEmpty(() => this.helper.appendOnConflictClause(QueryType.UPSERT, this.
|
|
775
|
-
if (this.lockMode) {
|
|
776
|
-
this.helper.getLockSQL(qb, this.lockMode, this.lockTables, this.
|
|
777
|
-
}
|
|
778
|
-
this.
|
|
779
|
-
return (this.
|
|
763
|
+
}, isNotEmptyObject(this.#state.orderBy));
|
|
764
|
+
Utils.runIfNotEmpty(() => qb.limit(this.#state.limit), this.#state.limit != null);
|
|
765
|
+
Utils.runIfNotEmpty(() => qb.offset(this.#state.offset), this.#state.offset);
|
|
766
|
+
Utils.runIfNotEmpty(() => qb.comment(this.#state.comments), this.#state.comments);
|
|
767
|
+
Utils.runIfNotEmpty(() => qb.hintComment(this.#state.hintComments), this.#state.hintComments);
|
|
768
|
+
Utils.runIfNotEmpty(() => this.helper.appendOnConflictClause(QueryType.UPSERT, this.#state.onConflict, qb), this.#state.onConflict);
|
|
769
|
+
if (this.#state.lockMode) {
|
|
770
|
+
this.helper.getLockSQL(qb, this.#state.lockMode, this.#state.lockTables, this.#state.joins);
|
|
771
|
+
}
|
|
772
|
+
this.processReturningStatement(qb, this.mainAlias.meta, this.#state.data, this.#state.returning);
|
|
773
|
+
return (this.#query.qb = qb);
|
|
774
|
+
}
|
|
775
|
+
processReturningStatement(qb, meta, data, returning) {
|
|
776
|
+
const usesReturningStatement = this.platform.usesReturningStatement() || this.platform.usesOutputStatement();
|
|
777
|
+
if (!meta || !data || !usesReturningStatement) {
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
// always respect explicit returning hint
|
|
781
|
+
if (returning && returning.length > 0) {
|
|
782
|
+
qb.returning(returning.map(field => this.helper.mapper(field, this.type)));
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
if (this.type === QueryType.INSERT) {
|
|
786
|
+
const returningProps = meta.hydrateProps
|
|
787
|
+
.filter(prop => prop.returning || (prop.persist !== false && ((prop.primary && prop.autoincrement) || prop.defaultRaw)))
|
|
788
|
+
.filter(prop => !(prop.name in data));
|
|
789
|
+
if (returningProps.length > 0) {
|
|
790
|
+
qb.returning(Utils.flatten(returningProps.map(prop => prop.fieldNames)));
|
|
791
|
+
}
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
if (this.type === QueryType.UPDATE) {
|
|
795
|
+
const returningProps = meta.hydrateProps.filter(prop => prop.fieldNames && isRaw(data[prop.fieldNames[0]]));
|
|
796
|
+
if (returningProps.length > 0) {
|
|
797
|
+
qb.returning(returningProps.flatMap((prop) => {
|
|
798
|
+
if (prop.hasConvertToJSValueSQL) {
|
|
799
|
+
const aliased = this.platform.quoteIdentifier(prop.fieldNames[0]);
|
|
800
|
+
const sql = prop.customType.convertToJSValueSQL(aliased, this.platform) +
|
|
801
|
+
' as ' +
|
|
802
|
+
this.platform.quoteIdentifier(prop.fieldNames[0]);
|
|
803
|
+
return [raw(sql)];
|
|
804
|
+
}
|
|
805
|
+
return prop.fieldNames;
|
|
806
|
+
}));
|
|
807
|
+
}
|
|
808
|
+
}
|
|
780
809
|
}
|
|
781
810
|
/**
|
|
782
811
|
* Returns the query with parameters as wildcards.
|
|
@@ -792,16 +821,16 @@ export class QueryBuilder {
|
|
|
792
821
|
return raw(sql, params);
|
|
793
822
|
}
|
|
794
823
|
toQuery() {
|
|
795
|
-
if (this.
|
|
796
|
-
return this.
|
|
824
|
+
if (this.#state.unionQuery) {
|
|
825
|
+
return this.#state.unionQuery;
|
|
797
826
|
}
|
|
798
|
-
if (this
|
|
799
|
-
return { sql: this.
|
|
827
|
+
if (this.#query?.sql) {
|
|
828
|
+
return { sql: this.#query.sql, params: this.#query.params };
|
|
800
829
|
}
|
|
801
830
|
const query = this.getNativeQuery().compile();
|
|
802
|
-
this.
|
|
803
|
-
this.
|
|
804
|
-
return { sql: this.
|
|
831
|
+
this.#query.sql = query.sql;
|
|
832
|
+
this.#query.params = query.params;
|
|
833
|
+
return { sql: this.#query.sql, params: this.#query.params };
|
|
805
834
|
}
|
|
806
835
|
/**
|
|
807
836
|
* Returns the list of all parameters for this query.
|
|
@@ -833,7 +862,7 @@ export class QueryBuilder {
|
|
|
833
862
|
* @internal
|
|
834
863
|
*/
|
|
835
864
|
getJoinForPath(path, options) {
|
|
836
|
-
const joins = Object.values(this.
|
|
865
|
+
const joins = Object.values(this.#state.joins);
|
|
837
866
|
if (joins.length === 0) {
|
|
838
867
|
return undefined;
|
|
839
868
|
}
|
|
@@ -865,7 +894,7 @@ export class QueryBuilder {
|
|
|
865
894
|
*/
|
|
866
895
|
getNextAlias(entityName = 'e') {
|
|
867
896
|
entityName = Utils.className(entityName);
|
|
868
|
-
return this.driver.config.getNamingStrategy().aliasName(entityName, this.aliasCounter++);
|
|
897
|
+
return this.driver.config.getNamingStrategy().aliasName(entityName, this.#state.aliasCounter++);
|
|
869
898
|
}
|
|
870
899
|
/**
|
|
871
900
|
* Registers a join for a specific polymorphic target type.
|
|
@@ -878,15 +907,15 @@ export class QueryBuilder {
|
|
|
878
907
|
const referencedColumnNames = targetMeta.getPrimaryProps().flatMap(pk => pk.fieldNames);
|
|
879
908
|
const targetProp = { ...prop, targetMeta, referencedColumnNames };
|
|
880
909
|
const aliasedName = `${ownerAlias}.${prop.name}[${targetMeta.className}]#${alias}`;
|
|
881
|
-
this.
|
|
882
|
-
this.
|
|
910
|
+
this.#state.joins[aliasedName] = this.helper.joinManyToOneReference(targetProp, ownerAlias, alias, type, {}, schema);
|
|
911
|
+
this.#state.joins[aliasedName].path = path;
|
|
883
912
|
this.createAlias(targetMeta.class, alias);
|
|
884
913
|
}
|
|
885
914
|
/**
|
|
886
915
|
* @internal
|
|
887
916
|
*/
|
|
888
917
|
getAliasMap() {
|
|
889
|
-
return Object.fromEntries(Object.entries(this.
|
|
918
|
+
return Object.fromEntries(Object.entries(this.#state.aliases).map(([key, value]) => [key, value.entityName]));
|
|
890
919
|
}
|
|
891
920
|
/**
|
|
892
921
|
* Executes this QB and returns the raw results, mapped to the property names (unless disabled via last parameter).
|
|
@@ -901,11 +930,11 @@ export class QueryBuilder {
|
|
|
901
930
|
if (!this.connectionType && (isRunType || this.context)) {
|
|
902
931
|
this.connectionType = 'write';
|
|
903
932
|
}
|
|
904
|
-
if (!this.finalized && method === 'get' && this.type === QueryType.SELECT) {
|
|
933
|
+
if (!this.#state.finalized && method === 'get' && this.type === QueryType.SELECT) {
|
|
905
934
|
this.limit(1);
|
|
906
935
|
}
|
|
907
936
|
const query = this.toQuery();
|
|
908
|
-
const cached = await this.em?.tryCache(this.mainAlias.entityName, this.
|
|
937
|
+
const cached = await this.em?.tryCache(this.mainAlias.entityName, this.#state.cache, [
|
|
909
938
|
'qb.execute',
|
|
910
939
|
query.sql,
|
|
911
940
|
query.params,
|
|
@@ -918,17 +947,17 @@ export class QueryBuilder {
|
|
|
918
947
|
const res = await this.getConnection().execute(query.sql, query.params, method, this.context, loggerContext);
|
|
919
948
|
const meta = this.mainAlias.meta;
|
|
920
949
|
if (!options.mapResults || !meta) {
|
|
921
|
-
await this.em?.storeCache(this.
|
|
950
|
+
await this.em?.storeCache(this.#state.cache, cached, res);
|
|
922
951
|
return res;
|
|
923
952
|
}
|
|
924
953
|
if (method === 'run') {
|
|
925
954
|
return res;
|
|
926
955
|
}
|
|
927
|
-
const joinedProps = this.driver.joinedProps(meta, this.
|
|
956
|
+
const joinedProps = this.driver.joinedProps(meta, this.#state.populate);
|
|
928
957
|
let mapped;
|
|
929
958
|
if (Array.isArray(res)) {
|
|
930
959
|
const map = {};
|
|
931
|
-
mapped = res.map(r => this.driver.mapResult(r, meta, this.
|
|
960
|
+
mapped = res.map(r => this.driver.mapResult(r, meta, this.#state.populate, this, map));
|
|
932
961
|
if (options.mergeResults && joinedProps.length > 0) {
|
|
933
962
|
mapped = this.driver.mergeJoinedResult(mapped, this.mainAlias.meta, joinedProps);
|
|
934
963
|
}
|
|
@@ -937,10 +966,10 @@ export class QueryBuilder {
|
|
|
937
966
|
mapped = [this.driver.mapResult(res, meta, joinedProps, this)];
|
|
938
967
|
}
|
|
939
968
|
if (method === 'get') {
|
|
940
|
-
await this.em?.storeCache(this.
|
|
969
|
+
await this.em?.storeCache(this.#state.cache, cached, mapped[0]);
|
|
941
970
|
return mapped[0];
|
|
942
971
|
}
|
|
943
|
-
await this.em?.storeCache(this.
|
|
972
|
+
await this.em?.storeCache(this.#state.cache, cached, mapped);
|
|
944
973
|
return mapped;
|
|
945
974
|
}
|
|
946
975
|
getConnection() {
|
|
@@ -976,13 +1005,13 @@ export class QueryBuilder {
|
|
|
976
1005
|
yield* res;
|
|
977
1006
|
return;
|
|
978
1007
|
}
|
|
979
|
-
const joinedProps = this.driver.joinedProps(meta, this.
|
|
1008
|
+
const joinedProps = this.driver.joinedProps(meta, this.#state.populate);
|
|
980
1009
|
const stack = [];
|
|
981
1010
|
const hash = (data) => {
|
|
982
1011
|
return Utils.getPrimaryKeyHash(meta.primaryKeys.map(pk => data[pk]));
|
|
983
1012
|
};
|
|
984
1013
|
for await (const row of res) {
|
|
985
|
-
const mapped = this.driver.mapResult(row, meta, this.
|
|
1014
|
+
const mapped = this.driver.mapResult(row, meta, this.#state.populate, this);
|
|
986
1015
|
if (!options.mergeResults || joinedProps.length === 0) {
|
|
987
1016
|
yield this.mapResult(mapped, options.mapResults);
|
|
988
1017
|
continue;
|
|
@@ -1011,7 +1040,7 @@ export class QueryBuilder {
|
|
|
1011
1040
|
* Executes the query, returning array of results mapped to entity instances.
|
|
1012
1041
|
*/
|
|
1013
1042
|
async getResultList(limit) {
|
|
1014
|
-
await this.em.tryFlush(this.mainAlias.entityName, { flushMode: this.flushMode });
|
|
1043
|
+
await this.em.tryFlush(this.mainAlias.entityName, { flushMode: this.#state.flushMode });
|
|
1015
1044
|
const res = await this.execute('all', true);
|
|
1016
1045
|
return this.mapResults(res, limit);
|
|
1017
1046
|
}
|
|
@@ -1033,15 +1062,15 @@ export class QueryBuilder {
|
|
|
1033
1062
|
if (!map) {
|
|
1034
1063
|
return row;
|
|
1035
1064
|
}
|
|
1036
|
-
const entity = this.em.map(this.mainAlias.entityName, row, { schema: this.
|
|
1037
|
-
this.propagatePopulateHint(entity, this.
|
|
1065
|
+
const entity = this.em.map(this.mainAlias.entityName, row, { schema: this.#state.schema });
|
|
1066
|
+
this.propagatePopulateHint(entity, this.#state.populate);
|
|
1038
1067
|
return entity;
|
|
1039
1068
|
}
|
|
1040
1069
|
mapResults(res, limit) {
|
|
1041
1070
|
const entities = [];
|
|
1042
1071
|
for (const row of res) {
|
|
1043
1072
|
const entity = this.mapResult(row);
|
|
1044
|
-
this.propagatePopulateHint(entity, this.
|
|
1073
|
+
this.propagatePopulateHint(entity, this.#state.populate);
|
|
1045
1074
|
entities.push(entity);
|
|
1046
1075
|
if (limit != null && --limit === 0) {
|
|
1047
1076
|
break;
|
|
@@ -1053,7 +1082,7 @@ export class QueryBuilder {
|
|
|
1053
1082
|
* Executes the query, returning the first result or null
|
|
1054
1083
|
*/
|
|
1055
1084
|
async getSingleResult() {
|
|
1056
|
-
if (!this.finalized) {
|
|
1085
|
+
if (!this.#state.finalized) {
|
|
1057
1086
|
this.limit(1);
|
|
1058
1087
|
}
|
|
1059
1088
|
const [res] = await this.getResultList(1);
|
|
@@ -1065,7 +1094,7 @@ export class QueryBuilder {
|
|
|
1065
1094
|
res = await this.execute('get', false);
|
|
1066
1095
|
}
|
|
1067
1096
|
else {
|
|
1068
|
-
const qb = (this.
|
|
1097
|
+
const qb = (this.#state.type === undefined ? this : this.clone());
|
|
1069
1098
|
qb.processPopulateHint(); // needs to happen sooner so `qb.hasToManyJoins()` reports correctly
|
|
1070
1099
|
qb.count(field, distinct ?? qb.hasToManyJoins())
|
|
1071
1100
|
.limit(undefined)
|
|
@@ -1138,12 +1167,12 @@ export class QueryBuilder {
|
|
|
1138
1167
|
const parts = [];
|
|
1139
1168
|
const params = [];
|
|
1140
1169
|
for (const qb of all) {
|
|
1141
|
-
const compiled = qb instanceof
|
|
1170
|
+
const compiled = qb instanceof _a ? qb.toQuery() : qb.compile();
|
|
1142
1171
|
parts.push(`(${compiled.sql})`);
|
|
1143
1172
|
params.push(...compiled.params);
|
|
1144
1173
|
}
|
|
1145
1174
|
const result = this.clone(true);
|
|
1146
|
-
result.
|
|
1175
|
+
result.#state.unionQuery = { sql: parts.join(` ${separator} `), params };
|
|
1147
1176
|
return result;
|
|
1148
1177
|
}
|
|
1149
1178
|
with(name, query, options) {
|
|
@@ -1154,12 +1183,12 @@ export class QueryBuilder {
|
|
|
1154
1183
|
}
|
|
1155
1184
|
addCte(name, query, options, recursive) {
|
|
1156
1185
|
this.ensureNotFinalized();
|
|
1157
|
-
if (this.
|
|
1186
|
+
if (this.#state.ctes.some(cte => cte.name === name)) {
|
|
1158
1187
|
throw new Error(`CTE with name '${name}' already exists`);
|
|
1159
1188
|
}
|
|
1160
1189
|
// Eagerly compile QueryBuilder to RawQueryFragment — later mutations to the sub-query won't be reflected
|
|
1161
|
-
const compiled = query instanceof
|
|
1162
|
-
this.
|
|
1190
|
+
const compiled = query instanceof _a ? query.toRaw() : query;
|
|
1191
|
+
this.#state.ctes.push({
|
|
1163
1192
|
name,
|
|
1164
1193
|
query: compiled,
|
|
1165
1194
|
recursive,
|
|
@@ -1169,53 +1198,26 @@ export class QueryBuilder {
|
|
|
1169
1198
|
return this;
|
|
1170
1199
|
}
|
|
1171
1200
|
clone(reset, preserve) {
|
|
1172
|
-
const qb = new
|
|
1173
|
-
reset
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
'_joins',
|
|
1183
|
-
'_joinedProps',
|
|
1184
|
-
'_cond',
|
|
1185
|
-
'_data',
|
|
1186
|
-
'_orderBy',
|
|
1187
|
-
'_schema',
|
|
1188
|
-
'_indexHint',
|
|
1189
|
-
'_collation',
|
|
1190
|
-
'_cache',
|
|
1191
|
-
'subQueries',
|
|
1192
|
-
'lockMode',
|
|
1193
|
-
'lockTables',
|
|
1194
|
-
'_groupBy',
|
|
1195
|
-
'_having',
|
|
1196
|
-
'_returning',
|
|
1197
|
-
'_comments',
|
|
1198
|
-
'_hintComments',
|
|
1199
|
-
'aliasCounter',
|
|
1200
|
-
'_unionQuery',
|
|
1201
|
-
];
|
|
1202
|
-
for (const prop of Object.keys(this)) {
|
|
1203
|
-
if (!preserve?.includes(prop) &&
|
|
1204
|
-
(reset === true || reset.includes(prop) || ['_helper', '_query'].includes(prop))) {
|
|
1205
|
-
continue;
|
|
1201
|
+
const qb = new _a(this.#state.mainAlias.entityName, this.metadata, this.driver, this.context, this.#state.mainAlias.aliasName, this.connectionType, this.em);
|
|
1202
|
+
if (reset !== true) {
|
|
1203
|
+
qb.#state = Utils.copy(this.#state);
|
|
1204
|
+
// CTEs contain NativeQueryBuilder instances that should not be deep-cloned
|
|
1205
|
+
qb.#state.ctes = this.#state.ctes.map(cte => ({ ...cte }));
|
|
1206
|
+
if (Array.isArray(reset)) {
|
|
1207
|
+
const fresh = _a.createDefaultState();
|
|
1208
|
+
for (const key of reset) {
|
|
1209
|
+
qb.#state[key] = fresh[key];
|
|
1210
|
+
}
|
|
1206
1211
|
}
|
|
1207
|
-
qb[prop] = properties.includes(prop) ? Utils.copy(this[prop]) : this[prop];
|
|
1208
|
-
}
|
|
1209
|
-
/* v8 ignore next */
|
|
1210
|
-
if (this._fields && reset !== true && !reset.includes('_fields')) {
|
|
1211
|
-
qb._fields = [...this._fields];
|
|
1212
1212
|
}
|
|
1213
|
-
if (
|
|
1214
|
-
|
|
1213
|
+
else if (preserve) {
|
|
1214
|
+
for (const key of preserve) {
|
|
1215
|
+
qb.#state[key] = Utils.copy(this.#state[key]);
|
|
1216
|
+
}
|
|
1215
1217
|
}
|
|
1216
|
-
qb.
|
|
1217
|
-
qb
|
|
1218
|
-
qb
|
|
1218
|
+
qb.#state.finalized = false;
|
|
1219
|
+
qb.#query = undefined;
|
|
1220
|
+
qb.#helper = qb.createQueryBuilderHelper();
|
|
1219
1221
|
return qb;
|
|
1220
1222
|
}
|
|
1221
1223
|
/**
|
|
@@ -1235,11 +1237,11 @@ export class QueryBuilder {
|
|
|
1235
1237
|
if (typeof meta.expression === 'string') {
|
|
1236
1238
|
return `(${meta.expression}) as ${this.platform.quoteIdentifier(this.alias)}`;
|
|
1237
1239
|
}
|
|
1238
|
-
const res = meta.expression(this.em, this.
|
|
1240
|
+
const res = meta.expression(this.em, this.#state.cond, {});
|
|
1239
1241
|
if (typeof res === 'string') {
|
|
1240
1242
|
return `(${res}) as ${this.platform.quoteIdentifier(this.alias)}`;
|
|
1241
1243
|
}
|
|
1242
|
-
if (res instanceof
|
|
1244
|
+
if (res instanceof _a) {
|
|
1243
1245
|
return `(${res.getFormattedQuery()}) as ${this.platform.quoteIdentifier(this.alias)}`;
|
|
1244
1246
|
}
|
|
1245
1247
|
if (isRaw(res)) {
|
|
@@ -1258,11 +1260,11 @@ export class QueryBuilder {
|
|
|
1258
1260
|
addPropertyJoin(prop, ownerAlias, alias, type, path, schema) {
|
|
1259
1261
|
schema ??= prop.targetMeta?.schema === '*' ? '*' : this.driver.getSchemaName(prop.targetMeta);
|
|
1260
1262
|
const key = `[tpt]${ownerAlias}#${alias}`;
|
|
1261
|
-
this.
|
|
1263
|
+
this.#state.joins[key] =
|
|
1262
1264
|
prop.kind === ReferenceKind.MANY_TO_ONE
|
|
1263
1265
|
? this.helper.joinManyToOneReference(prop, ownerAlias, alias, type, {}, schema)
|
|
1264
1266
|
: this.helper.joinOneToReference(prop, ownerAlias, alias, type, {}, schema);
|
|
1265
|
-
this.
|
|
1267
|
+
this.#state.joins[key].path = path;
|
|
1266
1268
|
return key;
|
|
1267
1269
|
}
|
|
1268
1270
|
joinReference(field, alias, cond, type, path, schema, subquery) {
|
|
@@ -1272,7 +1274,7 @@ export class QueryBuilder {
|
|
|
1272
1274
|
name: '__subquery__',
|
|
1273
1275
|
kind: ReferenceKind.MANY_TO_ONE,
|
|
1274
1276
|
};
|
|
1275
|
-
if (field instanceof
|
|
1277
|
+
if (field instanceof _a) {
|
|
1276
1278
|
prop.type = Utils.className(field.mainAlias.entityName);
|
|
1277
1279
|
prop.targetMeta = field.mainAlias.meta;
|
|
1278
1280
|
field = field.getNativeQuery();
|
|
@@ -1281,7 +1283,7 @@ export class QueryBuilder {
|
|
|
1281
1283
|
field = this.platform.formatQuery(field.sql, field.params);
|
|
1282
1284
|
}
|
|
1283
1285
|
const key = `${this.alias}.${prop.name}#${alias}`;
|
|
1284
|
-
this.
|
|
1286
|
+
this.#state.joins[key] = {
|
|
1285
1287
|
prop,
|
|
1286
1288
|
alias,
|
|
1287
1289
|
type,
|
|
@@ -1297,10 +1299,10 @@ export class QueryBuilder {
|
|
|
1297
1299
|
}
|
|
1298
1300
|
const [fromAlias, fromField] = this.helper.splitField(field);
|
|
1299
1301
|
const q = (str) => `'${str}'`;
|
|
1300
|
-
if (!this.
|
|
1301
|
-
throw new Error(`Trying to join ${q(fromField)} with alias ${q(fromAlias)}, but ${q(fromAlias)} is not a known alias. Available aliases are: ${Object.keys(this.
|
|
1302
|
+
if (!this.#state.aliases[fromAlias]) {
|
|
1303
|
+
throw new Error(`Trying to join ${q(fromField)} with alias ${q(fromAlias)}, but ${q(fromAlias)} is not a known alias. Available aliases are: ${Object.keys(this.#state.aliases).map(q).join(', ')}.`);
|
|
1302
1304
|
}
|
|
1303
|
-
const entityName = this.
|
|
1305
|
+
const entityName = this.#state.aliases[fromAlias].entityName;
|
|
1304
1306
|
const meta = this.metadata.get(entityName);
|
|
1305
1307
|
const prop = meta.properties[fromField];
|
|
1306
1308
|
if (!prop) {
|
|
@@ -1323,10 +1325,10 @@ export class QueryBuilder {
|
|
|
1323
1325
|
const criteriaNode = CriteriaNodeFactory.createNode(this.metadata, prop.targetMeta.class, cond);
|
|
1324
1326
|
cond = criteriaNode.process(this, { ignoreBranching: true, alias });
|
|
1325
1327
|
let aliasedName = `${fromAlias}.${prop.name}#${alias}`;
|
|
1326
|
-
path ??= `${Object.values(this.
|
|
1328
|
+
path ??= `${Object.values(this.#state.joins).find(j => j.alias === fromAlias)?.path ?? Utils.className(entityName)}.${prop.name}`;
|
|
1327
1329
|
if (prop.kind === ReferenceKind.ONE_TO_MANY) {
|
|
1328
|
-
this.
|
|
1329
|
-
this.
|
|
1330
|
+
this.#state.joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond, schema);
|
|
1331
|
+
this.#state.joins[aliasedName].path ??= path;
|
|
1330
1332
|
}
|
|
1331
1333
|
else if (prop.kind === ReferenceKind.MANY_TO_MANY) {
|
|
1332
1334
|
let pivotAlias = alias;
|
|
@@ -1336,19 +1338,19 @@ export class QueryBuilder {
|
|
|
1336
1338
|
aliasedName = `${fromAlias}.${prop.name}#${pivotAlias}`;
|
|
1337
1339
|
}
|
|
1338
1340
|
const joins = this.helper.joinManyToManyReference(prop, fromAlias, alias, pivotAlias, type, cond, path, schema);
|
|
1339
|
-
Object.assign(this.
|
|
1341
|
+
Object.assign(this.#state.joins, joins);
|
|
1340
1342
|
this.createAlias(prop.pivotEntity, pivotAlias);
|
|
1341
|
-
this.
|
|
1343
|
+
this.#state.joins[aliasedName].path ??= path;
|
|
1342
1344
|
aliasedName = Object.keys(joins)[1];
|
|
1343
1345
|
}
|
|
1344
1346
|
else if (prop.kind === ReferenceKind.ONE_TO_ONE) {
|
|
1345
|
-
this.
|
|
1346
|
-
this.
|
|
1347
|
+
this.#state.joins[aliasedName] = this.helper.joinOneToReference(prop, ownerAlias, alias, type, cond, schema);
|
|
1348
|
+
this.#state.joins[aliasedName].path ??= path;
|
|
1347
1349
|
}
|
|
1348
1350
|
else {
|
|
1349
1351
|
// MANY_TO_ONE
|
|
1350
|
-
this.
|
|
1351
|
-
this.
|
|
1352
|
+
this.#state.joins[aliasedName] = this.helper.joinManyToOneReference(prop, ownerAlias, alias, type, cond, schema);
|
|
1353
|
+
this.#state.joins[aliasedName].path ??= path;
|
|
1352
1354
|
}
|
|
1353
1355
|
return { prop, key: aliasedName };
|
|
1354
1356
|
}
|
|
@@ -1366,14 +1368,14 @@ export class QueryBuilder {
|
|
|
1366
1368
|
// Strip 'as alias' suffix if present — the alias is passed to mapper at the end
|
|
1367
1369
|
let field = originalField;
|
|
1368
1370
|
let customAlias;
|
|
1369
|
-
const asMatch =
|
|
1371
|
+
const asMatch = FIELD_ALIAS_RE.exec(originalField);
|
|
1370
1372
|
if (asMatch) {
|
|
1371
1373
|
field = asMatch[1].trim();
|
|
1372
1374
|
customAlias = asMatch[2];
|
|
1373
1375
|
}
|
|
1374
|
-
const join = Object.keys(this.
|
|
1376
|
+
const join = Object.keys(this.#state.joins).find(k => field === k.substring(0, k.indexOf('#')));
|
|
1375
1377
|
if (join && type === 'where') {
|
|
1376
|
-
ret.push(...this.helper.mapJoinColumns(this.type, this.
|
|
1378
|
+
ret.push(...this.helper.mapJoinColumns(this.type, this.#state.joins[join]));
|
|
1377
1379
|
return;
|
|
1378
1380
|
}
|
|
1379
1381
|
const [a, f] = this.helper.splitField(field);
|
|
@@ -1387,7 +1389,7 @@ export class QueryBuilder {
|
|
|
1387
1389
|
}
|
|
1388
1390
|
if (prop?.embedded || (prop?.kind === ReferenceKind.EMBEDDED && prop.object)) {
|
|
1389
1391
|
const name = prop.embeddedPath?.join('.') ?? prop.fieldNames[0];
|
|
1390
|
-
const aliased = this.
|
|
1392
|
+
const aliased = this.#state.aliases[a] ? `${a}.${name}` : name;
|
|
1391
1393
|
ret.push(getFieldName(aliased, customAlias));
|
|
1392
1394
|
return;
|
|
1393
1395
|
}
|
|
@@ -1420,16 +1422,16 @@ export class QueryBuilder {
|
|
|
1420
1422
|
ret.push(getFieldName(field, customAlias));
|
|
1421
1423
|
});
|
|
1422
1424
|
const requiresSQLConversion = this.mainAlias.meta.props.filter(p => p.hasConvertToJSValueSQL && p.persist !== false);
|
|
1423
|
-
if (this.flags.has(QueryFlag.CONVERT_CUSTOM_TYPES) &&
|
|
1425
|
+
if (this.#state.flags.has(QueryFlag.CONVERT_CUSTOM_TYPES) &&
|
|
1424
1426
|
(fields.includes('*') || fields.includes(`${this.mainAlias.aliasName}.*`)) &&
|
|
1425
1427
|
requiresSQLConversion.length > 0) {
|
|
1426
1428
|
for (const p of requiresSQLConversion) {
|
|
1427
1429
|
ret.push(this.helper.mapper(p.name, this.type));
|
|
1428
1430
|
}
|
|
1429
1431
|
}
|
|
1430
|
-
for (const f of Object.keys(this.
|
|
1431
|
-
if (type === 'where' && this.
|
|
1432
|
-
ret.push(...this.helper.mapJoinColumns(this.type, this.
|
|
1432
|
+
for (const f of Object.keys(this.#state.populateMap)) {
|
|
1433
|
+
if (type === 'where' && this.#state.joins[f]) {
|
|
1434
|
+
ret.push(...this.helper.mapJoinColumns(this.type, this.#state.joins[f]));
|
|
1433
1435
|
}
|
|
1434
1436
|
}
|
|
1435
1437
|
return Utils.unique(ret);
|
|
@@ -1445,16 +1447,16 @@ export class QueryBuilder {
|
|
|
1445
1447
|
}
|
|
1446
1448
|
const parts = field.split('.');
|
|
1447
1449
|
// Simple alias.property case - let prepareFields handle it
|
|
1448
|
-
if (parts.length === 2 && this.
|
|
1450
|
+
if (parts.length === 2 && this.#state.aliases[parts[0]]) {
|
|
1449
1451
|
return field;
|
|
1450
1452
|
}
|
|
1451
1453
|
// Start with root alias
|
|
1452
1454
|
let currentAlias = parts[0];
|
|
1453
|
-
let currentMeta = this.
|
|
1454
|
-
? this.metadata.get(this.
|
|
1455
|
+
let currentMeta = this.#state.aliases[currentAlias]
|
|
1456
|
+
? this.metadata.get(this.#state.aliases[currentAlias].entityName)
|
|
1455
1457
|
: this.mainAlias.meta;
|
|
1456
1458
|
// If first part is not an alias, it's a property of the main entity
|
|
1457
|
-
if (!this.
|
|
1459
|
+
if (!this.#state.aliases[currentAlias]) {
|
|
1458
1460
|
currentAlias = this.mainAlias.aliasName;
|
|
1459
1461
|
parts.unshift(currentAlias);
|
|
1460
1462
|
}
|
|
@@ -1492,14 +1494,14 @@ export class QueryBuilder {
|
|
|
1492
1494
|
}
|
|
1493
1495
|
// Find existing join or create new one
|
|
1494
1496
|
const joinPath = parts.slice(0, i + 1).join('.');
|
|
1495
|
-
const existingJoinKey = Object.keys(this.
|
|
1496
|
-
const join = this.
|
|
1497
|
+
const existingJoinKey = Object.keys(this.#state.joins).find(k => {
|
|
1498
|
+
const join = this.#state.joins[k];
|
|
1497
1499
|
// Check by path or by key prefix (key format is `alias.field#joinAlias`)
|
|
1498
1500
|
return join.path === joinPath || k.startsWith(`${currentAlias}.${propName}#`);
|
|
1499
1501
|
});
|
|
1500
1502
|
let joinAlias;
|
|
1501
1503
|
if (existingJoinKey) {
|
|
1502
|
-
joinAlias = this.
|
|
1504
|
+
joinAlias = this.#state.joins[existingJoinKey].alias;
|
|
1503
1505
|
}
|
|
1504
1506
|
else {
|
|
1505
1507
|
joinAlias = this.getNextAlias(prop.targetMeta?.className ?? propName);
|
|
@@ -1516,18 +1518,18 @@ export class QueryBuilder {
|
|
|
1516
1518
|
}
|
|
1517
1519
|
init(type, data, cond) {
|
|
1518
1520
|
this.ensureNotFinalized();
|
|
1519
|
-
this.
|
|
1520
|
-
if ([QueryType.UPDATE, QueryType.DELETE].includes(type) && Utils.hasObjectKeys(this.
|
|
1521
|
+
this.#state.type = type;
|
|
1522
|
+
if ([QueryType.UPDATE, QueryType.DELETE].includes(type) && Utils.hasObjectKeys(this.#state.cond)) {
|
|
1521
1523
|
throw new Error(`You are trying to call \`qb.where().${type.toLowerCase()}()\`. Calling \`qb.${type.toLowerCase()}()\` before \`qb.where()\` is required.`);
|
|
1522
1524
|
}
|
|
1523
1525
|
if (!this.helper.isTableNameAliasRequired(type)) {
|
|
1524
|
-
|
|
1526
|
+
this.#state.fields = undefined;
|
|
1525
1527
|
}
|
|
1526
1528
|
if (data) {
|
|
1527
1529
|
if (Utils.isEntity(data)) {
|
|
1528
1530
|
data = this.em?.getComparator().prepareEntity(data) ?? serialize(data);
|
|
1529
1531
|
}
|
|
1530
|
-
this.
|
|
1532
|
+
this.#state.data = this.helper.processData(data, this.#state.flags.has(QueryFlag.CONVERT_CUSTOM_TYPES), false);
|
|
1531
1533
|
}
|
|
1532
1534
|
if (cond) {
|
|
1533
1535
|
this.where(cond);
|
|
@@ -1535,9 +1537,9 @@ export class QueryBuilder {
|
|
|
1535
1537
|
return this;
|
|
1536
1538
|
}
|
|
1537
1539
|
getQueryBase(processVirtualEntity) {
|
|
1538
|
-
const qb = this.platform.createNativeQueryBuilder().setFlags(this.flags);
|
|
1540
|
+
const qb = this.platform.createNativeQueryBuilder().setFlags(this.#state.flags);
|
|
1539
1541
|
const { subQuery, aliasName, entityName, meta, rawTableName } = this.mainAlias;
|
|
1540
|
-
const requiresAlias = this.finalized && (this.
|
|
1542
|
+
const requiresAlias = this.#state.finalized && (this.#state.explicitAlias || this.helper.isTableNameAliasRequired(this.type));
|
|
1541
1543
|
const alias = requiresAlias ? aliasName : undefined;
|
|
1542
1544
|
const schema = this.getSchema(this.mainAlias);
|
|
1543
1545
|
const tableName = rawTableName
|
|
@@ -1547,41 +1549,42 @@ export class QueryBuilder {
|
|
|
1547
1549
|
: subQuery
|
|
1548
1550
|
? raw(`(${subQuery.sql}) as ${this.platform.quoteIdentifier(aliasName)}`, subQuery.params)
|
|
1549
1551
|
: this.helper.getTableName(entityName);
|
|
1550
|
-
const joinSchema = this.
|
|
1552
|
+
const joinSchema = this.#state.schema ?? this.em?.schema ?? schema;
|
|
1553
|
+
const schemaOverride = this.#state.schema ?? this.em?.schema;
|
|
1551
1554
|
if (meta.virtual && processVirtualEntity) {
|
|
1552
|
-
qb.from(raw(this.fromVirtual(meta)), { indexHint: this.
|
|
1555
|
+
qb.from(raw(this.fromVirtual(meta)), { indexHint: this.#state.indexHint });
|
|
1553
1556
|
}
|
|
1554
1557
|
else {
|
|
1555
1558
|
qb.from(tableName, {
|
|
1556
1559
|
schema: rawTableName ? undefined : schema,
|
|
1557
1560
|
alias,
|
|
1558
|
-
indexHint: this.
|
|
1561
|
+
indexHint: this.#state.indexHint,
|
|
1559
1562
|
});
|
|
1560
1563
|
}
|
|
1561
1564
|
switch (this.type) {
|
|
1562
1565
|
case QueryType.SELECT:
|
|
1563
|
-
qb.select(this.prepareFields(this.
|
|
1564
|
-
if (this.
|
|
1565
|
-
qb.distinctOn(this.prepareFields(this.
|
|
1566
|
+
qb.select(this.prepareFields(this.#state.fields, 'where', schema));
|
|
1567
|
+
if (this.#state.distinctOn) {
|
|
1568
|
+
qb.distinctOn(this.prepareFields(this.#state.distinctOn, 'where', schema));
|
|
1566
1569
|
}
|
|
1567
|
-
else if (this.flags.has(QueryFlag.DISTINCT)) {
|
|
1570
|
+
else if (this.#state.flags.has(QueryFlag.DISTINCT)) {
|
|
1568
1571
|
qb.distinct();
|
|
1569
1572
|
}
|
|
1570
|
-
this.helper.processJoins(qb, this.
|
|
1573
|
+
this.helper.processJoins(qb, this.#state.joins, joinSchema, schemaOverride);
|
|
1571
1574
|
break;
|
|
1572
1575
|
case QueryType.COUNT: {
|
|
1573
|
-
const fields = this.
|
|
1574
|
-
qb.count(fields, this.flags.has(QueryFlag.DISTINCT));
|
|
1575
|
-
this.helper.processJoins(qb, this.
|
|
1576
|
+
const fields = this.#state.fields.map(f => this.helper.mapper(f, this.type, undefined, undefined, schema));
|
|
1577
|
+
qb.count(fields, this.#state.flags.has(QueryFlag.DISTINCT));
|
|
1578
|
+
this.helper.processJoins(qb, this.#state.joins, joinSchema, schemaOverride);
|
|
1576
1579
|
break;
|
|
1577
1580
|
}
|
|
1578
1581
|
case QueryType.INSERT:
|
|
1579
|
-
qb.insert(this.
|
|
1582
|
+
qb.insert(this.#state.data);
|
|
1580
1583
|
break;
|
|
1581
1584
|
case QueryType.UPDATE:
|
|
1582
|
-
qb.update(this.
|
|
1583
|
-
this.helper.processJoins(qb, this.
|
|
1584
|
-
this.helper.updateVersionProperty(qb, this.
|
|
1585
|
+
qb.update(this.#state.data);
|
|
1586
|
+
this.helper.processJoins(qb, this.#state.joins, joinSchema, schemaOverride);
|
|
1587
|
+
this.helper.updateVersionProperty(qb, this.#state.data);
|
|
1585
1588
|
break;
|
|
1586
1589
|
case QueryType.DELETE:
|
|
1587
1590
|
qb.delete();
|
|
@@ -1632,17 +1635,17 @@ export class QueryBuilder {
|
|
|
1632
1635
|
![QueryType.SELECT, QueryType.COUNT].includes(this.type)) {
|
|
1633
1636
|
return;
|
|
1634
1637
|
}
|
|
1635
|
-
if (this.tptJoinsApplied) {
|
|
1638
|
+
if (this.#state.tptJoinsApplied) {
|
|
1636
1639
|
return;
|
|
1637
1640
|
}
|
|
1638
|
-
this.tptJoinsApplied = true;
|
|
1641
|
+
this.#state.tptJoinsApplied = true;
|
|
1639
1642
|
let childMeta = meta;
|
|
1640
1643
|
let childAlias = this.mainAlias.aliasName;
|
|
1641
1644
|
while (childMeta.tptParent) {
|
|
1642
1645
|
const parentMeta = childMeta.tptParent;
|
|
1643
1646
|
const parentAlias = this.getNextAlias(parentMeta.className);
|
|
1644
1647
|
this.createAlias(parentMeta.class, parentAlias);
|
|
1645
|
-
this.
|
|
1648
|
+
this.#state.tptAlias[parentMeta.className] = parentAlias;
|
|
1646
1649
|
this.addPropertyJoin(childMeta.tptParentProp, childAlias, parentAlias, JoinType.innerJoin, `[tpt]${childMeta.className}`);
|
|
1647
1650
|
childMeta = parentMeta;
|
|
1648
1651
|
childAlias = parentAlias;
|
|
@@ -1658,17 +1661,17 @@ export class QueryBuilder {
|
|
|
1658
1661
|
![QueryType.SELECT, QueryType.COUNT].includes(this.type)) {
|
|
1659
1662
|
return;
|
|
1660
1663
|
}
|
|
1661
|
-
if (!this.
|
|
1664
|
+
if (!this.#state.fields?.includes('*') && !this.#state.fields?.includes(`${this.mainAlias.aliasName}.*`)) {
|
|
1662
1665
|
return;
|
|
1663
1666
|
}
|
|
1664
1667
|
let parentMeta = meta.tptParent;
|
|
1665
1668
|
while (parentMeta) {
|
|
1666
|
-
const parentAlias = this.
|
|
1669
|
+
const parentAlias = this.#state.tptAlias[parentMeta.className];
|
|
1667
1670
|
if (parentAlias) {
|
|
1668
1671
|
const schema = parentMeta.schema === '*' ? '*' : this.driver.getSchemaName(parentMeta);
|
|
1669
1672
|
parentMeta
|
|
1670
1673
|
.ownProps.filter(prop => this.platform.shouldHaveColumn(prop, []))
|
|
1671
|
-
.forEach(prop => this.
|
|
1674
|
+
.forEach(prop => this.#state.fields.push(...this.driver.mapPropToFieldNames(this, prop, parentAlias, parentMeta, schema)));
|
|
1672
1675
|
}
|
|
1673
1676
|
parentMeta = parentMeta.tptParent;
|
|
1674
1677
|
}
|
|
@@ -1683,32 +1686,32 @@ export class QueryBuilder {
|
|
|
1683
1686
|
if (!descendants?.length || ![QueryType.SELECT, QueryType.COUNT].includes(this.type)) {
|
|
1684
1687
|
return;
|
|
1685
1688
|
}
|
|
1686
|
-
if (!this.
|
|
1689
|
+
if (!this.#state.fields?.includes('*') && !this.#state.fields?.includes(`${this.mainAlias.aliasName}.*`)) {
|
|
1687
1690
|
return;
|
|
1688
1691
|
}
|
|
1689
1692
|
// LEFT JOIN each descendant table and add their fields
|
|
1690
1693
|
for (const childMeta of descendants) {
|
|
1691
1694
|
const childAlias = this.getNextAlias(childMeta.className);
|
|
1692
1695
|
this.createAlias(childMeta.class, childAlias);
|
|
1693
|
-
this.
|
|
1696
|
+
this.#state.tptAlias[childMeta.className] = childAlias;
|
|
1694
1697
|
this.addPropertyJoin(childMeta.tptInverseProp, this.mainAlias.aliasName, childAlias, JoinType.leftJoin, `[tpt]${meta.className}`);
|
|
1695
1698
|
// Add child fields
|
|
1696
1699
|
const schema = childMeta.schema === '*' ? '*' : this.driver.getSchemaName(childMeta);
|
|
1697
1700
|
childMeta
|
|
1698
1701
|
.ownProps.filter(prop => !prop.primary && this.platform.shouldHaveColumn(prop, []))
|
|
1699
|
-
.forEach(prop => this.
|
|
1702
|
+
.forEach(prop => this.#state.fields.push(...this.driver.mapPropToFieldNames(this, prop, childAlias, childMeta, schema)));
|
|
1700
1703
|
}
|
|
1701
1704
|
// Add computed discriminator (CASE WHEN to determine concrete type)
|
|
1702
1705
|
// descendants is pre-sorted by depth (deepest first) during discovery
|
|
1703
1706
|
if (meta.tptDiscriminatorColumn) {
|
|
1704
|
-
this.
|
|
1707
|
+
this.#state.fields.push(this.driver.buildTPTDiscriminatorExpression(meta, descendants, this.#state.tptAlias, this.mainAlias.aliasName));
|
|
1705
1708
|
}
|
|
1706
1709
|
}
|
|
1707
1710
|
finalize() {
|
|
1708
|
-
if (this.finalized) {
|
|
1711
|
+
if (this.#state.finalized) {
|
|
1709
1712
|
return;
|
|
1710
1713
|
}
|
|
1711
|
-
if (!this.
|
|
1714
|
+
if (!this.#state.type) {
|
|
1712
1715
|
this.select('*');
|
|
1713
1716
|
}
|
|
1714
1717
|
const meta = this.mainAlias.meta;
|
|
@@ -1718,99 +1721,102 @@ export class QueryBuilder {
|
|
|
1718
1721
|
this.applyTPTPolymorphicJoins();
|
|
1719
1722
|
this.processPopulateHint();
|
|
1720
1723
|
this.processNestedJoins();
|
|
1721
|
-
if (meta && (this.
|
|
1724
|
+
if (meta && (this.#state.fields?.includes('*') || this.#state.fields?.includes(`${this.mainAlias.aliasName}.*`))) {
|
|
1722
1725
|
const schema = this.getSchema(this.mainAlias);
|
|
1723
1726
|
// Create a column mapping with unquoted aliases - quoting should be handled by the user via `quote` helper
|
|
1724
1727
|
// For TPT, use helper to resolve correct alias per property (inherited props use parent alias)
|
|
1725
1728
|
const quotedMainAlias = this.platform.quoteIdentifier(this.mainAlias.aliasName).toString();
|
|
1726
1729
|
const columns = meta.createColumnMappingObject(prop => this.helper.getTPTAliasForProperty(prop.name, this.mainAlias.aliasName), quotedMainAlias);
|
|
1727
1730
|
meta.props
|
|
1728
|
-
.filter(prop => prop.formula && (!prop.lazy || this.flags.has(QueryFlag.INCLUDE_LAZY_FORMULAS)))
|
|
1731
|
+
.filter(prop => prop.formula && (!prop.lazy || this.#state.flags.has(QueryFlag.INCLUDE_LAZY_FORMULAS)))
|
|
1729
1732
|
.map(prop => {
|
|
1730
1733
|
const aliased = this.platform.quoteIdentifier(prop.fieldNames[0]);
|
|
1731
1734
|
const table = this.helper.createFormulaTable(quotedMainAlias, meta, schema);
|
|
1732
1735
|
return `${this.driver.evaluateFormula(prop.formula, columns, table)} as ${aliased}`;
|
|
1733
1736
|
})
|
|
1734
|
-
.filter(field => !this.
|
|
1737
|
+
.filter(field => !this.#state.fields.some(f => {
|
|
1735
1738
|
if (isRaw(f)) {
|
|
1736
1739
|
return f.sql === field && f.params.length === 0;
|
|
1737
1740
|
}
|
|
1738
1741
|
return f === field;
|
|
1739
1742
|
}))
|
|
1740
|
-
.forEach(field => this.
|
|
1743
|
+
.forEach(field => this.#state.fields.push(raw(field)));
|
|
1741
1744
|
}
|
|
1742
|
-
QueryHelper.processObjectParams(this.
|
|
1743
|
-
QueryHelper.processObjectParams(this.
|
|
1744
|
-
QueryHelper.processObjectParams(this.
|
|
1745
|
+
QueryHelper.processObjectParams(this.#state.data);
|
|
1746
|
+
QueryHelper.processObjectParams(this.#state.cond);
|
|
1747
|
+
QueryHelper.processObjectParams(this.#state.having);
|
|
1745
1748
|
// automatically enable paginate flag when we detect to-many joins, but only if there is no `group by` clause
|
|
1746
|
-
if (!this.flags.has(QueryFlag.DISABLE_PAGINATE) &&
|
|
1747
|
-
this.
|
|
1749
|
+
if (!this.#state.flags.has(QueryFlag.DISABLE_PAGINATE) &&
|
|
1750
|
+
this.#state.groupBy.length === 0 &&
|
|
1751
|
+
this.hasToManyJoins()) {
|
|
1752
|
+
this.#state.flags.add(QueryFlag.PAGINATE);
|
|
1748
1753
|
}
|
|
1749
1754
|
if (meta &&
|
|
1750
1755
|
!meta.virtual &&
|
|
1751
|
-
this.flags.has(QueryFlag.PAGINATE) &&
|
|
1752
|
-
!this.flags.has(QueryFlag.DISABLE_PAGINATE) &&
|
|
1753
|
-
(this.
|
|
1756
|
+
this.#state.flags.has(QueryFlag.PAGINATE) &&
|
|
1757
|
+
!this.#state.flags.has(QueryFlag.DISABLE_PAGINATE) &&
|
|
1758
|
+
(this.#state.limit > 0 || this.#state.offset > 0)) {
|
|
1754
1759
|
this.wrapPaginateSubQuery(meta);
|
|
1755
1760
|
}
|
|
1756
|
-
if (meta &&
|
|
1761
|
+
if (meta &&
|
|
1762
|
+
(this.#state.flags.has(QueryFlag.UPDATE_SUB_QUERY) || this.#state.flags.has(QueryFlag.DELETE_SUB_QUERY))) {
|
|
1757
1763
|
this.wrapModifySubQuery(meta);
|
|
1758
1764
|
}
|
|
1759
|
-
this.finalized = true;
|
|
1765
|
+
this.#state.finalized = true;
|
|
1760
1766
|
}
|
|
1761
1767
|
/** @internal */
|
|
1762
1768
|
processPopulateHint() {
|
|
1763
|
-
if (this.populateHintFinalized) {
|
|
1769
|
+
if (this.#state.populateHintFinalized) {
|
|
1764
1770
|
return;
|
|
1765
1771
|
}
|
|
1766
1772
|
const meta = this.mainAlias.meta;
|
|
1767
|
-
if (meta && this.flags.has(QueryFlag.AUTO_JOIN_ONE_TO_ONE_OWNER)) {
|
|
1768
|
-
const relationsToPopulate = this.
|
|
1773
|
+
if (meta && this.#state.flags.has(QueryFlag.AUTO_JOIN_ONE_TO_ONE_OWNER)) {
|
|
1774
|
+
const relationsToPopulate = this.#state.populate.map(({ field }) => field);
|
|
1769
1775
|
meta.relations
|
|
1770
1776
|
.filter(prop => prop.kind === ReferenceKind.ONE_TO_ONE &&
|
|
1771
1777
|
!prop.owner &&
|
|
1772
1778
|
!relationsToPopulate.includes(prop.name) &&
|
|
1773
1779
|
!relationsToPopulate.includes(`${prop.name}:ref`))
|
|
1774
1780
|
.map(prop => ({ field: `${prop.name}:ref` }))
|
|
1775
|
-
.forEach(item => this.
|
|
1781
|
+
.forEach(item => this.#state.populate.push(item));
|
|
1776
1782
|
}
|
|
1777
|
-
this.
|
|
1783
|
+
this.#state.populate.forEach(({ field }) => {
|
|
1778
1784
|
const [fromAlias, fromField] = this.helper.splitField(field);
|
|
1779
1785
|
const aliasedField = `${fromAlias}.${fromField}`;
|
|
1780
|
-
const join = Object.keys(this.
|
|
1781
|
-
if (join && this.
|
|
1782
|
-
this.
|
|
1786
|
+
const join = Object.keys(this.#state.joins).find(k => `${aliasedField}#${this.#state.joins[k].alias}` === k);
|
|
1787
|
+
if (join && this.#state.joins[join] && this.helper.isOneToOneInverse(fromField)) {
|
|
1788
|
+
this.#state.populateMap[join] = this.#state.joins[join].alias;
|
|
1783
1789
|
return;
|
|
1784
1790
|
}
|
|
1785
1791
|
if (meta && this.helper.isOneToOneInverse(fromField)) {
|
|
1786
1792
|
const prop = meta.properties[fromField];
|
|
1787
1793
|
const alias = this.getNextAlias(prop.pivotEntity ?? prop.targetMeta.class);
|
|
1788
1794
|
const aliasedName = `${fromAlias}.${prop.name}#${alias}`;
|
|
1789
|
-
this.
|
|
1790
|
-
this.
|
|
1791
|
-
`${Object.values(this.
|
|
1792
|
-
this.
|
|
1795
|
+
this.#state.joins[aliasedName] = this.helper.joinOneToReference(prop, this.mainAlias.aliasName, alias, JoinType.leftJoin);
|
|
1796
|
+
this.#state.joins[aliasedName].path =
|
|
1797
|
+
`${Object.values(this.#state.joins).find(j => j.alias === fromAlias)?.path ?? meta.className}.${prop.name}`;
|
|
1798
|
+
this.#state.populateMap[aliasedName] = this.#state.joins[aliasedName].alias;
|
|
1793
1799
|
this.createAlias(prop.targetMeta.class, alias);
|
|
1794
1800
|
}
|
|
1795
1801
|
});
|
|
1796
1802
|
this.processPopulateWhere(false);
|
|
1797
1803
|
this.processPopulateWhere(true);
|
|
1798
|
-
this.populateHintFinalized = true;
|
|
1804
|
+
this.#state.populateHintFinalized = true;
|
|
1799
1805
|
}
|
|
1800
1806
|
processPopulateWhere(filter) {
|
|
1801
|
-
const
|
|
1802
|
-
if (
|
|
1807
|
+
const value = filter ? this.#state.populateFilter : this.#state.populateWhere;
|
|
1808
|
+
if (value == null || value === PopulateHint.ALL) {
|
|
1803
1809
|
return;
|
|
1804
1810
|
}
|
|
1805
|
-
let joins = Object.values(this.
|
|
1811
|
+
let joins = Object.values(this.#state.joins);
|
|
1806
1812
|
for (const join of joins) {
|
|
1807
1813
|
join.cond_ ??= join.cond;
|
|
1808
1814
|
join.cond = { ...join.cond };
|
|
1809
1815
|
}
|
|
1810
|
-
if (typeof
|
|
1811
|
-
const cond = CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName,
|
|
1816
|
+
if (typeof value === 'object') {
|
|
1817
|
+
const cond = CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, value).process(this, { matchPopulateJoins: true, ignoreBranching: true, preferNoBranch: true, filter });
|
|
1812
1818
|
// there might be new joins created by processing the `populateWhere` object
|
|
1813
|
-
joins = Object.values(this.
|
|
1819
|
+
joins = Object.values(this.#state.joins);
|
|
1814
1820
|
this.mergeOnConditions(joins, cond, filter);
|
|
1815
1821
|
}
|
|
1816
1822
|
}
|
|
@@ -1856,10 +1862,10 @@ export class QueryBuilder {
|
|
|
1856
1862
|
* otherwise the inner join could discard rows of the root table.
|
|
1857
1863
|
*/
|
|
1858
1864
|
processNestedJoins() {
|
|
1859
|
-
if (this.flags.has(QueryFlag.DISABLE_NESTED_INNER_JOIN)) {
|
|
1865
|
+
if (this.#state.flags.has(QueryFlag.DISABLE_NESTED_INNER_JOIN)) {
|
|
1860
1866
|
return;
|
|
1861
1867
|
}
|
|
1862
|
-
const joins = Object.values(this.
|
|
1868
|
+
const joins = Object.values(this.#state.joins);
|
|
1863
1869
|
const lookupParentGroup = (j) => {
|
|
1864
1870
|
return j.nested ?? (j.parent ? lookupParentGroup(j.parent) : undefined);
|
|
1865
1871
|
};
|
|
@@ -1882,30 +1888,30 @@ export class QueryBuilder {
|
|
|
1882
1888
|
}
|
|
1883
1889
|
}
|
|
1884
1890
|
hasToManyJoins() {
|
|
1885
|
-
return Object.values(this.
|
|
1891
|
+
return Object.values(this.#state.joins).some(join => {
|
|
1886
1892
|
return [ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(join.prop.kind);
|
|
1887
1893
|
});
|
|
1888
1894
|
}
|
|
1889
1895
|
wrapPaginateSubQuery(meta) {
|
|
1890
1896
|
const schema = this.getSchema(this.mainAlias);
|
|
1891
1897
|
const pks = this.prepareFields(meta.primaryKeys, 'sub-query', schema);
|
|
1892
|
-
const subQuery = this.clone(['
|
|
1898
|
+
const subQuery = this.clone(['orderBy', 'fields', 'lockMode', 'lockTables'])
|
|
1893
1899
|
.select(pks)
|
|
1894
1900
|
.groupBy(pks)
|
|
1895
|
-
.limit(this.
|
|
1901
|
+
.limit(this.#state.limit);
|
|
1896
1902
|
// revert the on conditions added via populateWhere, we want to apply those only once
|
|
1897
|
-
for (const join of Object.values(subQuery.
|
|
1903
|
+
for (const join of Object.values(subQuery.#state.joins)) {
|
|
1898
1904
|
if (join.cond_) {
|
|
1899
1905
|
join.cond = join.cond_;
|
|
1900
1906
|
}
|
|
1901
1907
|
}
|
|
1902
|
-
if (this.
|
|
1903
|
-
subQuery.offset(this.
|
|
1908
|
+
if (this.#state.offset) {
|
|
1909
|
+
subQuery.offset(this.#state.offset);
|
|
1904
1910
|
}
|
|
1905
1911
|
const addToSelect = [];
|
|
1906
|
-
if (this.
|
|
1912
|
+
if (this.#state.orderBy.length > 0) {
|
|
1907
1913
|
const orderBy = [];
|
|
1908
|
-
for (const orderMap of this.
|
|
1914
|
+
for (const orderMap of this.#state.orderBy) {
|
|
1909
1915
|
for (const field of Utils.getObjectQueryKeys(orderMap)) {
|
|
1910
1916
|
const direction = orderMap[field];
|
|
1911
1917
|
if (RawQueryFragment.isKnownFragmentSymbol(field)) {
|
|
@@ -1926,11 +1932,11 @@ export class QueryBuilder {
|
|
|
1926
1932
|
}
|
|
1927
1933
|
subQuery.orderBy(orderBy);
|
|
1928
1934
|
}
|
|
1929
|
-
subQuery.finalized = true;
|
|
1935
|
+
subQuery.#state.finalized = true;
|
|
1930
1936
|
const innerQuery = subQuery.as(this.mainAlias.aliasName).clear('select').select(pks);
|
|
1931
1937
|
if (addToSelect.length > 0) {
|
|
1932
1938
|
addToSelect.forEach(prop => {
|
|
1933
|
-
const field = this.
|
|
1939
|
+
const field = this.#state.fields.find(field => {
|
|
1934
1940
|
if (typeof field === 'object' && field && '__as' in field) {
|
|
1935
1941
|
return field.__as === prop;
|
|
1936
1942
|
}
|
|
@@ -1956,18 +1962,18 @@ export class QueryBuilder {
|
|
|
1956
1962
|
// https://stackoverflow.com/questions/17892762/mysql-this-version-of-mysql-doesnt-yet-support-limit-in-all-any-some-subqu
|
|
1957
1963
|
const subSubQuery = this.platform.createNativeQueryBuilder();
|
|
1958
1964
|
subSubQuery.select(pks).from(innerQuery);
|
|
1959
|
-
this.
|
|
1960
|
-
this.
|
|
1965
|
+
this.#state.limit = undefined;
|
|
1966
|
+
this.#state.offset = undefined;
|
|
1961
1967
|
// Save the original WHERE conditions before pruning joins
|
|
1962
|
-
const originalCond = this.
|
|
1968
|
+
const originalCond = this.#state.cond;
|
|
1963
1969
|
const populatePaths = this.getPopulatePaths();
|
|
1964
|
-
if (!this.
|
|
1970
|
+
if (!this.#state.fields.some(field => isRaw(field))) {
|
|
1965
1971
|
this.pruneJoinsForPagination(meta, populatePaths);
|
|
1966
1972
|
}
|
|
1967
1973
|
// Transfer WHERE conditions to ORDER BY joins (GH #6160)
|
|
1968
1974
|
this.transferConditionsForOrderByJoins(meta, originalCond, populatePaths);
|
|
1969
1975
|
const { sql, params } = subSubQuery.compile();
|
|
1970
|
-
this.select(this.
|
|
1976
|
+
this.select(this.#state.fields).where({
|
|
1971
1977
|
[Utils.getPrimaryKeyHash(meta.primaryKeys)]: { $in: raw(sql, params) },
|
|
1972
1978
|
});
|
|
1973
1979
|
}
|
|
@@ -1986,7 +1992,7 @@ export class QueryBuilder {
|
|
|
1986
1992
|
}
|
|
1987
1993
|
}
|
|
1988
1994
|
}
|
|
1989
|
-
addPath(this.
|
|
1995
|
+
addPath(this.#state.populate);
|
|
1990
1996
|
return paths;
|
|
1991
1997
|
}
|
|
1992
1998
|
normalizeJoinPath(join, meta) {
|
|
@@ -1998,14 +2004,14 @@ export class QueryBuilder {
|
|
|
1998
2004
|
* GH #6160
|
|
1999
2005
|
*/
|
|
2000
2006
|
transferConditionsForOrderByJoins(meta, cond, populatePaths) {
|
|
2001
|
-
if (!cond || this.
|
|
2007
|
+
if (!cond || this.#state.orderBy.length === 0) {
|
|
2002
2008
|
return;
|
|
2003
2009
|
}
|
|
2004
|
-
const orderByAliases = new Set(this.
|
|
2010
|
+
const orderByAliases = new Set(this.#state.orderBy
|
|
2005
2011
|
.flatMap(hint => Object.keys(hint))
|
|
2006
2012
|
.filter(k => !RawQueryFragment.isKnownFragmentSymbol(k))
|
|
2007
2013
|
.map(k => k.split('.')[0]));
|
|
2008
|
-
for (const join of Object.values(this.
|
|
2014
|
+
for (const join of Object.values(this.#state.joins)) {
|
|
2009
2015
|
const joinPath = this.normalizeJoinPath(join, meta);
|
|
2010
2016
|
const isPopulateJoin = populatePaths.has(joinPath);
|
|
2011
2017
|
// Only transfer conditions for joins used for ORDER BY but not for population
|
|
@@ -2018,8 +2024,8 @@ export class QueryBuilder {
|
|
|
2018
2024
|
* Removes joins that are not used for population or ordering to improve performance.
|
|
2019
2025
|
*/
|
|
2020
2026
|
pruneJoinsForPagination(meta, populatePaths) {
|
|
2021
|
-
const orderByAliases = this.
|
|
2022
|
-
const joins = Object.entries(this.
|
|
2027
|
+
const orderByAliases = this.#state.orderBy.flatMap(hint => Object.keys(hint)).map(k => k.split('.')[0]);
|
|
2028
|
+
const joins = Object.entries(this.#state.joins);
|
|
2023
2029
|
const rootAlias = this.alias;
|
|
2024
2030
|
function addParentAlias(alias) {
|
|
2025
2031
|
const join = joins.find(j => j[1].alias === alias);
|
|
@@ -2034,7 +2040,7 @@ export class QueryBuilder {
|
|
|
2034
2040
|
for (const [key, join] of joins) {
|
|
2035
2041
|
const path = this.normalizeJoinPath(join, meta);
|
|
2036
2042
|
if (!populatePaths.has(path) && !orderByAliases.includes(join.alias)) {
|
|
2037
|
-
delete this.
|
|
2043
|
+
delete this.#state.joins[key];
|
|
2038
2044
|
}
|
|
2039
2045
|
}
|
|
2040
2046
|
}
|
|
@@ -2066,25 +2072,25 @@ export class QueryBuilder {
|
|
|
2066
2072
|
}
|
|
2067
2073
|
wrapModifySubQuery(meta) {
|
|
2068
2074
|
const subQuery = this.clone();
|
|
2069
|
-
subQuery.finalized = true;
|
|
2075
|
+
subQuery.#state.finalized = true;
|
|
2070
2076
|
// wrap one more time to get around MySQL limitations
|
|
2071
2077
|
// https://stackoverflow.com/questions/45494/mysql-error-1093-cant-specify-target-table-for-update-in-from-clause
|
|
2072
2078
|
const subSubQuery = this.platform.createNativeQueryBuilder();
|
|
2073
|
-
const method = this.flags.has(QueryFlag.UPDATE_SUB_QUERY) ? 'update' : 'delete';
|
|
2079
|
+
const method = this.#state.flags.has(QueryFlag.UPDATE_SUB_QUERY) ? 'update' : 'delete';
|
|
2074
2080
|
const schema = this.getSchema(this.mainAlias);
|
|
2075
2081
|
const pks = this.prepareFields(meta.primaryKeys, 'sub-query', schema);
|
|
2076
|
-
this.
|
|
2077
|
-
this.
|
|
2082
|
+
this.#state.cond = {}; // otherwise we would trigger validation error
|
|
2083
|
+
this.#state.joins = {}; // included in the subquery
|
|
2078
2084
|
subSubQuery.select(pks).from(subQuery.as(this.mainAlias.aliasName));
|
|
2079
2085
|
const { sql, params } = subSubQuery.compile();
|
|
2080
|
-
this[method](this.
|
|
2086
|
+
this[method](this.#state.data).where({
|
|
2081
2087
|
[Utils.getPrimaryKeyHash(meta.primaryKeys)]: { $in: raw(sql, params) },
|
|
2082
2088
|
});
|
|
2083
2089
|
}
|
|
2084
2090
|
getSchema(alias) {
|
|
2085
2091
|
const { meta } = alias;
|
|
2086
2092
|
const metaSchema = meta.schema && meta.schema !== '*' ? meta.schema : undefined;
|
|
2087
|
-
const schema = this.
|
|
2093
|
+
const schema = this.#state.schema ?? metaSchema ?? this.em?.schema ?? this.em?.config.getSchema(true);
|
|
2088
2094
|
if (schema === this.platform.getDefaultSchemaName()) {
|
|
2089
2095
|
return undefined;
|
|
2090
2096
|
}
|
|
@@ -2094,51 +2100,51 @@ export class QueryBuilder {
|
|
|
2094
2100
|
createAlias(entityName, aliasName, subQuery) {
|
|
2095
2101
|
const meta = this.metadata.find(entityName);
|
|
2096
2102
|
const alias = { aliasName, entityName, meta, subQuery };
|
|
2097
|
-
this.
|
|
2103
|
+
this.#state.aliases[aliasName] = alias;
|
|
2098
2104
|
return alias;
|
|
2099
2105
|
}
|
|
2100
2106
|
createMainAlias(entityName, aliasName, subQuery) {
|
|
2101
|
-
this.
|
|
2102
|
-
this
|
|
2103
|
-
return this.
|
|
2107
|
+
this.#state.mainAlias = this.createAlias(entityName, aliasName, subQuery);
|
|
2108
|
+
this.#helper = this.createQueryBuilderHelper();
|
|
2109
|
+
return this.#state.mainAlias;
|
|
2104
2110
|
}
|
|
2105
2111
|
fromSubQuery(target, aliasName) {
|
|
2106
2112
|
const { entityName } = target.mainAlias;
|
|
2107
2113
|
aliasName ??= this.getNextAlias(entityName);
|
|
2108
|
-
const subQuery = target.
|
|
2114
|
+
const subQuery = target.#state.unionQuery ? target.toRaw() : target.getNativeQuery();
|
|
2109
2115
|
this.createMainAlias(entityName, aliasName, subQuery);
|
|
2110
2116
|
}
|
|
2111
2117
|
fromEntityName(entityName, aliasName) {
|
|
2112
|
-
aliasName ??= this.
|
|
2118
|
+
aliasName ??= this.#state.mainAlias?.aliasName ?? this.getNextAlias(entityName);
|
|
2113
2119
|
this.createMainAlias(entityName, aliasName);
|
|
2114
2120
|
}
|
|
2115
2121
|
fromRawTable(tableName, aliasName) {
|
|
2116
|
-
aliasName ??= this.
|
|
2122
|
+
aliasName ??= this.#state.mainAlias?.aliasName ?? this.getNextAlias(tableName);
|
|
2117
2123
|
const meta = new EntityMetadata({
|
|
2118
2124
|
className: tableName,
|
|
2119
2125
|
collection: tableName,
|
|
2120
2126
|
});
|
|
2121
2127
|
meta.root = meta;
|
|
2122
|
-
this.
|
|
2128
|
+
this.#state.mainAlias = {
|
|
2123
2129
|
aliasName,
|
|
2124
2130
|
entityName: tableName,
|
|
2125
2131
|
meta,
|
|
2126
2132
|
rawTableName: tableName,
|
|
2127
2133
|
};
|
|
2128
|
-
this.
|
|
2129
|
-
this
|
|
2134
|
+
this.#state.aliases[aliasName] = this.#state.mainAlias;
|
|
2135
|
+
this.#helper = this.createQueryBuilderHelper();
|
|
2130
2136
|
}
|
|
2131
2137
|
createQueryBuilderHelper() {
|
|
2132
|
-
return new QueryBuilderHelper(this.mainAlias.entityName, this.mainAlias.aliasName, this.
|
|
2138
|
+
return new QueryBuilderHelper(this.mainAlias.entityName, this.mainAlias.aliasName, this.#state.aliases, this.#state.subQueries, this.driver, this.#state.tptAlias);
|
|
2133
2139
|
}
|
|
2134
2140
|
ensureFromClause() {
|
|
2135
2141
|
/* v8 ignore next */
|
|
2136
|
-
if (!this.
|
|
2142
|
+
if (!this.#state.mainAlias) {
|
|
2137
2143
|
throw new Error(`Cannot proceed to build a query because the main alias is not set.`);
|
|
2138
2144
|
}
|
|
2139
2145
|
}
|
|
2140
2146
|
ensureNotFinalized() {
|
|
2141
|
-
if (this.finalized) {
|
|
2147
|
+
if (this.#state.finalized) {
|
|
2142
2148
|
throw new Error('This QueryBuilder instance is already finalized, clone it first if you want to modify it.');
|
|
2143
2149
|
}
|
|
2144
2150
|
}
|
|
@@ -2155,26 +2161,27 @@ export class QueryBuilder {
|
|
|
2155
2161
|
.forEach(k => delete object[k]);
|
|
2156
2162
|
hidden.forEach(k => delete object[k]);
|
|
2157
2163
|
let prefix = this.type ? this.type.substring(0, 1) + this.type.toLowerCase().substring(1) : '';
|
|
2158
|
-
if (this.
|
|
2159
|
-
object.data = this.
|
|
2164
|
+
if (this.#state.data) {
|
|
2165
|
+
object.data = this.#state.data;
|
|
2160
2166
|
}
|
|
2161
|
-
if (this.
|
|
2162
|
-
object.schema = this.
|
|
2167
|
+
if (this.#state.schema) {
|
|
2168
|
+
object.schema = this.#state.schema;
|
|
2163
2169
|
}
|
|
2164
|
-
if (!Utils.isEmpty(this.
|
|
2165
|
-
object.where = this.
|
|
2170
|
+
if (!Utils.isEmpty(this.#state.cond)) {
|
|
2171
|
+
object.where = this.#state.cond;
|
|
2166
2172
|
}
|
|
2167
|
-
if (this.
|
|
2173
|
+
if (this.#state.onConflict?.[0]) {
|
|
2168
2174
|
prefix = 'Upsert';
|
|
2169
|
-
object.onConflict = this.
|
|
2175
|
+
object.onConflict = this.#state.onConflict[0];
|
|
2170
2176
|
}
|
|
2171
|
-
if (!Utils.isEmpty(this.
|
|
2172
|
-
object.orderBy = this.
|
|
2177
|
+
if (!Utils.isEmpty(this.#state.orderBy)) {
|
|
2178
|
+
object.orderBy = this.#state.orderBy;
|
|
2173
2179
|
}
|
|
2174
|
-
const name = this.
|
|
2175
|
-
? `${prefix}QueryBuilder<${Utils.className(this.
|
|
2180
|
+
const name = this.#state.mainAlias
|
|
2181
|
+
? `${prefix}QueryBuilder<${Utils.className(this.#state.mainAlias?.entityName)}>`
|
|
2176
2182
|
: 'QueryBuilder';
|
|
2177
2183
|
const ret = inspect(object, { depth });
|
|
2178
2184
|
return ret === '[Object]' ? `[${name}]` : name + ' ' + ret;
|
|
2179
2185
|
}
|
|
2180
2186
|
}
|
|
2187
|
+
_a = QueryBuilder;
|