@mikro-orm/sql 7.0.0-rc.2 → 7.0.0-rc.3
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.js +2 -1
- package/AbstractSqlDriver.d.ts +18 -12
- package/AbstractSqlDriver.js +187 -38
- package/AbstractSqlPlatform.d.ts +1 -0
- package/AbstractSqlPlatform.js +5 -3
- package/PivotCollectionPersister.js +2 -2
- package/SqlEntityManager.d.ts +2 -2
- package/SqlEntityManager.js +5 -5
- package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +2 -0
- package/dialects/mssql/MsSqlNativeQueryBuilder.js +8 -4
- package/dialects/mysql/BaseMySqlPlatform.js +1 -2
- package/dialects/mysql/MySqlSchemaHelper.js +21 -10
- package/dialects/postgresql/BasePostgreSqlPlatform.js +38 -30
- package/dialects/postgresql/PostgreSqlSchemaHelper.js +63 -47
- package/dialects/sqlite/BaseSqliteConnection.js +2 -2
- package/dialects/sqlite/NodeSqliteDialect.js +3 -1
- package/dialects/sqlite/SqlitePlatform.js +4 -1
- package/dialects/sqlite/SqliteSchemaHelper.js +11 -9
- package/package.json +30 -30
- package/plugin/transformer.js +17 -16
- package/query/CriteriaNode.js +28 -10
- package/query/CriteriaNodeFactory.js +5 -1
- package/query/NativeQueryBuilder.d.ts +25 -0
- package/query/NativeQueryBuilder.js +61 -1
- package/query/ObjectCriteriaNode.js +71 -27
- package/query/QueryBuilder.d.ts +151 -48
- package/query/QueryBuilder.js +233 -54
- package/query/QueryBuilderHelper.d.ts +4 -3
- package/query/QueryBuilderHelper.js +47 -17
- package/query/ScalarCriteriaNode.js +14 -7
- package/query/raw.js +1 -1
- package/schema/DatabaseSchema.js +21 -15
- package/schema/DatabaseTable.js +114 -54
- package/schema/SchemaComparator.js +56 -32
- package/schema/SchemaHelper.js +28 -8
- package/schema/SqlSchemaGenerator.js +13 -7
- package/tsconfig.build.tsbuildinfo +1 -1
- package/typings.d.ts +6 -4
package/query/QueryBuilder.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { helper, inspect, isRaw, LoadStrategy, LockMode, PopulateHint, QueryFlag, QueryHelper, raw, RawQueryFragment, Reference, ReferenceKind, serialize, Utils, ValidationError, } from '@mikro-orm/core';
|
|
1
|
+
import { EntityMetadata, helper, inspect, isRaw, LoadStrategy, LockMode, PopulateHint, QueryFlag, QueryHelper, raw, RawQueryFragment, Reference, ReferenceKind, serialize, Utils, ValidationError, } from '@mikro-orm/core';
|
|
2
2
|
import { JoinType, QueryType } from './enums.js';
|
|
3
3
|
import { QueryBuilderHelper } from './QueryBuilderHelper.js';
|
|
4
4
|
import { CriteriaNodeFactory } from './CriteriaNodeFactory.js';
|
|
@@ -81,6 +81,8 @@ export class QueryBuilder {
|
|
|
81
81
|
_tptAlias = {}; // maps entity className to alias for TPT parent tables
|
|
82
82
|
_helper;
|
|
83
83
|
_query;
|
|
84
|
+
_unionQuery;
|
|
85
|
+
_ctes = [];
|
|
84
86
|
platform;
|
|
85
87
|
tptJoinsApplied = false;
|
|
86
88
|
autoJoinedPaths = [];
|
|
@@ -279,7 +281,8 @@ export class QueryBuilder {
|
|
|
279
281
|
if (populate) {
|
|
280
282
|
populate.children.push(item);
|
|
281
283
|
}
|
|
282
|
-
else {
|
|
284
|
+
else {
|
|
285
|
+
// root entity
|
|
283
286
|
this._populate.push(item);
|
|
284
287
|
}
|
|
285
288
|
this._joinedProps.set(alias, item);
|
|
@@ -382,7 +385,8 @@ export class QueryBuilder {
|
|
|
382
385
|
if (Utils.hasObjectKeys(cond) || RawQueryFragment.hasObjectFragments(cond)) {
|
|
383
386
|
// remove nested filters, we only care about scalars here, nesting would require another join branch
|
|
384
387
|
for (const key of Object.keys(cond)) {
|
|
385
|
-
if (Utils.isPlainObject(cond[key]) &&
|
|
388
|
+
if (Utils.isPlainObject(cond[key]) &&
|
|
389
|
+
Object.keys(cond[key]).every(k => !(Utils.isOperator(k) && !['$some', '$none', '$every', '$size'].includes(k)))) {
|
|
386
390
|
delete cond[key];
|
|
387
391
|
}
|
|
388
392
|
}
|
|
@@ -433,7 +437,8 @@ export class QueryBuilder {
|
|
|
433
437
|
const topLevel = !op || !(Utils.hasObjectKeys(this._cond) || RawQueryFragment.hasObjectFragments(this._cond));
|
|
434
438
|
const criteriaNode = CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, processedCond);
|
|
435
439
|
const ignoreBranching = this.__populateWhere === 'infer';
|
|
436
|
-
if ([QueryType.UPDATE, QueryType.DELETE].includes(this.type) &&
|
|
440
|
+
if ([QueryType.UPDATE, QueryType.DELETE].includes(this.type) &&
|
|
441
|
+
criteriaNode.willAutoJoin(this, undefined, { ignoreBranching })) {
|
|
437
442
|
// use sub-query to support joining
|
|
438
443
|
this.setFlag(this.type === QueryType.UPDATE ? QueryFlag.UPDATE_SUB_QUERY : QueryFlag.DELETE_SUB_QUERY);
|
|
439
444
|
this.select(this.mainAlias.meta.primaryKeys, true);
|
|
@@ -711,6 +716,9 @@ export class QueryBuilder {
|
|
|
711
716
|
if (target instanceof QueryBuilder) {
|
|
712
717
|
this.fromSubQuery(target, aliasName);
|
|
713
718
|
}
|
|
719
|
+
else if (typeof target === 'string' && !this.metadata.find(target)) {
|
|
720
|
+
this.fromRawTable(target, aliasName);
|
|
721
|
+
}
|
|
714
722
|
else {
|
|
715
723
|
if (aliasName && this._mainAlias && Utils.className(target) !== this._mainAlias.aliasName) {
|
|
716
724
|
throw new Error(`Cannot override the alias to '${aliasName}' since a query already contains references to '${this._mainAlias.aliasName}'`);
|
|
@@ -720,12 +728,32 @@ export class QueryBuilder {
|
|
|
720
728
|
return this;
|
|
721
729
|
}
|
|
722
730
|
getNativeQuery(processVirtualEntity = true) {
|
|
731
|
+
if (this._unionQuery) {
|
|
732
|
+
if (!this._query?.qb) {
|
|
733
|
+
this._query = {};
|
|
734
|
+
const nqb = this.platform.createNativeQueryBuilder();
|
|
735
|
+
nqb.select('*');
|
|
736
|
+
nqb.from(raw(`(${this._unionQuery.sql})`, this._unionQuery.params));
|
|
737
|
+
this._query.qb = nqb;
|
|
738
|
+
}
|
|
739
|
+
return this._query.qb;
|
|
740
|
+
}
|
|
723
741
|
if (this._query?.qb) {
|
|
724
742
|
return this._query.qb;
|
|
725
743
|
}
|
|
726
744
|
this._query = {};
|
|
727
745
|
this.finalize();
|
|
728
746
|
const qb = this.getQueryBase(processVirtualEntity);
|
|
747
|
+
for (const cte of this._ctes) {
|
|
748
|
+
const query = cte.query;
|
|
749
|
+
const opts = { columns: cte.columns, materialized: cte.materialized };
|
|
750
|
+
if (cte.recursive) {
|
|
751
|
+
qb.withRecursive(cte.name, query, opts);
|
|
752
|
+
}
|
|
753
|
+
else {
|
|
754
|
+
qb.with(cte.name, query, opts);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
729
757
|
const schema = this.getSchema(this.mainAlias);
|
|
730
758
|
const isNotEmptyObject = (obj) => Utils.hasObjectKeys(obj) || RawQueryFragment.hasObjectFragments(obj);
|
|
731
759
|
Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type, this._cond, qb), this._cond && !this._onConflict);
|
|
@@ -748,7 +776,7 @@ export class QueryBuilder {
|
|
|
748
776
|
this.helper.getLockSQL(qb, this.lockMode, this.lockTables, this._joins);
|
|
749
777
|
}
|
|
750
778
|
this.helper.finalize(this.type, qb, this.mainAlias.meta, this._data, this._returning);
|
|
751
|
-
return this._query.qb = qb;
|
|
779
|
+
return (this._query.qb = qb);
|
|
752
780
|
}
|
|
753
781
|
/**
|
|
754
782
|
* Returns the query with parameters as wildcards.
|
|
@@ -764,6 +792,9 @@ export class QueryBuilder {
|
|
|
764
792
|
return raw(sql, params);
|
|
765
793
|
}
|
|
766
794
|
toQuery() {
|
|
795
|
+
if (this._unionQuery) {
|
|
796
|
+
return this._unionQuery;
|
|
797
|
+
}
|
|
767
798
|
if (this._query?.sql) {
|
|
768
799
|
return { sql: this._query.sql, params: this._query.params };
|
|
769
800
|
}
|
|
@@ -874,7 +905,12 @@ export class QueryBuilder {
|
|
|
874
905
|
this.limit(1);
|
|
875
906
|
}
|
|
876
907
|
const query = this.toQuery();
|
|
877
|
-
const cached = await this.em?.tryCache(this.mainAlias.entityName, this._cache, [
|
|
908
|
+
const cached = await this.em?.tryCache(this.mainAlias.entityName, this._cache, [
|
|
909
|
+
'qb.execute',
|
|
910
|
+
query.sql,
|
|
911
|
+
query.params,
|
|
912
|
+
method,
|
|
913
|
+
]);
|
|
878
914
|
if (cached?.data !== undefined) {
|
|
879
915
|
return cached.data;
|
|
880
916
|
}
|
|
@@ -1031,7 +1067,10 @@ export class QueryBuilder {
|
|
|
1031
1067
|
else {
|
|
1032
1068
|
const qb = (this._type === undefined ? this : this.clone());
|
|
1033
1069
|
qb.processPopulateHint(); // needs to happen sooner so `qb.hasToManyJoins()` reports correctly
|
|
1034
|
-
qb.count(field, distinct ?? qb.hasToManyJoins())
|
|
1070
|
+
qb.count(field, distinct ?? qb.hasToManyJoins())
|
|
1071
|
+
.limit(undefined)
|
|
1072
|
+
.offset(undefined)
|
|
1073
|
+
.orderBy([]);
|
|
1035
1074
|
res = await qb.execute('get', false);
|
|
1036
1075
|
}
|
|
1037
1076
|
return res ? +res.count : 0;
|
|
@@ -1059,17 +1098,110 @@ export class QueryBuilder {
|
|
|
1059
1098
|
Object.defineProperty(qb, '__as', { enumerable: false, value: finalAlias });
|
|
1060
1099
|
return qb;
|
|
1061
1100
|
}
|
|
1101
|
+
/**
|
|
1102
|
+
* Combines the current query with one or more other queries using `UNION ALL`.
|
|
1103
|
+
* All queries must select the same columns. Returns a `QueryBuilder` that
|
|
1104
|
+
* can be used with `$in`, passed to `qb.from()`, or converted via `.getQuery()`,
|
|
1105
|
+
* `.getParams()`, `.toQuery()`, `.toRaw()`, etc.
|
|
1106
|
+
*
|
|
1107
|
+
* ```ts
|
|
1108
|
+
* const qb1 = em.createQueryBuilder(Employee).select('id').where(condition1);
|
|
1109
|
+
* const qb2 = em.createQueryBuilder(Employee).select('id').where(condition2);
|
|
1110
|
+
* const qb3 = em.createQueryBuilder(Employee).select('id').where(condition3);
|
|
1111
|
+
* const subquery = qb1.unionAll(qb2, qb3);
|
|
1112
|
+
*
|
|
1113
|
+
* const results = await em.find(Employee, { id: { $in: subquery } });
|
|
1114
|
+
* ```
|
|
1115
|
+
*/
|
|
1116
|
+
unionAll(...others) {
|
|
1117
|
+
return this.buildUnionQuery('union all', others);
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Combines the current query with one or more other queries using `UNION` (with deduplication).
|
|
1121
|
+
* All queries must select the same columns. Returns a `QueryBuilder` that
|
|
1122
|
+
* can be used with `$in`, passed to `qb.from()`, or converted via `.getQuery()`,
|
|
1123
|
+
* `.getParams()`, `.toQuery()`, `.toRaw()`, etc.
|
|
1124
|
+
*
|
|
1125
|
+
* ```ts
|
|
1126
|
+
* const qb1 = em.createQueryBuilder(Employee).select('id').where(condition1);
|
|
1127
|
+
* const qb2 = em.createQueryBuilder(Employee).select('id').where(condition2);
|
|
1128
|
+
* const subquery = qb1.union(qb2);
|
|
1129
|
+
*
|
|
1130
|
+
* const results = await em.find(Employee, { id: { $in: subquery } });
|
|
1131
|
+
* ```
|
|
1132
|
+
*/
|
|
1133
|
+
union(...others) {
|
|
1134
|
+
return this.buildUnionQuery('union', others);
|
|
1135
|
+
}
|
|
1136
|
+
buildUnionQuery(separator, others) {
|
|
1137
|
+
const all = [this, ...others];
|
|
1138
|
+
const parts = [];
|
|
1139
|
+
const params = [];
|
|
1140
|
+
for (const qb of all) {
|
|
1141
|
+
const compiled = qb instanceof QueryBuilder ? qb.toQuery() : qb.compile();
|
|
1142
|
+
parts.push(`(${compiled.sql})`);
|
|
1143
|
+
params.push(...compiled.params);
|
|
1144
|
+
}
|
|
1145
|
+
const result = this.clone(true);
|
|
1146
|
+
result._unionQuery = { sql: parts.join(` ${separator} `), params };
|
|
1147
|
+
return result;
|
|
1148
|
+
}
|
|
1149
|
+
with(name, query, options) {
|
|
1150
|
+
return this.addCte(name, query, options);
|
|
1151
|
+
}
|
|
1152
|
+
withRecursive(name, query, options) {
|
|
1153
|
+
return this.addCte(name, query, options, true);
|
|
1154
|
+
}
|
|
1155
|
+
addCte(name, query, options, recursive) {
|
|
1156
|
+
this.ensureNotFinalized();
|
|
1157
|
+
if (this._ctes.some(cte => cte.name === name)) {
|
|
1158
|
+
throw new Error(`CTE with name '${name}' already exists`);
|
|
1159
|
+
}
|
|
1160
|
+
// Eagerly compile QueryBuilder to RawQueryFragment — later mutations to the sub-query won't be reflected
|
|
1161
|
+
const compiled = query instanceof QueryBuilder ? query.toRaw() : query;
|
|
1162
|
+
this._ctes.push({
|
|
1163
|
+
name,
|
|
1164
|
+
query: compiled,
|
|
1165
|
+
recursive,
|
|
1166
|
+
columns: options?.columns,
|
|
1167
|
+
materialized: options?.materialized,
|
|
1168
|
+
});
|
|
1169
|
+
return this;
|
|
1170
|
+
}
|
|
1062
1171
|
clone(reset, preserve) {
|
|
1063
1172
|
const qb = new QueryBuilder(this.mainAlias.entityName, this.metadata, this.driver, this.context, this.mainAlias.aliasName, this.connectionType, this.em);
|
|
1064
1173
|
reset = reset || [];
|
|
1065
1174
|
// clone array/object properties
|
|
1066
1175
|
const properties = [
|
|
1067
|
-
'flags',
|
|
1068
|
-
'
|
|
1069
|
-
'
|
|
1176
|
+
'flags',
|
|
1177
|
+
'_populate',
|
|
1178
|
+
'_populateWhere',
|
|
1179
|
+
'_populateFilter',
|
|
1180
|
+
'__populateWhere',
|
|
1181
|
+
'_populateMap',
|
|
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',
|
|
1070
1201
|
];
|
|
1071
1202
|
for (const prop of Object.keys(this)) {
|
|
1072
|
-
if (!preserve?.includes(prop) &&
|
|
1203
|
+
if (!preserve?.includes(prop) &&
|
|
1204
|
+
(reset === true || reset.includes(prop) || ['_helper', '_query'].includes(prop))) {
|
|
1073
1205
|
continue;
|
|
1074
1206
|
}
|
|
1075
1207
|
qb[prop] = properties.includes(prop) ? Utils.copy(this[prop]) : this[prop];
|
|
@@ -1078,6 +1210,9 @@ export class QueryBuilder {
|
|
|
1078
1210
|
if (this._fields && reset !== true && !reset.includes('_fields')) {
|
|
1079
1211
|
qb._fields = [...this._fields];
|
|
1080
1212
|
}
|
|
1213
|
+
if (this._ctes.length && reset !== true && !reset.includes('_ctes')) {
|
|
1214
|
+
qb._ctes = this._ctes.map(cte => ({ ...cte }));
|
|
1215
|
+
}
|
|
1081
1216
|
qb._aliases = { ...this._aliases };
|
|
1082
1217
|
qb._helper.aliasMap = qb._aliases;
|
|
1083
1218
|
qb.finalized = false;
|
|
@@ -1123,9 +1258,10 @@ export class QueryBuilder {
|
|
|
1123
1258
|
addPropertyJoin(prop, ownerAlias, alias, type, path, schema) {
|
|
1124
1259
|
schema ??= prop.targetMeta?.schema === '*' ? '*' : this.driver.getSchemaName(prop.targetMeta);
|
|
1125
1260
|
const key = `[tpt]${ownerAlias}#${alias}`;
|
|
1126
|
-
this._joins[key] =
|
|
1127
|
-
|
|
1128
|
-
|
|
1261
|
+
this._joins[key] =
|
|
1262
|
+
prop.kind === ReferenceKind.MANY_TO_ONE
|
|
1263
|
+
? this.helper.joinManyToOneReference(prop, ownerAlias, alias, type, {}, schema)
|
|
1264
|
+
: this.helper.joinOneToReference(prop, ownerAlias, alias, type, {}, schema);
|
|
1129
1265
|
this._joins[key].path = path;
|
|
1130
1266
|
return key;
|
|
1131
1267
|
}
|
|
@@ -1172,7 +1308,7 @@ export class QueryBuilder {
|
|
|
1172
1308
|
}
|
|
1173
1309
|
// For TPT inheritance, owning relations (M:1 and owning 1:1) may have FK columns in a parent table
|
|
1174
1310
|
// Resolve the correct alias for the table that owns the FK column
|
|
1175
|
-
const ownerAlias =
|
|
1311
|
+
const ownerAlias = prop.kind === ReferenceKind.MANY_TO_ONE || (prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner)
|
|
1176
1312
|
? this.helper.getTPTAliasForProperty(fromField, fromAlias)
|
|
1177
1313
|
: fromAlias;
|
|
1178
1314
|
this.createAlias(prop.targetMeta.class, alias);
|
|
@@ -1187,7 +1323,7 @@ export class QueryBuilder {
|
|
|
1187
1323
|
const criteriaNode = CriteriaNodeFactory.createNode(this.metadata, prop.targetMeta.class, cond);
|
|
1188
1324
|
cond = criteriaNode.process(this, { ignoreBranching: true, alias });
|
|
1189
1325
|
let aliasedName = `${fromAlias}.${prop.name}#${alias}`;
|
|
1190
|
-
path ??= `${
|
|
1326
|
+
path ??= `${Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? Utils.className(entityName)}.${prop.name}`;
|
|
1191
1327
|
if (prop.kind === ReferenceKind.ONE_TO_MANY) {
|
|
1192
1328
|
this._joins[aliasedName] = this.helper.joinOneToReference(prop, fromAlias, alias, type, cond, schema);
|
|
1193
1329
|
this._joins[aliasedName].path ??= path;
|
|
@@ -1209,7 +1345,8 @@ export class QueryBuilder {
|
|
|
1209
1345
|
this._joins[aliasedName] = this.helper.joinOneToReference(prop, ownerAlias, alias, type, cond, schema);
|
|
1210
1346
|
this._joins[aliasedName].path ??= path;
|
|
1211
1347
|
}
|
|
1212
|
-
else {
|
|
1348
|
+
else {
|
|
1349
|
+
// MANY_TO_ONE
|
|
1213
1350
|
this._joins[aliasedName] = this.helper.joinManyToOneReference(prop, ownerAlias, alias, type, cond, schema);
|
|
1214
1351
|
this._joins[aliasedName].path ??= path;
|
|
1215
1352
|
}
|
|
@@ -1260,7 +1397,9 @@ export class QueryBuilder {
|
|
|
1260
1397
|
}
|
|
1261
1398
|
const nest = (prop) => {
|
|
1262
1399
|
for (const childProp of Object.values(prop.embeddedProps)) {
|
|
1263
|
-
if (childProp.fieldNames &&
|
|
1400
|
+
if (childProp.fieldNames &&
|
|
1401
|
+
(childProp.kind !== ReferenceKind.EMBEDDED || childProp.object) &&
|
|
1402
|
+
childProp.persist !== false) {
|
|
1264
1403
|
ret.push(getFieldName(childProp.fieldNames[0]));
|
|
1265
1404
|
}
|
|
1266
1405
|
else {
|
|
@@ -1311,7 +1450,9 @@ export class QueryBuilder {
|
|
|
1311
1450
|
}
|
|
1312
1451
|
// Start with root alias
|
|
1313
1452
|
let currentAlias = parts[0];
|
|
1314
|
-
let currentMeta = this._aliases[currentAlias]
|
|
1453
|
+
let currentMeta = this._aliases[currentAlias]
|
|
1454
|
+
? this.metadata.get(this._aliases[currentAlias].entityName)
|
|
1455
|
+
: this.mainAlias.meta;
|
|
1315
1456
|
// If first part is not an alias, it's a property of the main entity
|
|
1316
1457
|
if (!this._aliases[currentAlias]) {
|
|
1317
1458
|
currentAlias = this.mainAlias.aliasName;
|
|
@@ -1395,18 +1536,24 @@ export class QueryBuilder {
|
|
|
1395
1536
|
}
|
|
1396
1537
|
getQueryBase(processVirtualEntity) {
|
|
1397
1538
|
const qb = this.platform.createNativeQueryBuilder().setFlags(this.flags);
|
|
1398
|
-
const { subQuery, aliasName, entityName, meta } = this.mainAlias;
|
|
1539
|
+
const { subQuery, aliasName, entityName, meta, rawTableName } = this.mainAlias;
|
|
1399
1540
|
const requiresAlias = this.finalized && (this._explicitAlias || this.helper.isTableNameAliasRequired(this.type));
|
|
1400
1541
|
const alias = requiresAlias ? aliasName : undefined;
|
|
1401
1542
|
const schema = this.getSchema(this.mainAlias);
|
|
1402
|
-
const tableName =
|
|
1543
|
+
const tableName = rawTableName
|
|
1544
|
+
? rawTableName
|
|
1545
|
+
: subQuery instanceof NativeQueryBuilder
|
|
1546
|
+
? subQuery.as(aliasName)
|
|
1547
|
+
: subQuery
|
|
1548
|
+
? raw(`(${subQuery.sql}) as ${this.platform.quoteIdentifier(aliasName)}`, subQuery.params)
|
|
1549
|
+
: this.helper.getTableName(entityName);
|
|
1403
1550
|
const joinSchema = this._schema ?? this.em?.schema ?? schema;
|
|
1404
1551
|
if (meta.virtual && processVirtualEntity) {
|
|
1405
1552
|
qb.from(raw(this.fromVirtual(meta)), { indexHint: this._indexHint });
|
|
1406
1553
|
}
|
|
1407
1554
|
else {
|
|
1408
1555
|
qb.from(tableName, {
|
|
1409
|
-
schema,
|
|
1556
|
+
schema: rawTableName ? undefined : schema,
|
|
1410
1557
|
alias,
|
|
1411
1558
|
indexHint: this._indexHint,
|
|
1412
1559
|
});
|
|
@@ -1460,7 +1607,9 @@ export class QueryBuilder {
|
|
|
1460
1607
|
};
|
|
1461
1608
|
lookUpChildren(children, meta.class);
|
|
1462
1609
|
this.andWhere({
|
|
1463
|
-
[meta.root.discriminatorColumn]: children.length > 0
|
|
1610
|
+
[meta.root.discriminatorColumn]: children.length > 0
|
|
1611
|
+
? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] }
|
|
1612
|
+
: meta.discriminatorValue,
|
|
1464
1613
|
});
|
|
1465
1614
|
}
|
|
1466
1615
|
/**
|
|
@@ -1478,7 +1627,9 @@ export class QueryBuilder {
|
|
|
1478
1627
|
*/
|
|
1479
1628
|
applyTPTJoins() {
|
|
1480
1629
|
const meta = this.mainAlias.meta;
|
|
1481
|
-
if (meta?.inheritanceType !== 'tpt' ||
|
|
1630
|
+
if (meta?.inheritanceType !== 'tpt' ||
|
|
1631
|
+
!meta.tptParent ||
|
|
1632
|
+
![QueryType.SELECT, QueryType.COUNT].includes(this.type)) {
|
|
1482
1633
|
return;
|
|
1483
1634
|
}
|
|
1484
1635
|
if (this.tptJoinsApplied) {
|
|
@@ -1502,7 +1653,9 @@ export class QueryBuilder {
|
|
|
1502
1653
|
*/
|
|
1503
1654
|
addTPTParentFields() {
|
|
1504
1655
|
const meta = this.mainAlias.meta;
|
|
1505
|
-
if (meta?.inheritanceType !== 'tpt' ||
|
|
1656
|
+
if (meta?.inheritanceType !== 'tpt' ||
|
|
1657
|
+
!meta.tptParent ||
|
|
1658
|
+
![QueryType.SELECT, QueryType.COUNT].includes(this.type)) {
|
|
1506
1659
|
return;
|
|
1507
1660
|
}
|
|
1508
1661
|
if (!this._fields?.includes('*') && !this._fields?.includes(`${this.mainAlias.aliasName}.*`)) {
|
|
@@ -1513,8 +1666,8 @@ export class QueryBuilder {
|
|
|
1513
1666
|
const parentAlias = this._tptAlias[parentMeta.className];
|
|
1514
1667
|
if (parentAlias) {
|
|
1515
1668
|
const schema = parentMeta.schema === '*' ? '*' : this.driver.getSchemaName(parentMeta);
|
|
1516
|
-
parentMeta
|
|
1517
|
-
.filter(prop => this.platform.shouldHaveColumn(prop, []))
|
|
1669
|
+
parentMeta
|
|
1670
|
+
.ownProps.filter(prop => this.platform.shouldHaveColumn(prop, []))
|
|
1518
1671
|
.forEach(prop => this._fields.push(...this.driver.mapPropToFieldNames(this, prop, parentAlias, parentMeta, schema)));
|
|
1519
1672
|
}
|
|
1520
1673
|
parentMeta = parentMeta.tptParent;
|
|
@@ -1541,8 +1694,8 @@ export class QueryBuilder {
|
|
|
1541
1694
|
this.addPropertyJoin(childMeta.tptInverseProp, this.mainAlias.aliasName, childAlias, JoinType.leftJoin, `[tpt]${meta.className}`);
|
|
1542
1695
|
// Add child fields
|
|
1543
1696
|
const schema = childMeta.schema === '*' ? '*' : this.driver.getSchemaName(childMeta);
|
|
1544
|
-
childMeta
|
|
1545
|
-
.filter(prop => !prop.primary && this.platform.shouldHaveColumn(prop, []))
|
|
1697
|
+
childMeta
|
|
1698
|
+
.ownProps.filter(prop => !prop.primary && this.platform.shouldHaveColumn(prop, []))
|
|
1546
1699
|
.forEach(prop => this._fields.push(...this.driver.mapPropToFieldNames(this, prop, childAlias, childMeta, schema)));
|
|
1547
1700
|
}
|
|
1548
1701
|
// Add computed discriminator (CASE WHEN to determine concrete type)
|
|
@@ -1593,7 +1746,11 @@ export class QueryBuilder {
|
|
|
1593
1746
|
if (!this.flags.has(QueryFlag.DISABLE_PAGINATE) && this._groupBy.length === 0 && this.hasToManyJoins()) {
|
|
1594
1747
|
this.flags.add(QueryFlag.PAGINATE);
|
|
1595
1748
|
}
|
|
1596
|
-
if (meta &&
|
|
1749
|
+
if (meta &&
|
|
1750
|
+
!meta.virtual &&
|
|
1751
|
+
this.flags.has(QueryFlag.PAGINATE) &&
|
|
1752
|
+
!this.flags.has(QueryFlag.DISABLE_PAGINATE) &&
|
|
1753
|
+
(this._limit > 0 || this._offset > 0)) {
|
|
1597
1754
|
this.wrapPaginateSubQuery(meta);
|
|
1598
1755
|
}
|
|
1599
1756
|
if (meta && (this.flags.has(QueryFlag.UPDATE_SUB_QUERY) || this.flags.has(QueryFlag.DELETE_SUB_QUERY))) {
|
|
@@ -1610,7 +1767,10 @@ export class QueryBuilder {
|
|
|
1610
1767
|
if (meta && this.flags.has(QueryFlag.AUTO_JOIN_ONE_TO_ONE_OWNER)) {
|
|
1611
1768
|
const relationsToPopulate = this._populate.map(({ field }) => field);
|
|
1612
1769
|
meta.relations
|
|
1613
|
-
.filter(prop => prop.kind === ReferenceKind.ONE_TO_ONE &&
|
|
1770
|
+
.filter(prop => prop.kind === ReferenceKind.ONE_TO_ONE &&
|
|
1771
|
+
!prop.owner &&
|
|
1772
|
+
!relationsToPopulate.includes(prop.name) &&
|
|
1773
|
+
!relationsToPopulate.includes(`${prop.name}:ref`))
|
|
1614
1774
|
.map(prop => ({ field: `${prop.name}:ref` }))
|
|
1615
1775
|
.forEach(item => this._populate.push(item));
|
|
1616
1776
|
}
|
|
@@ -1627,7 +1787,8 @@ export class QueryBuilder {
|
|
|
1627
1787
|
const alias = this.getNextAlias(prop.pivotEntity ?? prop.targetMeta.class);
|
|
1628
1788
|
const aliasedName = `${fromAlias}.${prop.name}#${alias}`;
|
|
1629
1789
|
this._joins[aliasedName] = this.helper.joinOneToReference(prop, this.mainAlias.aliasName, alias, JoinType.leftJoin);
|
|
1630
|
-
this._joins[aliasedName].path =
|
|
1790
|
+
this._joins[aliasedName].path =
|
|
1791
|
+
`${Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? meta.className}.${prop.name}`;
|
|
1631
1792
|
this._populateMap[aliasedName] = this._joins[aliasedName].alias;
|
|
1632
1793
|
this.createAlias(prop.targetMeta.class, alias);
|
|
1633
1794
|
}
|
|
@@ -1647,9 +1808,7 @@ export class QueryBuilder {
|
|
|
1647
1808
|
join.cond = { ...join.cond };
|
|
1648
1809
|
}
|
|
1649
1810
|
if (typeof this[key] === 'object') {
|
|
1650
|
-
const cond = CriteriaNodeFactory
|
|
1651
|
-
.createNode(this.metadata, this.mainAlias.entityName, this[key])
|
|
1652
|
-
.process(this, { matchPopulateJoins: true, ignoreBranching: true, preferNoBranch: true, filter });
|
|
1811
|
+
const cond = CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, this[key]).process(this, { matchPopulateJoins: true, ignoreBranching: true, preferNoBranch: true, filter });
|
|
1653
1812
|
// there might be new joins created by processing the `populateWhere` object
|
|
1654
1813
|
joins = Object.values(this._joins);
|
|
1655
1814
|
this.mergeOnConditions(joins, cond, filter);
|
|
@@ -1671,9 +1830,11 @@ export class QueryBuilder {
|
|
|
1671
1830
|
// https://stackoverflow.com/a/56815807/3665878
|
|
1672
1831
|
if (parentJoin && !filter) {
|
|
1673
1832
|
const nested = (parentJoin.nested ??= new Set());
|
|
1674
|
-
join.type =
|
|
1675
|
-
|
|
1676
|
-
|
|
1833
|
+
join.type =
|
|
1834
|
+
join.type === JoinType.innerJoin ||
|
|
1835
|
+
[ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(parentJoin.prop.kind)
|
|
1836
|
+
? JoinType.nestedInnerJoin
|
|
1837
|
+
: JoinType.nestedLeftJoin;
|
|
1677
1838
|
nested.add(join);
|
|
1678
1839
|
}
|
|
1679
1840
|
if (join.cond[k]) {
|
|
@@ -1707,18 +1868,14 @@ export class QueryBuilder {
|
|
|
1707
1868
|
join.parent = joins.find(j => j.alias === join.ownerAlias);
|
|
1708
1869
|
// https://stackoverflow.com/a/56815807/3665878
|
|
1709
1870
|
if (join.parent?.type === JoinType.leftJoin || join.parent?.type === JoinType.nestedLeftJoin) {
|
|
1710
|
-
const nested = (
|
|
1711
|
-
join.type = join.type === JoinType.innerJoin
|
|
1712
|
-
? JoinType.nestedInnerJoin
|
|
1713
|
-
: JoinType.nestedLeftJoin;
|
|
1871
|
+
const nested = (join.parent.nested ??= new Set());
|
|
1872
|
+
join.type = join.type === JoinType.innerJoin ? JoinType.nestedInnerJoin : JoinType.nestedLeftJoin;
|
|
1714
1873
|
nested.add(join);
|
|
1715
1874
|
}
|
|
1716
1875
|
else if (join.parent?.type === JoinType.nestedInnerJoin) {
|
|
1717
1876
|
const group = lookupParentGroup(join.parent);
|
|
1718
|
-
const nested = group ?? (
|
|
1719
|
-
join.type = join.type === JoinType.innerJoin
|
|
1720
|
-
? JoinType.nestedInnerJoin
|
|
1721
|
-
: JoinType.nestedLeftJoin;
|
|
1877
|
+
const nested = group ?? (join.parent.nested ??= new Set());
|
|
1878
|
+
join.type = join.type === JoinType.innerJoin ? JoinType.nestedInnerJoin : JoinType.nestedLeftJoin;
|
|
1722
1879
|
nested.add(join);
|
|
1723
1880
|
}
|
|
1724
1881
|
}
|
|
@@ -1810,7 +1967,9 @@ export class QueryBuilder {
|
|
|
1810
1967
|
// Transfer WHERE conditions to ORDER BY joins (GH #6160)
|
|
1811
1968
|
this.transferConditionsForOrderByJoins(meta, originalCond, populatePaths);
|
|
1812
1969
|
const { sql, params } = subSubQuery.compile();
|
|
1813
|
-
this.select(this._fields).where({
|
|
1970
|
+
this.select(this._fields).where({
|
|
1971
|
+
[Utils.getPrimaryKeyHash(meta.primaryKeys)]: { $in: raw(sql, params) },
|
|
1972
|
+
});
|
|
1814
1973
|
}
|
|
1815
1974
|
/**
|
|
1816
1975
|
* Computes the set of populate paths from the _populate hints.
|
|
@@ -1859,9 +2018,7 @@ export class QueryBuilder {
|
|
|
1859
2018
|
* Removes joins that are not used for population or ordering to improve performance.
|
|
1860
2019
|
*/
|
|
1861
2020
|
pruneJoinsForPagination(meta, populatePaths) {
|
|
1862
|
-
const orderByAliases = this._orderBy
|
|
1863
|
-
.flatMap(hint => Object.keys(hint))
|
|
1864
|
-
.map(k => k.split('.')[0]);
|
|
2021
|
+
const orderByAliases = this._orderBy.flatMap(hint => Object.keys(hint)).map(k => k.split('.')[0]);
|
|
1865
2022
|
const joins = Object.entries(this._joins);
|
|
1866
2023
|
const rootAlias = this.alias;
|
|
1867
2024
|
function addParentAlias(alias) {
|
|
@@ -1946,15 +2103,31 @@ export class QueryBuilder {
|
|
|
1946
2103
|
return this._mainAlias;
|
|
1947
2104
|
}
|
|
1948
2105
|
fromSubQuery(target, aliasName) {
|
|
1949
|
-
const subQuery = target.getNativeQuery();
|
|
1950
2106
|
const { entityName } = target.mainAlias;
|
|
1951
2107
|
aliasName ??= this.getNextAlias(entityName);
|
|
2108
|
+
const subQuery = target._unionQuery ? target.toRaw() : target.getNativeQuery();
|
|
1952
2109
|
this.createMainAlias(entityName, aliasName, subQuery);
|
|
1953
2110
|
}
|
|
1954
2111
|
fromEntityName(entityName, aliasName) {
|
|
1955
2112
|
aliasName ??= this._mainAlias?.aliasName ?? this.getNextAlias(entityName);
|
|
1956
2113
|
this.createMainAlias(entityName, aliasName);
|
|
1957
2114
|
}
|
|
2115
|
+
fromRawTable(tableName, aliasName) {
|
|
2116
|
+
aliasName ??= this._mainAlias?.aliasName ?? this.getNextAlias(tableName);
|
|
2117
|
+
const meta = new EntityMetadata({
|
|
2118
|
+
className: tableName,
|
|
2119
|
+
collection: tableName,
|
|
2120
|
+
});
|
|
2121
|
+
meta.root = meta;
|
|
2122
|
+
this._mainAlias = {
|
|
2123
|
+
aliasName,
|
|
2124
|
+
entityName: tableName,
|
|
2125
|
+
meta,
|
|
2126
|
+
rawTableName: tableName,
|
|
2127
|
+
};
|
|
2128
|
+
this._aliases[aliasName] = this._mainAlias;
|
|
2129
|
+
this._helper = this.createQueryBuilderHelper();
|
|
2130
|
+
}
|
|
1958
2131
|
createQueryBuilderHelper() {
|
|
1959
2132
|
return new QueryBuilderHelper(this.mainAlias.entityName, this.mainAlias.aliasName, this._aliases, this.subQueries, this.driver, this._tptAlias);
|
|
1960
2133
|
}
|
|
@@ -1974,8 +2147,12 @@ export class QueryBuilder {
|
|
|
1974
2147
|
[Symbol.for('nodejs.util.inspect.custom')](depth = 2) {
|
|
1975
2148
|
const object = { ...this };
|
|
1976
2149
|
const hidden = ['metadata', 'driver', 'context', 'platform', 'type'];
|
|
1977
|
-
Object.keys(object)
|
|
1978
|
-
|
|
2150
|
+
Object.keys(object)
|
|
2151
|
+
.filter(k => k.startsWith('_'))
|
|
2152
|
+
.forEach(k => delete object[k]);
|
|
2153
|
+
Object.keys(object)
|
|
2154
|
+
.filter(k => object[k] == null)
|
|
2155
|
+
.forEach(k => delete object[k]);
|
|
1979
2156
|
hidden.forEach(k => delete object[k]);
|
|
1980
2157
|
let prefix = this.type ? this.type.substring(0, 1) + this.type.toLowerCase().substring(1) : '';
|
|
1981
2158
|
if (this._data) {
|
|
@@ -1994,7 +2171,9 @@ export class QueryBuilder {
|
|
|
1994
2171
|
if (!Utils.isEmpty(this._orderBy)) {
|
|
1995
2172
|
object.orderBy = this._orderBy;
|
|
1996
2173
|
}
|
|
1997
|
-
const name = this._mainAlias
|
|
2174
|
+
const name = this._mainAlias
|
|
2175
|
+
? `${prefix}QueryBuilder<${Utils.className(this._mainAlias?.entityName)}>`
|
|
2176
|
+
: 'QueryBuilder';
|
|
1998
2177
|
const ret = inspect(object, { depth });
|
|
1999
2178
|
return ret === '[Object]' ? `[${name}]` : name + ' ' + ret;
|
|
2000
2179
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { type Dictionary, type EntityData, type EntityKey, type EntityMetadata, type EntityName, type EntityProperty, type FilterQuery, type FlatQueryOrderMap, type FormulaTable, LockMode, type QueryOrderMap, Raw, type RawQueryFragmentSymbol } from '@mikro-orm/core';
|
|
1
|
+
import { type Dictionary, type EntityData, type EntityKey, type EntityMetadata, type EntityName, type EntityProperty, type FilterQuery, type FlatQueryOrderMap, type FormulaTable, LockMode, type QueryOrderMap, Raw, type RawQueryFragment, type RawQueryFragmentSymbol } from '@mikro-orm/core';
|
|
2
2
|
import { JoinType, QueryType } from './enums.js';
|
|
3
3
|
import type { InternalField, JoinOptions } from '../typings.js';
|
|
4
4
|
import type { AbstractSqlDriver } from '../AbstractSqlDriver.js';
|
|
5
|
-
import { NativeQueryBuilder } from './NativeQueryBuilder.js';
|
|
5
|
+
import type { NativeQueryBuilder } from './NativeQueryBuilder.js';
|
|
6
6
|
/**
|
|
7
7
|
* @internal
|
|
8
8
|
*/
|
|
@@ -71,7 +71,8 @@ export interface Alias<T> {
|
|
|
71
71
|
aliasName: string;
|
|
72
72
|
entityName: EntityName<T>;
|
|
73
73
|
meta: EntityMetadata<T>;
|
|
74
|
-
subQuery?: NativeQueryBuilder;
|
|
74
|
+
subQuery?: NativeQueryBuilder | RawQueryFragment;
|
|
75
|
+
rawTableName?: string;
|
|
75
76
|
}
|
|
76
77
|
export interface OnConflictClause<T> {
|
|
77
78
|
fields: string[] | Raw;
|