@mikro-orm/sql 7.1.15-dev.8 → 7.1.15
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/AbstractSqlDriver.d.ts +0 -7
- package/AbstractSqlDriver.js +41 -31
- package/package.json +3 -3
- package/query/QueryBuilder.d.ts +7 -0
- package/query/QueryBuilder.js +29 -1
- package/query/QueryBuilderHelper.js +1 -1
package/AbstractSqlDriver.d.ts
CHANGED
|
@@ -118,13 +118,6 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
|
|
|
118
118
|
mergeJoinedResult<T extends object>(rawResults: EntityData<T>[], meta: EntityMetadata<T>, joinedProps: PopulateOptions<T>[]): EntityData<T>[];
|
|
119
119
|
protected shouldHaveColumn<T, U>(meta: EntityMetadata<T>, prop: EntityProperty<U>, populate: readonly PopulateOptions<U>[], fields?: readonly InternalField<U>[], exclude?: readonly InternalField<U>[]): boolean;
|
|
120
120
|
protected getFieldsForJoinedLoad<T extends object>(qb: AnyQueryBuilder<T>, meta: EntityMetadata<T>, options: FieldsForJoinedLoadOptions<T>): InternalField<T>[];
|
|
121
|
-
/**
|
|
122
|
-
* Walks the TPT inheritance chain of `leafMeta` and INNER JOINs each parent table.
|
|
123
|
-
* Registers the parent aliases in `qb.state.tptAlias` so column resolution finds them
|
|
124
|
-
* when filter conditions reference parent-table columns.
|
|
125
|
-
* @internal
|
|
126
|
-
*/
|
|
127
|
-
protected addTPTParentJoinsForRelation<T extends object>(qb: AnyQueryBuilder<T>, leafMeta: EntityMetadata, leafAlias: string, basePath: string): void;
|
|
128
121
|
/**
|
|
129
122
|
* Adds LEFT JOINs and fields for TPT polymorphic loading when populating a relation to a TPT base class.
|
|
130
123
|
* @internal
|
package/AbstractSqlDriver.js
CHANGED
|
@@ -915,7 +915,11 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
915
915
|
if (!options.upsert && options.unionWhere?.length) {
|
|
916
916
|
where = (await this.applyUnionWhere(meta, where, options, true));
|
|
917
917
|
}
|
|
918
|
-
if (
|
|
918
|
+
if (options.upsert && meta.tptParent) {
|
|
919
|
+
res = await this.nativeUpdateMany(entityName, [where], [data], options);
|
|
920
|
+
}
|
|
921
|
+
else if (Utils.hasObjectKeys(data) || (meta.inheritanceType === 'tpt' && meta.ownsVersionProperty())) {
|
|
922
|
+
// a TPT table declaring the version property is bumped even when only other tables of the hierarchy changed
|
|
919
923
|
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes, options.loggerContext).withSchema(this.getSchemaName(meta, options));
|
|
920
924
|
qb.setAbortOptions(pickAbortOptions(options));
|
|
921
925
|
if (options.upsert) {
|
|
@@ -941,7 +945,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
941
945
|
qb.update(data).where(where);
|
|
942
946
|
// reload generated columns and version fields
|
|
943
947
|
const returning = [];
|
|
944
|
-
meta
|
|
948
|
+
this.getTableProps(meta)
|
|
945
949
|
.filter(prop => (prop.generated && !prop.primary) || prop.version)
|
|
946
950
|
.forEach(prop => returning.push(prop.name));
|
|
947
951
|
qb.returning(returning);
|
|
@@ -957,13 +961,38 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
957
961
|
options.convertCustomTypes ??= true;
|
|
958
962
|
const meta = this.metadata.get(entityName);
|
|
959
963
|
if (options.upsert) {
|
|
964
|
+
if (meta.tptParent) {
|
|
965
|
+
// TPT parent tables go first, the PK they provide is the conflict target of this table
|
|
966
|
+
await this.nativeUpdateMany(meta.tptParent.class, where, data, options);
|
|
967
|
+
for (const [i, row] of data.entries()) {
|
|
968
|
+
if (meta.primaryKeys.some(pk => row[pk] == null)) {
|
|
969
|
+
const found = await this.findOne(meta.tptParent.class, where[i], {
|
|
970
|
+
fields: meta.primaryKeys,
|
|
971
|
+
ctx: options.ctx,
|
|
972
|
+
connectionType: 'write',
|
|
973
|
+
schema: options.schema,
|
|
974
|
+
});
|
|
975
|
+
meta.primaryKeys.forEach(pk => (row[pk] = found?.[pk]));
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
options = { ...options, onConflictFields: meta.primaryKeys, onConflictWhere: undefined };
|
|
979
|
+
}
|
|
960
980
|
const uniqueFields = options.onConflictFields ??
|
|
961
981
|
(Utils.isPlainObject(where[0])
|
|
962
982
|
? Object.keys(where[0]).flatMap(key => Utils.splitPrimaryKeys(key))
|
|
963
983
|
: meta.primaryKeys);
|
|
964
984
|
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes, options.loggerContext).withSchema(this.getSchemaName(meta, options));
|
|
965
985
|
qb.setAbortOptions(pickAbortOptions(options));
|
|
966
|
-
|
|
986
|
+
let returning = getOnConflictReturningFields(meta, data[0], uniqueFields, options);
|
|
987
|
+
if (meta.inheritanceType === 'tpt') {
|
|
988
|
+
// each TPT table only carries its own columns, the entity is reloaded instead of mapping the returned rows
|
|
989
|
+
const own = (key) => meta.primaryKeys.includes(key) ||
|
|
990
|
+
this.getTableProps(meta).some(prop => prop.name === key.split('.')[0]);
|
|
991
|
+
data = data.map(row => Object.fromEntries(Object.entries(row).filter(([key]) => own(key))));
|
|
992
|
+
options.onConflictMergeFields = options.onConflictMergeFields?.filter(f => own(f));
|
|
993
|
+
options.onConflictExcludeFields = options.onConflictExcludeFields?.filter(f => own(f));
|
|
994
|
+
returning = [];
|
|
995
|
+
}
|
|
967
996
|
qb.insert(data)
|
|
968
997
|
.onConflict(uniqueFields)
|
|
969
998
|
.returning(returning);
|
|
@@ -977,7 +1006,8 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
977
1006
|
if (options.onConflictWhere) {
|
|
978
1007
|
qb.where(options.onConflictWhere);
|
|
979
1008
|
}
|
|
980
|
-
|
|
1009
|
+
const res = await this.rethrow(qb.execute('run', false));
|
|
1010
|
+
return meta.inheritanceType === 'tpt' ? { ...res, row: undefined, rows: [] } : res;
|
|
981
1011
|
}
|
|
982
1012
|
const collections = options.processCollections ? data.map(d => this.extractManyToMany(meta, d)) : [];
|
|
983
1013
|
const keys = new Set();
|
|
@@ -992,7 +1022,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
992
1022
|
}
|
|
993
1023
|
}
|
|
994
1024
|
// reload generated columns and version fields
|
|
995
|
-
meta.
|
|
1025
|
+
meta.getPrimaryProps().forEach(prop => returning.add(prop.name));
|
|
1026
|
+
this.getTableProps(meta)
|
|
1027
|
+
.filter(prop => prop.generated || prop.version)
|
|
1028
|
+
.forEach(prop => returning.add(prop.name));
|
|
996
1029
|
const pkCond = Utils.flatten(meta.primaryKeys.map(pk => meta.properties[pk].fieldNames))
|
|
997
1030
|
.map(pk => `${this.platform.quoteIdentifier(pk)} = ?`)
|
|
998
1031
|
.join(' and ');
|
|
@@ -1050,7 +1083,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1050
1083
|
return sql;
|
|
1051
1084
|
});
|
|
1052
1085
|
}
|
|
1053
|
-
if (meta.
|
|
1086
|
+
if (meta.ownsVersionProperty()) {
|
|
1054
1087
|
const versionProperty = meta.properties[meta.versionProperty];
|
|
1055
1088
|
const quotedFieldName = this.platform.quoteIdentifier(versionProperty.fieldNames[0]);
|
|
1056
1089
|
sql += `${quotedFieldName} = `;
|
|
@@ -1063,7 +1096,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1063
1096
|
sql += `, `;
|
|
1064
1097
|
}
|
|
1065
1098
|
sql = sql.substring(0, sql.length - 2) + ' where ';
|
|
1066
|
-
const pkProps = meta.primaryKeys.concat(...meta.
|
|
1099
|
+
const pkProps = meta.primaryKeys.concat(...meta.getOwnConcurrencyCheckKeys());
|
|
1067
1100
|
const pks = Utils.flatten(pkProps.map(pk => meta.properties[pk].fieldNames));
|
|
1068
1101
|
const useTupleIn = pks.length <= 1 || this.platform.allowsComparingTuples();
|
|
1069
1102
|
const condTemplate = useTupleIn
|
|
@@ -1784,7 +1817,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1784
1817
|
// INNER JOINs get nested inside the polymorphic LEFT JOIN by processNestedJoins, which
|
|
1785
1818
|
// keeps the resulting query valid for rows pointing to other polymorphic targets.
|
|
1786
1819
|
if (targetMeta.inheritanceType === 'tpt' && targetMeta.tptParent) {
|
|
1787
|
-
|
|
1820
|
+
qb.addTPTParentJoins(targetMeta, tableAlias, targetPath);
|
|
1788
1821
|
}
|
|
1789
1822
|
// For polymorphic targets that are TPT base classes, also LEFT JOIN
|
|
1790
1823
|
// all descendant tables so child-specific fields can be selected.
|
|
@@ -1832,10 +1865,6 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1832
1865
|
: JoinType.leftJoin;
|
|
1833
1866
|
const schema = prop.targetMeta.schema === '*' ? (options?.schema ?? this.config.get('schema')) : prop.targetMeta.schema;
|
|
1834
1867
|
qb.join(field, tableAlias, {}, joinType, path, schema);
|
|
1835
|
-
// For relations to TPT child entities, INNER JOIN parent tables (GH #7469)
|
|
1836
|
-
if (meta2.inheritanceType === 'tpt' && meta2.tptParent) {
|
|
1837
|
-
this.addTPTParentJoinsForRelation(qb, meta2, tableAlias, path);
|
|
1838
|
-
}
|
|
1839
1868
|
// For relations to TPT base classes, add LEFT JOINs for all child tables (polymorphic loading)
|
|
1840
1869
|
if (meta2.inheritanceType === 'tpt' && meta2.tptChildren?.length && !ref) {
|
|
1841
1870
|
// Use the registry metadata to ensure allTPTDescendants is available
|
|
@@ -1882,25 +1911,6 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1882
1911
|
}
|
|
1883
1912
|
return fields;
|
|
1884
1913
|
}
|
|
1885
|
-
/**
|
|
1886
|
-
* Walks the TPT inheritance chain of `leafMeta` and INNER JOINs each parent table.
|
|
1887
|
-
* Registers the parent aliases in `qb.state.tptAlias` so column resolution finds them
|
|
1888
|
-
* when filter conditions reference parent-table columns.
|
|
1889
|
-
* @internal
|
|
1890
|
-
*/
|
|
1891
|
-
addTPTParentJoinsForRelation(qb, leafMeta, leafAlias, basePath) {
|
|
1892
|
-
let childAlias = leafAlias;
|
|
1893
|
-
let childMeta = leafMeta;
|
|
1894
|
-
while (childMeta.tptParent) {
|
|
1895
|
-
const parentMeta = childMeta.tptParent;
|
|
1896
|
-
const parentAlias = qb.getNextAlias(parentMeta.className);
|
|
1897
|
-
qb.createAlias(parentMeta.class, parentAlias);
|
|
1898
|
-
qb.state.tptAlias[`${leafAlias}:${parentMeta.className}`] = parentAlias;
|
|
1899
|
-
qb.addPropertyJoin(childMeta.tptParentProp, childAlias, parentAlias, JoinType.innerJoin, `${basePath}.[tpt]${childMeta.className}`);
|
|
1900
|
-
childAlias = parentAlias;
|
|
1901
|
-
childMeta = parentMeta;
|
|
1902
|
-
}
|
|
1903
|
-
}
|
|
1904
1914
|
/**
|
|
1905
1915
|
* Adds LEFT JOINs and fields for TPT polymorphic loading when populating a relation to a TPT base class.
|
|
1906
1916
|
* @internal
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/sql",
|
|
3
|
-
"version": "7.1.15
|
|
3
|
+
"version": "7.1.15",
|
|
4
4
|
"description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"data-mapper",
|
|
@@ -50,10 +50,10 @@
|
|
|
50
50
|
"kysely": "0.29.5"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@mikro-orm/core": "^7.1.
|
|
53
|
+
"@mikro-orm/core": "^7.1.15"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@mikro-orm/core": "7.1.15
|
|
56
|
+
"@mikro-orm/core": "7.1.15"
|
|
57
57
|
},
|
|
58
58
|
"engines": {
|
|
59
59
|
"node": ">= 22.17.0"
|
package/query/QueryBuilder.d.ts
CHANGED
|
@@ -950,6 +950,13 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
|
|
|
950
950
|
*/
|
|
951
951
|
addPropertyJoin(prop: EntityProperty, ownerAlias: string, alias: string, type: JoinType, path: string, schema?: string): string;
|
|
952
952
|
private joinReference;
|
|
953
|
+
/**
|
|
954
|
+
* Walks the TPT inheritance chain of `leafMeta` and INNER JOINs each parent table.
|
|
955
|
+
* Registers the parent aliases in `state.tptAlias` so column resolution finds them
|
|
956
|
+
* when conditions reference parent-table columns.
|
|
957
|
+
* @internal
|
|
958
|
+
*/
|
|
959
|
+
addTPTParentJoins(leafMeta: EntityMetadata, leafAlias: string, basePath: string): void;
|
|
953
960
|
protected prepareFields<T>(fields: InternalField<T>[], type?: 'where' | 'groupBy' | 'sub-query', schema?: string): (string | RawQueryFragment)[];
|
|
954
961
|
/**
|
|
955
962
|
* Resolves nested paths like `a.books.title` to their actual field references.
|
package/query/QueryBuilder.js
CHANGED
|
@@ -495,7 +495,10 @@ export class QueryBuilder {
|
|
|
495
495
|
return cond.some(c => this.condReferencesAlias(c, alias));
|
|
496
496
|
}
|
|
497
497
|
if (Utils.isPlainObject(cond)) {
|
|
498
|
-
return Object.entries(cond).some(([key, value]) =>
|
|
498
|
+
return Object.entries(cond).some(([key, value]) => {
|
|
499
|
+
const [keyAlias, field] = this.helper.splitField(key);
|
|
500
|
+
return this.helper.getTPTAliasForProperty(field, keyAlias) === alias || this.condReferencesAlias(value, alias);
|
|
501
|
+
});
|
|
499
502
|
}
|
|
500
503
|
return false;
|
|
501
504
|
}
|
|
@@ -904,6 +907,8 @@ export class QueryBuilder {
|
|
|
904
907
|
if (this.type === QueryType.INSERT) {
|
|
905
908
|
const returningProps = meta.hydrateProps
|
|
906
909
|
.filter(prop => prop.returning || (prop.persist !== false && ((prop.primary && prop.autoincrement) || prop.defaultRaw)))
|
|
910
|
+
// a TPT table can only return its own columns
|
|
911
|
+
.filter(prop => meta.inheritanceType !== 'tpt' || prop.primary || meta.ownProps.some(p => p.name === prop.name))
|
|
907
912
|
.filter(prop => !data || !(prop.name in data));
|
|
908
913
|
if (returningProps.length > 0) {
|
|
909
914
|
qb.returning(Utils.flatten(returningProps.map(prop => prop.fieldNames)));
|
|
@@ -1493,9 +1498,31 @@ export class QueryBuilder {
|
|
|
1493
1498
|
this.#state.joins[aliasedName] = this.helper.joinManyToOneReference(prop, ownerAlias, alias, type, cond, schema);
|
|
1494
1499
|
this.#state.joins[aliasedName].path ??= path;
|
|
1495
1500
|
}
|
|
1501
|
+
if (prop.targetMeta.inheritanceType === 'tpt' && prop.targetMeta.tptParent) {
|
|
1502
|
+
this.addTPTParentJoins(prop.targetMeta, alias, path);
|
|
1503
|
+
}
|
|
1496
1504
|
this.nestReferencedJoins(this.#state.joins[aliasedName]);
|
|
1497
1505
|
return { prop, key: aliasedName };
|
|
1498
1506
|
}
|
|
1507
|
+
/**
|
|
1508
|
+
* Walks the TPT inheritance chain of `leafMeta` and INNER JOINs each parent table.
|
|
1509
|
+
* Registers the parent aliases in `state.tptAlias` so column resolution finds them
|
|
1510
|
+
* when conditions reference parent-table columns.
|
|
1511
|
+
* @internal
|
|
1512
|
+
*/
|
|
1513
|
+
addTPTParentJoins(leafMeta, leafAlias, basePath) {
|
|
1514
|
+
let childAlias = leafAlias;
|
|
1515
|
+
let childMeta = leafMeta;
|
|
1516
|
+
while (childMeta.tptParent) {
|
|
1517
|
+
const parentMeta = childMeta.tptParent;
|
|
1518
|
+
const parentAlias = this.getNextAlias(parentMeta.className);
|
|
1519
|
+
this.createAlias(parentMeta.class, parentAlias);
|
|
1520
|
+
this.#state.tptAlias[`${leafAlias}:${parentMeta.className}`] = parentAlias;
|
|
1521
|
+
this.addPropertyJoin(childMeta.tptParentProp, childAlias, parentAlias, JoinType.innerJoin, `${basePath}.[tpt]${childMeta.className}`);
|
|
1522
|
+
childAlias = parentAlias;
|
|
1523
|
+
childMeta = parentMeta;
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1499
1526
|
prepareFields(fields, type = 'where', schema) {
|
|
1500
1527
|
const ret = [];
|
|
1501
1528
|
const getFieldName = (name, customAlias) => {
|
|
@@ -2085,6 +2112,7 @@ export class QueryBuilder {
|
|
|
2085
2112
|
else {
|
|
2086
2113
|
join.cond = { ...join.cond, [k]: cond[k] };
|
|
2087
2114
|
}
|
|
2115
|
+
this.nestReferencedJoins(join);
|
|
2088
2116
|
}
|
|
2089
2117
|
}
|
|
2090
2118
|
}
|
|
@@ -763,7 +763,7 @@ export class QueryBuilderHelper {
|
|
|
763
763
|
}
|
|
764
764
|
updateVersionProperty(qb, data) {
|
|
765
765
|
const meta = this.#metadata.find(this.#entityName);
|
|
766
|
-
if (!meta?.
|
|
766
|
+
if (!meta?.ownsVersionProperty() || meta.versionProperty in data) {
|
|
767
767
|
return;
|
|
768
768
|
}
|
|
769
769
|
const versionProperty = meta.properties[meta.versionProperty];
|