@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.
Files changed (38) hide show
  1. package/AbstractSqlConnection.js +2 -1
  2. package/AbstractSqlDriver.d.ts +18 -12
  3. package/AbstractSqlDriver.js +187 -38
  4. package/AbstractSqlPlatform.d.ts +1 -0
  5. package/AbstractSqlPlatform.js +5 -3
  6. package/PivotCollectionPersister.js +2 -2
  7. package/SqlEntityManager.d.ts +2 -2
  8. package/SqlEntityManager.js +5 -5
  9. package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +2 -0
  10. package/dialects/mssql/MsSqlNativeQueryBuilder.js +8 -4
  11. package/dialects/mysql/BaseMySqlPlatform.js +1 -2
  12. package/dialects/mysql/MySqlSchemaHelper.js +21 -10
  13. package/dialects/postgresql/BasePostgreSqlPlatform.js +38 -30
  14. package/dialects/postgresql/PostgreSqlSchemaHelper.js +63 -47
  15. package/dialects/sqlite/BaseSqliteConnection.js +2 -2
  16. package/dialects/sqlite/NodeSqliteDialect.js +3 -1
  17. package/dialects/sqlite/SqlitePlatform.js +4 -1
  18. package/dialects/sqlite/SqliteSchemaHelper.js +11 -9
  19. package/package.json +30 -30
  20. package/plugin/transformer.js +17 -16
  21. package/query/CriteriaNode.js +28 -10
  22. package/query/CriteriaNodeFactory.js +5 -1
  23. package/query/NativeQueryBuilder.d.ts +25 -0
  24. package/query/NativeQueryBuilder.js +61 -1
  25. package/query/ObjectCriteriaNode.js +71 -27
  26. package/query/QueryBuilder.d.ts +151 -48
  27. package/query/QueryBuilder.js +233 -54
  28. package/query/QueryBuilderHelper.d.ts +4 -3
  29. package/query/QueryBuilderHelper.js +47 -17
  30. package/query/ScalarCriteriaNode.js +14 -7
  31. package/query/raw.js +1 -1
  32. package/schema/DatabaseSchema.js +21 -15
  33. package/schema/DatabaseTable.js +114 -54
  34. package/schema/SchemaComparator.js +56 -32
  35. package/schema/SchemaHelper.js +28 -8
  36. package/schema/SqlSchemaGenerator.js +13 -7
  37. package/tsconfig.build.tsbuildinfo +1 -1
  38. package/typings.d.ts +6 -4
@@ -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 { // root entity
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]) && Object.keys(cond[key]).every(k => !(Utils.isOperator(k) && !['$some', '$none', '$every', '$size'].includes(k)))) {
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) && criteriaNode.willAutoJoin(this, undefined, { ignoreBranching })) {
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, ['qb.execute', query.sql, query.params, method]);
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()).limit(undefined).offset(undefined).orderBy([]);
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', '_populate', '_populateWhere', '_populateFilter', '__populateWhere', '_populateMap', '_joins', '_joinedProps', '_cond', '_data', '_orderBy',
1068
- '_schema', '_indexHint', '_collation', '_cache', 'subQueries', 'lockMode', 'lockTables', '_groupBy', '_having', '_returning',
1069
- '_comments', '_hintComments', 'aliasCounter',
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) && (reset === true || reset.includes(prop) || ['_helper', '_query'].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] = prop.kind === ReferenceKind.MANY_TO_ONE
1127
- ? this.helper.joinManyToOneReference(prop, ownerAlias, alias, type, {}, schema)
1128
- : this.helper.joinOneToReference(prop, ownerAlias, alias, type, {}, schema);
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 = (prop.kind === ReferenceKind.MANY_TO_ONE || (prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner))
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 ??= `${(Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? Utils.className(entityName))}.${prop.name}`;
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 { // MANY_TO_ONE
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 && (childProp.kind !== ReferenceKind.EMBEDDED || childProp.object) && childProp.persist !== false) {
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] ? this.metadata.get(this._aliases[currentAlias].entityName) : this.mainAlias.meta;
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 = subQuery ? subQuery.as(aliasName) : this.helper.getTableName(entityName);
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 ? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] } : meta.discriminatorValue,
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' || !meta.tptParent || ![QueryType.SELECT, QueryType.COUNT].includes(this.type)) {
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' || !meta.tptParent || ![QueryType.SELECT, QueryType.COUNT].includes(this.type)) {
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.ownProps
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.ownProps
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 && !meta.virtual && this.flags.has(QueryFlag.PAGINATE) && !this.flags.has(QueryFlag.DISABLE_PAGINATE) && (this._limit > 0 || this._offset > 0)) {
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 && !prop.owner && !relationsToPopulate.includes(prop.name) && !relationsToPopulate.includes(`${prop.name}:ref`))
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 = `${(Object.values(this._joins).find(j => j.alias === fromAlias)?.path ?? meta.className)}.${prop.name}`;
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 = join.type === JoinType.innerJoin || ([ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(parentJoin.prop.kind))
1675
- ? JoinType.nestedInnerJoin
1676
- : JoinType.nestedLeftJoin;
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 = ((join.parent).nested ??= new Set());
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 ?? ((join.parent).nested ??= new Set());
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({ [Utils.getPrimaryKeyHash(meta.primaryKeys)]: { $in: raw(sql, params) } });
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).filter(k => k.startsWith('_')).forEach(k => delete object[k]);
1978
- Object.keys(object).filter(k => object[k] == null).forEach(k => delete object[k]);
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 ? `${prefix}QueryBuilder<${Utils.className(this._mainAlias?.entityName)}>` : 'QueryBuilder';
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;