@mikro-orm/sql 7.2.2-dev.2 → 7.2.2-dev.21
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 +5 -1
- package/AbstractSqlDriver.js +40 -22
- package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +1 -0
- package/dialects/mssql/MsSqlNativeQueryBuilder.js +13 -7
- package/dialects/postgresql/PostgreSqlSchemaHelper.js +5 -1
- package/package.json +2 -2
- package/query/NativeQueryBuilder.d.ts +1 -1
- package/query/NativeQueryBuilder.js +3 -3
- package/query/QueryBuilder.js +23 -17
- package/query/QueryBuilderHelper.js +8 -14
package/AbstractSqlDriver.d.ts
CHANGED
|
@@ -17,7 +17,11 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
|
|
|
17
17
|
/** Evaluates a formula callback, handling both string and Raw return values. */
|
|
18
18
|
evaluateFormula(formula: (...args: any[]) => string | Raw, columns: any, table: FormulaTable): string;
|
|
19
19
|
/** For TPT entities, returns ownProps (columns in this table); otherwise returns all props. */
|
|
20
|
-
|
|
20
|
+
protected getTableProps<T extends object>(meta: EntityMetadata<T>): EntityProperty<T>[];
|
|
21
|
+
/** Columns reloaded by UPDATE: raw values, PKs, generated/version columns and explicit `returning` hints. */
|
|
22
|
+
protected getUpdateReturningProperties<T extends object>(meta: EntityMetadata<T>, data: EntityDictionary<T>[]): EntityProperty<T>[];
|
|
23
|
+
/** Column expression for the `returning` clause of batch updates, applying `convertToJSValueSQL` of custom types. */
|
|
24
|
+
protected getReturningFieldSQL(prop: EntityProperty, fieldName: string, index: number): string;
|
|
21
25
|
/** Creates a FormulaTable object for use in formula callbacks. */
|
|
22
26
|
private createFormulaTable;
|
|
23
27
|
private validateSqlOptions;
|
package/AbstractSqlDriver.js
CHANGED
|
@@ -49,6 +49,32 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
49
49
|
getTableProps(meta) {
|
|
50
50
|
return meta.inheritanceType === 'tpt' && meta.ownProps ? meta.ownProps : meta.props;
|
|
51
51
|
}
|
|
52
|
+
/** Columns reloaded by UPDATE: raw values, PKs, generated/version columns and explicit `returning` hints. */
|
|
53
|
+
getUpdateReturningProperties(meta, data) {
|
|
54
|
+
const returning = new Set();
|
|
55
|
+
for (const row of data) {
|
|
56
|
+
for (const k of Utils.keys(row)) {
|
|
57
|
+
if (isRaw(row[k])) {
|
|
58
|
+
returning.add(meta.properties[k] ?? meta.root.properties[k]);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
meta.getPrimaryProps().forEach(prop => returning.add(prop));
|
|
63
|
+
// a mixed STI batch shares one table, so the child columns live on the root
|
|
64
|
+
this.getTableProps(meta.root.discriminatorColumn ? meta.root : meta)
|
|
65
|
+
.filter(prop => prop.generated || prop.version || prop.returning)
|
|
66
|
+
.forEach(prop => returning.add(prop));
|
|
67
|
+
return [...returning];
|
|
68
|
+
}
|
|
69
|
+
/** Column expression for the `returning` clause of batch updates, applying `convertToJSValueSQL` of custom types. */
|
|
70
|
+
getReturningFieldSQL(prop, fieldName, index) {
|
|
71
|
+
const quoted = this.platform.quoteIdentifier(fieldName);
|
|
72
|
+
const customType = prop.customTypes?.[index] ?? prop.customType;
|
|
73
|
+
if (prop.hasConvertToJSValueSQL && customType?.convertToJSValueSQL) {
|
|
74
|
+
return `${customType.convertToJSValueSQL(quoted, this.platform)} as ${quoted}`;
|
|
75
|
+
}
|
|
76
|
+
return quoted;
|
|
77
|
+
}
|
|
52
78
|
/** Creates a FormulaTable object for use in formula callbacks. */
|
|
53
79
|
createFormulaTable(alias, meta, schema) {
|
|
54
80
|
const effectiveSchema = schema ?? (meta.schema !== '*' ? meta.schema : undefined);
|
|
@@ -946,7 +972,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
946
972
|
/* v8 ignore next */
|
|
947
973
|
const uniqueFields = options.onConflictFields ??
|
|
948
974
|
(Utils.isPlainObject(where) ? Utils.keys(where) : meta.primaryKeys);
|
|
949
|
-
const returning = getOnConflictReturningFields(meta, data, uniqueFields, options);
|
|
975
|
+
const returning = getOnConflictReturningFields(meta, data, uniqueFields, options, this.platform);
|
|
950
976
|
qb.insert(data)
|
|
951
977
|
.onConflict(uniqueFields)
|
|
952
978
|
.returning(returning);
|
|
@@ -963,11 +989,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
963
989
|
}
|
|
964
990
|
else {
|
|
965
991
|
qb.update(data).where(where);
|
|
966
|
-
// reload generated columns and version fields
|
|
967
992
|
const returning = [];
|
|
968
993
|
this.getTableProps(meta)
|
|
969
|
-
.filter(prop => (prop.generated && !prop.primary) || prop.version)
|
|
970
|
-
.forEach(prop => returning.push(prop.
|
|
994
|
+
.filter(prop => (prop.generated && !prop.primary) || prop.version || prop.returning || isRaw(data[prop.name]))
|
|
995
|
+
.forEach(prop => returning.push(...prop.fieldNames));
|
|
971
996
|
qb.returning(returning);
|
|
972
997
|
}
|
|
973
998
|
res = await this.rethrow(qb.execute('run', false));
|
|
@@ -1003,7 +1028,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1003
1028
|
: meta.primaryKeys);
|
|
1004
1029
|
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes, options.loggerContext).withSchema(this.getSchemaName(meta, options));
|
|
1005
1030
|
qb.setAbortOptions(pickAbortOptions(options));
|
|
1006
|
-
let returning = getOnConflictReturningFields(meta, data[0], uniqueFields, options);
|
|
1031
|
+
let returning = getOnConflictReturningFields(meta, data[0], uniqueFields, options, this.platform);
|
|
1007
1032
|
if (meta.inheritanceType === 'tpt') {
|
|
1008
1033
|
// each TPT table only carries its own columns, the entity is reloaded instead of mapping the returned rows
|
|
1009
1034
|
const own = (key) => meta.primaryKeys.includes(key) ||
|
|
@@ -1032,20 +1057,12 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1032
1057
|
const collections = options.processCollections ? data.map(d => this.extractManyToMany(meta, d)) : [];
|
|
1033
1058
|
const keys = new Set();
|
|
1034
1059
|
const fields = new Set();
|
|
1035
|
-
const returning =
|
|
1060
|
+
const returning = this.getUpdateReturningProperties(meta, data);
|
|
1036
1061
|
for (const row of data) {
|
|
1037
1062
|
for (const k of Utils.keys(row)) {
|
|
1038
1063
|
keys.add(k);
|
|
1039
|
-
if (isRaw(row[k])) {
|
|
1040
|
-
returning.add(k);
|
|
1041
|
-
}
|
|
1042
1064
|
}
|
|
1043
1065
|
}
|
|
1044
|
-
// reload generated columns and version fields
|
|
1045
|
-
meta.getPrimaryProps().forEach(prop => returning.add(prop.name));
|
|
1046
|
-
this.getTableProps(meta)
|
|
1047
|
-
.filter(prop => prop.generated || prop.version)
|
|
1048
|
-
.forEach(prop => returning.add(prop.name));
|
|
1049
1066
|
const pkCond = Utils.flatten(meta.primaryKeys.map(pk => meta.properties[pk].fieldNames))
|
|
1050
1067
|
.map(pk => `${this.platform.quoteIdentifier(pk)} = ?`)
|
|
1051
1068
|
.join(' and ');
|
|
@@ -1115,7 +1132,12 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1115
1132
|
}
|
|
1116
1133
|
sql += `, `;
|
|
1117
1134
|
}
|
|
1118
|
-
sql = sql.substring(0, sql.length - 2)
|
|
1135
|
+
sql = sql.substring(0, sql.length - 2);
|
|
1136
|
+
if (this.platform.usesOutputStatement() && returning.length > 0) {
|
|
1137
|
+
const fields = returning.flatMap(prop => prop.fieldNames);
|
|
1138
|
+
sql += ` output ${fields.map(field => 'inserted.' + this.platform.quoteIdentifier(field)).join(', ')}`;
|
|
1139
|
+
}
|
|
1140
|
+
sql += ' where ';
|
|
1119
1141
|
const pkProps = meta.primaryKeys.concat(...meta.getOwnConcurrencyCheckKeys());
|
|
1120
1142
|
const pks = Utils.flatten(pkProps.map(pk => meta.properties[pk].fieldNames));
|
|
1121
1143
|
const useTupleIn = pks.length <= 1 || this.platform.allowsComparingTuples();
|
|
@@ -1151,13 +1173,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1151
1173
|
else {
|
|
1152
1174
|
sql += conds.join(' or ');
|
|
1153
1175
|
}
|
|
1154
|
-
if (this.platform.usesReturningStatement() && returning.
|
|
1155
|
-
const returningFields =
|
|
1156
|
-
|
|
1157
|
-
sql +=
|
|
1158
|
-
returningFields.length > 0
|
|
1159
|
-
? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}`
|
|
1160
|
-
: '';
|
|
1176
|
+
if (this.platform.usesReturningStatement() && returning.length > 0) {
|
|
1177
|
+
const returningFields = returning.flatMap(prop => prop.fieldNames.map((field, idx) => this.getReturningFieldSQL(prop, field, idx)));
|
|
1178
|
+
sql += ` returning ${returningFields.join(', ')}`;
|
|
1161
1179
|
}
|
|
1162
1180
|
if (transform) {
|
|
1163
1181
|
sql = transform(sql, params);
|
|
@@ -6,6 +6,7 @@ export declare class MsSqlNativeQueryBuilder extends NativeQueryBuilder {
|
|
|
6
6
|
params: unknown[];
|
|
7
7
|
};
|
|
8
8
|
protected compileInsert(): void;
|
|
9
|
+
protected addOutputClause(type: 'inserted' | 'deleted'): void;
|
|
9
10
|
private appendOutputTable;
|
|
10
11
|
private compileUpsert;
|
|
11
12
|
protected compileSelect(): void;
|
|
@@ -22,6 +22,9 @@ export class MsSqlNativeQueryBuilder extends NativeQueryBuilder {
|
|
|
22
22
|
this.compileCtes();
|
|
23
23
|
if (this.options.onConflict && !Utils.isEmpty(Utils.asArray(this.options.data)[0])) {
|
|
24
24
|
this.compileUpsert();
|
|
25
|
+
if (suffix) {
|
|
26
|
+
this.parts.push(suffix);
|
|
27
|
+
}
|
|
25
28
|
}
|
|
26
29
|
else {
|
|
27
30
|
switch (this.type) {
|
|
@@ -72,31 +75,34 @@ export class MsSqlNativeQueryBuilder extends NativeQueryBuilder {
|
|
|
72
75
|
return;
|
|
73
76
|
}
|
|
74
77
|
const parts = this.processInsertData();
|
|
75
|
-
if (this.options.flags?.has(QueryFlag.OUTPUT_TABLE)) {
|
|
76
|
-
this.parts[this.parts.length - 2] += ' into #out ';
|
|
77
|
-
}
|
|
78
78
|
this.parts.push(parts.join(', '));
|
|
79
79
|
}
|
|
80
|
+
addOutputClause(type) {
|
|
81
|
+
super.addOutputClause(type);
|
|
82
|
+
if (this.options.returning?.length && this.options.flags?.has(QueryFlag.OUTPUT_TABLE)) {
|
|
83
|
+
this.parts[this.parts.length - 1] += ' into #out';
|
|
84
|
+
}
|
|
85
|
+
}
|
|
80
86
|
appendOutputTable() {
|
|
81
|
-
if (!this.options.flags?.has(QueryFlag.OUTPUT_TABLE)) {
|
|
87
|
+
if (!this.options.flags?.has(QueryFlag.OUTPUT_TABLE) || !this.options.returning?.length) {
|
|
82
88
|
return { prefix: '', suffix: '' };
|
|
83
89
|
}
|
|
84
90
|
const returningFields = this.options.returning;
|
|
85
91
|
const selections = returningFields.map(field => `[t].${this.platform.quoteIdentifier(field)}`).join(',');
|
|
86
92
|
return {
|
|
87
93
|
prefix: `select top(0) ${selections} into #out from ${this.getTableName()} as t left join ${this.getTableName()} on 0 = 1;`,
|
|
88
|
-
suffix: `select ${selections} from #out as t; drop table #out`,
|
|
94
|
+
suffix: `select ${selections} from #out as t; select @@rowcount as [__mikro_orm_row_count__]; drop table #out`,
|
|
89
95
|
};
|
|
90
96
|
}
|
|
91
97
|
compileUpsert() {
|
|
92
98
|
const clause = this.options.onConflict;
|
|
93
99
|
const dataAsArray = Utils.asArray(this.options.data);
|
|
94
|
-
const keys = Object.keys(
|
|
100
|
+
const keys = [...new Set(dataAsArray.flatMap(data => Object.keys(data)))];
|
|
95
101
|
const values = keys.map(() => '?');
|
|
96
102
|
const parts = [];
|
|
97
103
|
for (const data of dataAsArray) {
|
|
98
104
|
for (const key of keys) {
|
|
99
|
-
this.params.push(data[key]);
|
|
105
|
+
this.params.push(data[key] ?? null);
|
|
100
106
|
}
|
|
101
107
|
parts.push(`(${values.join(', ')})`);
|
|
102
108
|
}
|
|
@@ -419,7 +419,11 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
|
|
|
419
419
|
if ((col.udt_name === 'geometry' || col.udt_name === 'geography') && col.format_type) {
|
|
420
420
|
type = col.format_type;
|
|
421
421
|
}
|
|
422
|
-
|
|
422
|
+
// pgvector stores the dimension in atttypmod for all three of its vector types
|
|
423
|
+
if (['vector', 'halfvec', 'sparsevec'].includes(type) &&
|
|
424
|
+
col.length == null &&
|
|
425
|
+
col.custom_length != null &&
|
|
426
|
+
col.custom_length !== -1) {
|
|
423
427
|
col.length = col.custom_length;
|
|
424
428
|
}
|
|
425
429
|
if (col.length != null && !type.endsWith(`(${col.length})`) && !['text', 'date'].includes(type)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/sql",
|
|
3
|
-
"version": "7.2.2-dev.
|
|
3
|
+
"version": "7.2.2-dev.21",
|
|
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.1"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@mikro-orm/core": "7.2.2-dev.
|
|
56
|
+
"@mikro-orm/core": "7.2.2-dev.21"
|
|
57
57
|
},
|
|
58
58
|
"engines": {
|
|
59
59
|
"node": ">= 22.17.0"
|
|
@@ -127,7 +127,7 @@ export declare class NativeQueryBuilder implements Subquery {
|
|
|
127
127
|
as(alias: string): this;
|
|
128
128
|
toRaw(): RawQueryFragment;
|
|
129
129
|
protected compileSelect(): void;
|
|
130
|
-
/** Whether this COUNT query needs a subquery wrapper
|
|
130
|
+
/** Whether this COUNT query needs a subquery wrapper: multi-column distinct, or `distinct on` (keeps the null group). */
|
|
131
131
|
protected needsCountSubquery(): boolean;
|
|
132
132
|
protected getFields(countSubquery?: boolean): string;
|
|
133
133
|
protected compileInsert(): void;
|
|
@@ -330,12 +330,12 @@ export class NativeQueryBuilder {
|
|
|
330
330
|
this.parts.push(`)${asKeyword}${this.quote('dcnt')}`);
|
|
331
331
|
}
|
|
332
332
|
}
|
|
333
|
-
/** Whether this COUNT query needs a subquery wrapper
|
|
333
|
+
/** Whether this COUNT query needs a subquery wrapper: multi-column distinct, or `distinct on` (keeps the null group). */
|
|
334
334
|
needsCountSubquery() {
|
|
335
335
|
return (this.type === QueryType.COUNT &&
|
|
336
336
|
!!this.options.distinct &&
|
|
337
|
-
this.options.select.length > 1 &&
|
|
338
|
-
|
|
337
|
+
((this.options.select.length > 1 && !this.platform.supportsMultiColumnCountDistinct()) ||
|
|
338
|
+
!!this.options.distinctOn));
|
|
339
339
|
}
|
|
340
340
|
getFields(countSubquery) {
|
|
341
341
|
if (!this.options.select || this.options.select.length === 0) {
|
package/query/QueryBuilder.js
CHANGED
|
@@ -257,7 +257,11 @@ export class QueryBuilder {
|
|
|
257
257
|
if (field) {
|
|
258
258
|
this.#state.fields = Utils.asArray(field);
|
|
259
259
|
}
|
|
260
|
-
else if (
|
|
260
|
+
else if (this.#state.distinctOn) {
|
|
261
|
+
this.#state.fields = this.#state.distinctOn;
|
|
262
|
+
distinct = true;
|
|
263
|
+
}
|
|
264
|
+
else if (distinct || this.#state.flags.has(QueryFlag.DISTINCT) || this.hasToManyJoins()) {
|
|
261
265
|
this.#state.fields = this.mainAlias.meta.primaryKeys;
|
|
262
266
|
}
|
|
263
267
|
else {
|
|
@@ -916,18 +920,13 @@ export class QueryBuilder {
|
|
|
916
920
|
return;
|
|
917
921
|
}
|
|
918
922
|
if (this.type === QueryType.UPDATE && data) {
|
|
919
|
-
const returningProps = meta.hydrateProps
|
|
923
|
+
const returningProps = meta.hydrateProps
|
|
924
|
+
.filter(prop => prop.returning || (prop.fieldNames && isRaw(data[prop.fieldNames[0]])))
|
|
925
|
+
// a TPT table can only return its own columns
|
|
926
|
+
.filter(prop => meta.inheritanceType !== 'tpt' || prop.primary || meta.ownProps.some(p => p.name === prop.name));
|
|
920
927
|
if (returningProps.length > 0) {
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
const aliased = this.platform.quoteIdentifier(prop.fieldNames[0]);
|
|
924
|
-
const sql = prop.customType.convertToJSValueSQL(aliased, this.platform) +
|
|
925
|
-
' as ' +
|
|
926
|
-
this.platform.quoteIdentifier(prop.fieldNames[0]);
|
|
927
|
-
return [raw(sql)];
|
|
928
|
-
}
|
|
929
|
-
return prop.fieldNames;
|
|
930
|
-
}));
|
|
928
|
+
const fields = returningProps.flatMap(prop => prop.fieldNames);
|
|
929
|
+
qb.returning(fields.map(field => this.helper.mapper(field, this.type)));
|
|
931
930
|
}
|
|
932
931
|
}
|
|
933
932
|
}
|
|
@@ -1571,7 +1570,7 @@ export class QueryBuilder {
|
|
|
1571
1570
|
return;
|
|
1572
1571
|
}
|
|
1573
1572
|
if (prop?.embedded || (prop?.kind === ReferenceKind.EMBEDDED && prop.object)) {
|
|
1574
|
-
const name = prop.embeddedPath?.join('.') ?? prop.fieldNames[0];
|
|
1573
|
+
const name = prop.formula ? prop.name : (prop.embeddedPath?.join('.') ?? prop.fieldNames[0]);
|
|
1575
1574
|
const aliased = this.#state.aliases[a] ? `${a}.${name}` : name;
|
|
1576
1575
|
ret.push(getFieldName(aliased, customAlias));
|
|
1577
1576
|
return;
|
|
@@ -1586,11 +1585,11 @@ export class QueryBuilder {
|
|
|
1586
1585
|
}
|
|
1587
1586
|
const nest = (prop) => {
|
|
1588
1587
|
for (const childProp of Object.values(prop.embeddedProps)) {
|
|
1589
|
-
if (childProp.persist === false) {
|
|
1588
|
+
if (childProp.persist === false && !childProp.formula) {
|
|
1590
1589
|
continue;
|
|
1591
1590
|
}
|
|
1592
1591
|
if (childProp.fieldNames && (childProp.kind !== ReferenceKind.EMBEDDED || childProp.object)) {
|
|
1593
|
-
const name = childProp.fieldNames[0];
|
|
1592
|
+
const name = childProp.formula ? childProp.name : childProp.fieldNames[0];
|
|
1594
1593
|
ret.push(getFieldName(this.#state.aliases[a] ? `${a}.${name}` : name));
|
|
1595
1594
|
}
|
|
1596
1595
|
else {
|
|
@@ -1671,7 +1670,8 @@ export class QueryBuilder {
|
|
|
1671
1670
|
return `${currentAlias}.${embeddedProp.fieldNames[0]}`;
|
|
1672
1671
|
}
|
|
1673
1672
|
}
|
|
1674
|
-
|
|
1673
|
+
const name = embeddedProp?.formula ? embeddedProp.name : (embeddedProp?.fieldNames?.[0] ?? propName);
|
|
1674
|
+
return `${currentAlias}.${name}`;
|
|
1675
1675
|
}
|
|
1676
1676
|
// Handle relations - auto-join if not the last part
|
|
1677
1677
|
if (prop.kind === ReferenceKind.MANY_TO_ONE ||
|
|
@@ -1764,6 +1764,9 @@ export class QueryBuilder {
|
|
|
1764
1764
|
case QueryType.COUNT: {
|
|
1765
1765
|
const fields = this.#state.fields.map(f => this.helper.mapper(f, this.type, undefined, undefined, schema));
|
|
1766
1766
|
qb.count(fields, this.#state.flags.has(QueryFlag.DISTINCT));
|
|
1767
|
+
if (this.#state.distinctOn && this.#state.flags.has(QueryFlag.DISTINCT)) {
|
|
1768
|
+
qb.distinctOn(this.prepareFields(this.#state.distinctOn, 'where', schema));
|
|
1769
|
+
}
|
|
1767
1770
|
this.helper.processJoins(qb, this.#state.joins, joinSchema, schemaOverride);
|
|
1768
1771
|
break;
|
|
1769
1772
|
}
|
|
@@ -2327,7 +2330,10 @@ export class QueryBuilder {
|
|
|
2327
2330
|
const fieldName = this.helper.mapper(field, this.type, undefined, null);
|
|
2328
2331
|
// virtual fields (e.g. `qb.as(...)`) have no column to reference inside `min()`; inline their
|
|
2329
2332
|
// expression instead, as a select alias is not resolvable there on some dialects (e.g. PostgreSQL)
|
|
2330
|
-
const virtual = !prop
|
|
2333
|
+
const virtual = (!prop || prop.persist === false) &&
|
|
2334
|
+
!prop?.formula &&
|
|
2335
|
+
!prop?.hasConvertToJSValueSQL &&
|
|
2336
|
+
!pks.includes(fieldName)
|
|
2331
2337
|
? this.resolveVirtualField(f, fieldName)
|
|
2332
2338
|
: undefined;
|
|
2333
2339
|
const expr = virtual ? virtual.expr : this.platform.quoteIdentifier(fieldName);
|
|
@@ -112,7 +112,7 @@ export class QueryBuilderHelper {
|
|
|
112
112
|
const aliasPrefix = isTableNameAliasRequired ? resolvedAlias + '.' : '';
|
|
113
113
|
const fkIdx2 = prop?.fieldNames.findIndex(name => name === f) ?? -1;
|
|
114
114
|
const fkIdx = fkIdx2 === -1 ? 0 : fkIdx2;
|
|
115
|
-
if (a === prop?.embedded?.[0]) {
|
|
115
|
+
if (a === prop?.embedded?.[0] && !prop.formula) {
|
|
116
116
|
return aliasPrefix + prop.fieldNames[fkIdx];
|
|
117
117
|
}
|
|
118
118
|
const noPrefix = prop?.persist === false;
|
|
@@ -120,29 +120,23 @@ export class QueryBuilderHelper {
|
|
|
120
120
|
return raw(this.prefix(field, isTableNameAliasRequired));
|
|
121
121
|
}
|
|
122
122
|
if (prop?.formula) {
|
|
123
|
-
const
|
|
123
|
+
const formulaAlias = isTableAlias ? a : this.#alias;
|
|
124
|
+
const alias2 = this.#platform.quoteIdentifier(formulaAlias).toString();
|
|
124
125
|
const aliasName = alias === undefined ? prop.fieldNames[0] : alias;
|
|
125
126
|
const as = aliasName === null ? '' : ` as ${this.#platform.quoteIdentifier(aliasName)}`;
|
|
126
127
|
const meta = this.#aliasMap[a]?.meta ?? this.#metadata.get(this.#entityName);
|
|
127
128
|
const table = this.createFormulaTable(alias2, meta, schema);
|
|
128
|
-
const columns = meta.createColumnMappingObject(p => this.getTPTAliasForProperty(p.name,
|
|
129
|
+
const columns = meta.createColumnMappingObject(p => this.getTPTAliasForProperty(p.name, formulaAlias), alias2);
|
|
129
130
|
let value = this.#driver.evaluateFormula(prop.formula, columns, table);
|
|
130
131
|
if (!this.isTableNameAliasRequired(type)) {
|
|
131
132
|
value = value.replaceAll(alias2 + '.', '');
|
|
132
133
|
}
|
|
133
134
|
return raw(`${value}${as}`);
|
|
134
135
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const prefixed = this.prefix(field, isTableNameAliasRequired, true, fkIdx);
|
|
140
|
-
valueSQL = fk.customType.convertToJSValueSQL(prefixed, this.#platform);
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
const prefixed = this.prefix(field, isTableNameAliasRequired, true);
|
|
144
|
-
valueSQL = prop.customType.convertToJSValueSQL(prefixed, this.#platform);
|
|
145
|
-
}
|
|
136
|
+
const customType = prop?.customTypes?.[fkIdx] ?? prop?.customType;
|
|
137
|
+
if (prop?.hasConvertToJSValueSQL && customType?.convertToJSValueSQL && type !== QueryType.UPSERT) {
|
|
138
|
+
const prefixed = this.prefix(field, isTableNameAliasRequired, true, fkIdx);
|
|
139
|
+
const valueSQL = customType.convertToJSValueSQL(prefixed, this.#platform);
|
|
146
140
|
if (alias === null) {
|
|
147
141
|
return raw(valueSQL);
|
|
148
142
|
}
|