@mikro-orm/sql 7.2.5-dev.1 → 7.2.5-dev.11
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
CHANGED
|
@@ -127,6 +127,12 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
|
|
|
127
127
|
* @internal
|
|
128
128
|
*/
|
|
129
129
|
protected addTPTPolymorphicJoinsForRelation<T extends object>(qb: AnyQueryBuilder<T>, meta: EntityMetadata<T>, baseAlias: string, fields: InternalField<T>[]): void;
|
|
130
|
+
/**
|
|
131
|
+
* Registers the ancestor table aliases of each polymorphically joined TPT descendant,
|
|
132
|
+
* so formulas on descendants resolve inherited columns to the table that owns them.
|
|
133
|
+
* @internal
|
|
134
|
+
*/
|
|
135
|
+
registerTPTAncestorAliases(qb: AnyQueryBuilder<any>, meta: EntityMetadata, baseAlias: string, childAliases: Dictionary<string>): void;
|
|
130
136
|
/**
|
|
131
137
|
* Find the alias for a TPT child table in the query builder.
|
|
132
138
|
* @internal
|
package/AbstractSqlDriver.js
CHANGED
|
@@ -853,10 +853,16 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
853
853
|
// the map misses subtypes whose props were not initialized yet during discovery (root column owner, grandchildren)
|
|
854
854
|
const discriminator = row[meta.discriminatorColumn];
|
|
855
855
|
const childClass = meta.discriminatorMap?.[discriminator];
|
|
856
|
-
const
|
|
857
|
-
|
|
856
|
+
const childProp = childClass && this.metadata.find(childClass)?.properties[prop.name];
|
|
857
|
+
const activeField = prop.stiFieldNameMap[discriminator] ?? childProp?.fieldNames[0];
|
|
858
858
|
for (const field of prop.stiFieldNames) {
|
|
859
|
-
|
|
859
|
+
// the subtype's own property, as siblings can declare it with a different type or embeddable
|
|
860
|
+
if (field === activeField) {
|
|
861
|
+
addParams(childProp ?? prop, row);
|
|
862
|
+
}
|
|
863
|
+
else {
|
|
864
|
+
params.push(null);
|
|
865
|
+
}
|
|
860
866
|
keys.push('?');
|
|
861
867
|
}
|
|
862
868
|
return;
|
|
@@ -1989,6 +1995,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1989
1995
|
qb.createAlias(childMeta.class, childAlias);
|
|
1990
1996
|
childAliases[childMeta.className] = childAlias;
|
|
1991
1997
|
qb.addPropertyJoin(childMeta.tptInverseProp, baseAlias, childAlias, JoinType.leftJoin, `[tpt]${meta.className}`);
|
|
1998
|
+
}
|
|
1999
|
+
this.registerTPTAncestorAliases(qb, meta, baseAlias, childAliases);
|
|
2000
|
+
for (const childMeta of descendants) {
|
|
2001
|
+
const childAlias = childAliases[childMeta.className];
|
|
1992
2002
|
// Add fields from this child (only ownProps, skip PKs)
|
|
1993
2003
|
const schema = childMeta.schema === '*' ? '*' : this.getSchemaName(childMeta);
|
|
1994
2004
|
childMeta
|
|
@@ -2000,6 +2010,21 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
2000
2010
|
fields.push(this.buildTPTDiscriminatorExpression(meta, descendants, childAliases, baseAlias));
|
|
2001
2011
|
}
|
|
2002
2012
|
}
|
|
2013
|
+
/**
|
|
2014
|
+
* Registers the ancestor table aliases of each polymorphically joined TPT descendant,
|
|
2015
|
+
* so formulas on descendants resolve inherited columns to the table that owns them.
|
|
2016
|
+
* @internal
|
|
2017
|
+
*/
|
|
2018
|
+
registerTPTAncestorAliases(qb, meta, baseAlias, childAliases) {
|
|
2019
|
+
const tptAlias = qb.state.tptAlias;
|
|
2020
|
+
for (const childMeta of meta.allTPTDescendants) {
|
|
2021
|
+
for (let parent = childMeta.tptParent; parent; parent = parent.tptParent) {
|
|
2022
|
+
tptAlias[`${childAliases[childMeta.className]}:${parent.className}`] =
|
|
2023
|
+
childAliases[parent.className] ??
|
|
2024
|
+
(parent === meta ? baseAlias : tptAlias[`${baseAlias}:${parent.className}`]);
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2003
2028
|
/**
|
|
2004
2029
|
* Find the alias for a TPT child table in the query builder.
|
|
2005
2030
|
* @internal
|
|
@@ -2087,6 +2112,12 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
2087
2112
|
}
|
|
2088
2113
|
const aliased = this.platform.quoteIdentifier(`${tableAlias}__${prop.fieldNames[0]}`);
|
|
2089
2114
|
const sourceAlias = qb.helper.getTPTAliasForProperty(prop.name, tableAlias);
|
|
2115
|
+
if (prop.formula) {
|
|
2116
|
+
const quotedAlias = this.platform.quoteIdentifier(tableAlias).toString();
|
|
2117
|
+
const table = this.createFormulaTable(quotedAlias, meta, schema);
|
|
2118
|
+
const columns = meta.createColumnMappingObject(p => qb.helper.getTPTAliasForProperty(p.name, tableAlias), tableAlias);
|
|
2119
|
+
return [raw(`${this.evaluateFormula(prop.formula, columns, table)} as ${aliased}`)];
|
|
2120
|
+
}
|
|
2090
2121
|
if (prop.customTypes?.some(type => !!type?.convertToJSValueSQL)) {
|
|
2091
2122
|
return prop.fieldNames.map((col, idx) => {
|
|
2092
2123
|
if (!prop.customTypes[idx]?.convertToJSValueSQL) {
|
|
@@ -2101,12 +2132,6 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
2101
2132
|
const prefixed = this.platform.quoteIdentifier(`${sourceAlias}.${prop.fieldNames[0]}`);
|
|
2102
2133
|
return [raw(`${prop.customType.convertToJSValueSQL(prefixed, this.platform)} as ${aliased}`)];
|
|
2103
2134
|
}
|
|
2104
|
-
if (prop.formula) {
|
|
2105
|
-
const quotedAlias = this.platform.quoteIdentifier(tableAlias).toString();
|
|
2106
|
-
const table = this.createFormulaTable(quotedAlias, meta, schema);
|
|
2107
|
-
const columns = meta.createColumnMappingObject(tableAlias);
|
|
2108
|
-
return [raw(`${this.evaluateFormula(prop.formula, columns, table)} as ${aliased}`)];
|
|
2109
|
-
}
|
|
2110
2135
|
return prop.fieldNames.map(fieldName => {
|
|
2111
2136
|
return raw('?? as ??', [`${sourceAlias}.${fieldName}`, `${tableAlias}__${fieldName}`]);
|
|
2112
2137
|
});
|
|
@@ -2527,7 +2552,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
2527
2552
|
const table = this.createFormulaTable(quotedAlias.toString(), meta, effectiveSchema);
|
|
2528
2553
|
ret.push(raw(`${this.evaluateFormula(prop.formula, columns, table)} as ${aliased}`));
|
|
2529
2554
|
}
|
|
2530
|
-
if (!prop.object && (prop.hasConvertToDatabaseValueSQL || prop.hasConvertToJSValueSQL)) {
|
|
2555
|
+
if (!prop.object && !prop.formula && (prop.hasConvertToDatabaseValueSQL || prop.hasConvertToJSValueSQL)) {
|
|
2531
2556
|
ret.push(prop.name);
|
|
2532
2557
|
}
|
|
2533
2558
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/sql",
|
|
3
|
-
"version": "7.2.5-dev.
|
|
3
|
+
"version": "7.2.5-dev.11",
|
|
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",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"@mikro-orm/core": "^7.2.4"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@mikro-orm/core": "7.2.5-dev.
|
|
56
|
+
"@mikro-orm/core": "7.2.5-dev.11"
|
|
57
57
|
},
|
|
58
58
|
"engines": {
|
|
59
59
|
"node": ">= 22.17.0"
|
package/query/QueryBuilder.js
CHANGED
|
@@ -1873,20 +1873,7 @@ export class QueryBuilder {
|
|
|
1873
1873
|
if (meta.root.inheritanceType !== 'sti' || !meta.discriminatorValue) {
|
|
1874
1874
|
return;
|
|
1875
1875
|
}
|
|
1876
|
-
|
|
1877
|
-
const children = [];
|
|
1878
|
-
const lookUpChildren = (ret, parent) => {
|
|
1879
|
-
const children = types.filter(meta2 => meta2.extends && this.metadata.find(meta2.extends) === parent);
|
|
1880
|
-
children.forEach(m => lookUpChildren(ret, m));
|
|
1881
|
-
ret.push(...children.filter(c => c.discriminatorValue));
|
|
1882
|
-
return children;
|
|
1883
|
-
};
|
|
1884
|
-
lookUpChildren(children, meta);
|
|
1885
|
-
this.andWhere({
|
|
1886
|
-
[meta.root.discriminatorColumn]: children.length > 0
|
|
1887
|
-
? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] }
|
|
1888
|
-
: meta.discriminatorValue,
|
|
1889
|
-
});
|
|
1876
|
+
this.andWhere({ [meta.root.discriminatorColumn]: this.helper.getDiscriminatorCondition(meta) });
|
|
1890
1877
|
}
|
|
1891
1878
|
/**
|
|
1892
1879
|
* Ensures TPT joins are applied. Can be called early before finalize() to populate
|
|
@@ -1943,7 +1930,8 @@ export class QueryBuilder {
|
|
|
1943
1930
|
if (parentAlias) {
|
|
1944
1931
|
const schema = parentMeta.schema === '*' ? '*' : this.driver.getSchemaName(parentMeta);
|
|
1945
1932
|
parentMeta
|
|
1946
|
-
|
|
1933
|
+
// inherited formulas are selected on the main alias in `finalize()`
|
|
1934
|
+
.ownProps.filter(prop => !prop.formula && this.platform.shouldHaveColumn(prop, []))
|
|
1947
1935
|
.forEach(prop => this.#state.fields.push(...this.driver.mapPropToFieldNames(this, prop, parentAlias, parentMeta, schema)));
|
|
1948
1936
|
}
|
|
1949
1937
|
parentMeta = parentMeta.tptParent;
|
|
@@ -1962,12 +1950,16 @@ export class QueryBuilder {
|
|
|
1962
1950
|
if (!this.#state.fields?.includes('*') && !this.#state.fields?.includes(`${this.mainAlias.aliasName}.*`)) {
|
|
1963
1951
|
return;
|
|
1964
1952
|
}
|
|
1965
|
-
// LEFT JOIN each descendant table
|
|
1953
|
+
// LEFT JOIN each descendant table
|
|
1966
1954
|
for (const childMeta of descendants) {
|
|
1967
1955
|
const childAlias = this.getNextAlias(childMeta.className);
|
|
1968
1956
|
this.createAlias(childMeta.class, childAlias);
|
|
1969
1957
|
this.#state.tptAlias[childMeta.className] = childAlias;
|
|
1970
1958
|
this.addPropertyJoin(childMeta.tptInverseProp, this.mainAlias.aliasName, childAlias, JoinType.leftJoin, `[tpt]${meta.className}`);
|
|
1959
|
+
}
|
|
1960
|
+
this.driver.registerTPTAncestorAliases(this, meta, this.mainAlias.aliasName, this.#state.tptAlias);
|
|
1961
|
+
for (const childMeta of descendants) {
|
|
1962
|
+
const childAlias = this.#state.tptAlias[childMeta.className];
|
|
1971
1963
|
// Add child fields
|
|
1972
1964
|
const schema = childMeta.schema === '*' ? '*' : this.driver.getSchemaName(childMeta);
|
|
1973
1965
|
childMeta
|
|
@@ -1976,7 +1968,7 @@ export class QueryBuilder {
|
|
|
1976
1968
|
}
|
|
1977
1969
|
// Add computed discriminator (CASE WHEN to determine concrete type)
|
|
1978
1970
|
// descendants is pre-sorted by depth (deepest first) during discovery
|
|
1979
|
-
if (meta.tptDiscriminatorColumn) {
|
|
1971
|
+
if (meta.root.tptDiscriminatorColumn) {
|
|
1980
1972
|
this.#state.fields.push(this.driver.buildTPTDiscriminatorExpression(meta, descendants, this.#state.tptAlias, this.mainAlias.aliasName));
|
|
1981
1973
|
}
|
|
1982
1974
|
}
|
|
@@ -30,6 +30,8 @@ export declare class QueryBuilderHelper {
|
|
|
30
30
|
sql: string;
|
|
31
31
|
params: unknown[];
|
|
32
32
|
};
|
|
33
|
+
/** STI discriminator condition matching the entity and all its subclasses. */
|
|
34
|
+
getDiscriminatorCondition(meta: EntityMetadata): unknown;
|
|
33
35
|
mapJoinColumns(type: QueryType, join: JoinOptions): (string | Raw)[];
|
|
34
36
|
isOneToOneInverse(field: string, meta?: EntityMetadata): boolean;
|
|
35
37
|
getTableName(entityName: EntityName): string;
|
|
@@ -296,7 +296,7 @@ export class QueryBuilderHelper {
|
|
|
296
296
|
!join.path?.endsWith('[pivot]')) {
|
|
297
297
|
const typeProperty = join.prop.targetMeta.root.discriminatorColumn;
|
|
298
298
|
const alias = join.inverseAlias ?? join.alias;
|
|
299
|
-
join.cond[`${alias}.${typeProperty}`] = join.prop.targetMeta
|
|
299
|
+
join.cond[`${alias}.${typeProperty}`] = this.getDiscriminatorCondition(join.prop.targetMeta);
|
|
300
300
|
}
|
|
301
301
|
// For polymorphic relations, add discriminator condition to filter by target entity type
|
|
302
302
|
if (join.prop.polymorphic && join.prop.discriminatorColumn && join.prop.discriminatorMap) {
|
|
@@ -341,6 +341,22 @@ export class QueryBuilderHelper {
|
|
|
341
341
|
}
|
|
342
342
|
return { sql, params };
|
|
343
343
|
}
|
|
344
|
+
/** STI discriminator condition matching the entity and all its subclasses. */
|
|
345
|
+
getDiscriminatorCondition(meta) {
|
|
346
|
+
const types = Object.values(meta.root.discriminatorMap).map(cls => this.#metadata.get(cls));
|
|
347
|
+
const children = [];
|
|
348
|
+
const lookUpChildren = (ret, parent) => {
|
|
349
|
+
const children = types.filter(meta2 => meta2.extends && this.#metadata.find(meta2.extends) === parent);
|
|
350
|
+
children.forEach(m => lookUpChildren(ret, m));
|
|
351
|
+
ret.push(...children.filter(c => c.discriminatorValue));
|
|
352
|
+
return children;
|
|
353
|
+
};
|
|
354
|
+
// resolve from storage, `prop.targetMeta` can be a copy that fails the identity check above
|
|
355
|
+
lookUpChildren(children, this.#metadata.get(meta.class));
|
|
356
|
+
return children.length > 0
|
|
357
|
+
? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] }
|
|
358
|
+
: meta.discriminatorValue;
|
|
359
|
+
}
|
|
344
360
|
mapJoinColumns(type, join) {
|
|
345
361
|
if (join.prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(join.prop.kind)) {
|
|
346
362
|
return join.prop.fieldNames.map((_fieldName, idx) => {
|
|
@@ -166,11 +166,12 @@ export class SchemaComparator {
|
|
|
166
166
|
diff.changedTables[viewName] = tableDiff;
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
|
-
this.compareRoutines(fromSchema, toSchema, diff);
|
|
169
|
+
this.compareRoutines(fromSchema, toSchema, diff, !!inverseDiff);
|
|
170
170
|
return diff;
|
|
171
171
|
}
|
|
172
|
-
compareRoutines(fromSchema, toSchema, diff) {
|
|
172
|
+
compareRoutines(fromSchema, toSchema, diff, inverse) {
|
|
173
173
|
// `ignoreRoutines` makes routines create-only: new ones are still added, but existing routines are never diffed for drop/alter.
|
|
174
|
+
// The down diff (`inverse`) mirrors that: it drops what `up` created and never recreates routines unknown to the metadata.
|
|
174
175
|
const ignoreRoutines = this.#platform.getConfig().get('schemaGenerator').ignoreRoutines;
|
|
175
176
|
// Case-fold so user-written `'sql_hash'` matches Oracle's introspected `'SQL_HASH'`.
|
|
176
177
|
const routineKey = (r) => ((r.schema ? `${r.schema}.` : '') + r.name).toLowerCase();
|
|
@@ -179,6 +180,9 @@ export class SchemaComparator {
|
|
|
179
180
|
for (const [key, toRoutine] of toByKey) {
|
|
180
181
|
const fromRoutine = fromByKey.get(key);
|
|
181
182
|
if (!fromRoutine) {
|
|
183
|
+
if (ignoreRoutines && inverse) {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
182
186
|
diff.newRoutines[key] = toRoutine;
|
|
183
187
|
this.log(`routine ${key} added`);
|
|
184
188
|
continue;
|
|
@@ -188,7 +192,7 @@ export class SchemaComparator {
|
|
|
188
192
|
this.log(`routine ${key} changed`, { fromRoutine, toRoutine });
|
|
189
193
|
}
|
|
190
194
|
}
|
|
191
|
-
if (!ignoreRoutines) {
|
|
195
|
+
if (!ignoreRoutines || inverse) {
|
|
192
196
|
for (const [key, fromRoutine] of fromByKey) {
|
|
193
197
|
if (!toByKey.has(key)) {
|
|
194
198
|
diff.removedRoutines[key] = fromRoutine;
|
|
@@ -493,10 +497,12 @@ export class SchemaComparator {
|
|
|
493
497
|
tableDifferences.changedChecks[check.name] = toTableCheck;
|
|
494
498
|
changes++;
|
|
495
499
|
}
|
|
500
|
+
const ignoreTriggers = this.#platform.getConfig().get('schemaGenerator').ignoreTriggers;
|
|
496
501
|
const fromTableTriggers = fromTable.getTriggers();
|
|
497
502
|
const toTableTriggers = toTable.getTriggers();
|
|
498
503
|
for (const trigger of toTableTriggers) {
|
|
499
|
-
|
|
504
|
+
// see `compareRoutines` for how `ignoreTriggers` applies to the down diff
|
|
505
|
+
if (fromTable.hasTrigger(trigger.name) || (ignoreTriggers && inverse)) {
|
|
500
506
|
continue;
|
|
501
507
|
}
|
|
502
508
|
tableDifferences.addedTriggers[trigger.name] = trigger;
|
|
@@ -504,7 +510,7 @@ export class SchemaComparator {
|
|
|
504
510
|
changes++;
|
|
505
511
|
}
|
|
506
512
|
// `ignoreTriggers` makes triggers create-only: declared ones are still added above, but existing triggers are never diffed for drop/alter.
|
|
507
|
-
if (!
|
|
513
|
+
if (!ignoreTriggers || inverse) {
|
|
508
514
|
for (const trigger of fromTableTriggers) {
|
|
509
515
|
if (!toTable.hasTrigger(trigger.name)) {
|
|
510
516
|
tableDifferences.removedTriggers[trigger.name] = trigger;
|
|
@@ -513,7 +519,7 @@ export class SchemaComparator {
|
|
|
513
519
|
continue;
|
|
514
520
|
}
|
|
515
521
|
const toTableTrigger = toTable.getTrigger(trigger.name);
|
|
516
|
-
if (this.diffTrigger(trigger, toTableTrigger)) {
|
|
522
|
+
if (!ignoreTriggers && this.diffTrigger(trigger, toTableTrigger)) {
|
|
517
523
|
this.log(`trigger ${trigger.name} changed in table ${tableDifferences.name}`, {
|
|
518
524
|
fromTableTrigger: trigger,
|
|
519
525
|
toTableTrigger,
|