@mikro-orm/core 7.2.2-dev.5 → 7.2.2-dev.7
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/EntityManager.js +31 -4
- package/entity/defineEntity.d.ts +1 -1
- package/entity/defineEntity.js +1 -1
- package/metadata/MetadataDiscovery.d.ts +2 -0
- package/metadata/MetadataDiscovery.js +11 -0
- package/metadata/types.d.ts +1 -1
- package/package.json +1 -1
- package/utils/Utils.js +1 -1
- package/utils/upsert-utils.d.ts +2 -1
- package/utils/upsert-utils.js +9 -5
package/EntityManager.js
CHANGED
|
@@ -994,13 +994,14 @@ export class EntityManager {
|
|
|
994
994
|
});
|
|
995
995
|
const uniqueFields = options.onConflictFields ??
|
|
996
996
|
(Utils.isPlainObject(where) ? Object.keys(where) : meta.primaryKeys);
|
|
997
|
-
const
|
|
997
|
+
const platform = this.getPlatform();
|
|
998
|
+
const returning = getOnConflictReturningFields(meta, data, uniqueFields, options, platform);
|
|
998
999
|
if (options.onConflictWhere && !ret.row) {
|
|
999
1000
|
returning.push(...Object.keys(data));
|
|
1000
1001
|
}
|
|
1001
1002
|
if (options.onConflictAction === 'ignore' ||
|
|
1002
1003
|
!helper(entity).hasPrimaryKey() ||
|
|
1003
|
-
(returning.length > 0 && !(
|
|
1004
|
+
(returning.length > 0 && !((platform.usesReturningStatement() || platform.usesOutputStatement()) && ret.row))) {
|
|
1004
1005
|
const where = {};
|
|
1005
1006
|
if (Array.isArray(uniqueFields)) {
|
|
1006
1007
|
for (const prop of uniqueFields) {
|
|
@@ -1091,6 +1092,30 @@ export class EntityManager {
|
|
|
1091
1092
|
}
|
|
1092
1093
|
return ret;
|
|
1093
1094
|
}
|
|
1095
|
+
// merge fields are derived from the first row, rows of a different shape need their own statement
|
|
1096
|
+
if (!options.onConflictMergeFields && options.onConflictAction !== 'ignore') {
|
|
1097
|
+
const pks = this.metadata.get(entityName).primaryKeys;
|
|
1098
|
+
const shapes = new Map();
|
|
1099
|
+
data.forEach((row, i) => {
|
|
1100
|
+
// entity instances carry their unset properties as `undefined` keys, while in plain rows those are merged as `null`
|
|
1101
|
+
const shape = Object.keys(row)
|
|
1102
|
+
.filter(key => !pks.includes(key) && !(Utils.isEntity(row) && row[key] === undefined))
|
|
1103
|
+
.sort()
|
|
1104
|
+
.join();
|
|
1105
|
+
const idx = shapes.get(shape) ?? [];
|
|
1106
|
+
shapes.set(shape, idx);
|
|
1107
|
+
idx.push(i);
|
|
1108
|
+
});
|
|
1109
|
+
if (shapes.size > 1) {
|
|
1110
|
+
const ret = [];
|
|
1111
|
+
for (const idx of shapes.values()) {
|
|
1112
|
+
const res = await this.upsertMany(entityName, idx.map(i => data[i]), options);
|
|
1113
|
+
idx.forEach((i, j) => (ret[i] = res[j]));
|
|
1114
|
+
}
|
|
1115
|
+
// rows resolving to the same entity are returned once, as in the single statement path
|
|
1116
|
+
return [...new Set(ret.filter(entity => entity))];
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1094
1119
|
// the rows are replaced with their processed copies below, keep the caller's array intact
|
|
1095
1120
|
data = [...data];
|
|
1096
1121
|
const meta = this.metadata.get(entityName);
|
|
@@ -1206,11 +1231,13 @@ export class EntityManager {
|
|
|
1206
1231
|
// (onConflictWhere can suppress writes, leaving some rows out)
|
|
1207
1232
|
// oxfmt-ignore
|
|
1208
1233
|
const uniqueFields = options.onConflictFields ?? (Utils.isPlainObject(allWhere[0]) ? Object.keys(allWhere[0]).flatMap(key => Utils.splitPrimaryKeys(key)) : meta.primaryKeys);
|
|
1209
|
-
const
|
|
1234
|
+
const platform = this.getPlatform();
|
|
1235
|
+
const returning = getOnConflictReturningFields(meta, data[0], uniqueFields, options, platform);
|
|
1210
1236
|
if (options.onConflictWhere && res.rows?.length !== allData.length) {
|
|
1211
1237
|
returning.push(...meta.comparableProps.filter(p => !p.lazy && !p.embeddable).map(p => p.name));
|
|
1212
1238
|
}
|
|
1213
|
-
const reloadFields = returning.length > 0 &&
|
|
1239
|
+
const reloadFields = returning.length > 0 &&
|
|
1240
|
+
!((platform.usesReturningStatement() || platform.usesOutputStatement()) && res.rows?.length === data.length);
|
|
1214
1241
|
if (options.onConflictAction === 'ignore' || (!res.rows?.length && loadPK.size > 0) || reloadFields) {
|
|
1215
1242
|
const unique = meta.hydrateProps.filter(p => !p.lazy).map(p => p.name);
|
|
1216
1243
|
const add = new Set(propIndex !== false && propIndex >= 0
|
package/entity/defineEntity.d.ts
CHANGED
|
@@ -267,7 +267,7 @@ export declare class UniversalPropertyOptionsBuilder<in out Value, in out Option
|
|
|
267
267
|
autoincrement: false;
|
|
268
268
|
}, IncludeKeys>, IncludeKeys>;
|
|
269
269
|
/**
|
|
270
|
-
* Add the property to the `returning`
|
|
270
|
+
* Add the property to the `returning` clause of inserts, updates and upserts, reloading the database value into the entity.
|
|
271
271
|
*/
|
|
272
272
|
returning(returning?: boolean): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
|
|
273
273
|
/**
|
package/entity/defineEntity.js
CHANGED
|
@@ -88,7 +88,7 @@ export class UniversalPropertyOptionsBuilder {
|
|
|
88
88
|
return this.assignOptions({ autoincrement });
|
|
89
89
|
}
|
|
90
90
|
/**
|
|
91
|
-
* Add the property to the `returning`
|
|
91
|
+
* Add the property to the `returning` clause of inserts, updates and upserts, reloading the database value into the entity.
|
|
92
92
|
*/
|
|
93
93
|
returning(returning = true) {
|
|
94
94
|
return this.assignOptions({ returning });
|
|
@@ -128,6 +128,8 @@ export declare class MetadataDiscovery {
|
|
|
128
128
|
private initRelation;
|
|
129
129
|
/** Resolves the `through` option of a virtual to-one relation into a read-only formula property. */
|
|
130
130
|
private initThroughRelation;
|
|
131
|
+
/** `returning` is only honoured for physical columns that can be hydrated back into the entity. */
|
|
132
|
+
private initReturning;
|
|
131
133
|
private initColumnType;
|
|
132
134
|
private getMappedType;
|
|
133
135
|
private getPrefix;
|
|
@@ -174,6 +174,7 @@ export class MetadataDiscovery {
|
|
|
174
174
|
this.initRelation(p);
|
|
175
175
|
this.initThroughRelation(m, p);
|
|
176
176
|
this.initColumnType(p);
|
|
177
|
+
this.initReturning(p);
|
|
177
178
|
});
|
|
178
179
|
forEachProp((m, p) => this.initIndexes(m, p));
|
|
179
180
|
filtered.forEach(meta => this.autoWireBidirectionalProperties(meta));
|
|
@@ -2129,6 +2130,16 @@ export class MetadataDiscovery {
|
|
|
2129
2130
|
prop.persist = false;
|
|
2130
2131
|
prop.formula = columns => this.#platform.getThroughRelationFormula(prop, columns);
|
|
2131
2132
|
}
|
|
2133
|
+
/** `returning` is only honoured for physical columns that can be hydrated back into the entity. */
|
|
2134
|
+
initReturning(prop) {
|
|
2135
|
+
const owning = prop.kind === ReferenceKind.SCALAR ||
|
|
2136
|
+
prop.kind === ReferenceKind.MANY_TO_ONE ||
|
|
2137
|
+
(prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner) ||
|
|
2138
|
+
(prop.kind === ReferenceKind.EMBEDDED && prop.object);
|
|
2139
|
+
if (prop.returning && (!owning || prop.persist === false || prop.hydrate === false || prop.formula)) {
|
|
2140
|
+
prop.returning = false;
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2132
2143
|
initColumnType(prop) {
|
|
2133
2144
|
this.initUnsigned(prop);
|
|
2134
2145
|
// Get the target properties for FK relations - use targetKey property if specified, otherwise PKs
|
package/metadata/types.d.ts
CHANGED
|
@@ -175,7 +175,7 @@ export interface PropertyOptions<Owner> {
|
|
|
175
175
|
*/
|
|
176
176
|
autoincrement?: boolean;
|
|
177
177
|
/**
|
|
178
|
-
* Add the property to the `returning`
|
|
178
|
+
* Add the property to the `returning` clause of inserts, updates and upserts, reloading the database value into the entity.
|
|
179
179
|
*/
|
|
180
180
|
returning?: boolean;
|
|
181
181
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/core",
|
|
3
|
-
"version": "7.2.2-dev.
|
|
3
|
+
"version": "7.2.2-dev.7",
|
|
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",
|
package/utils/Utils.js
CHANGED
|
@@ -153,7 +153,7 @@ export function parseJsonSafe(value) {
|
|
|
153
153
|
/** Collection of general-purpose utility methods used throughout the ORM. */
|
|
154
154
|
export class Utils {
|
|
155
155
|
static PK_SEPARATOR = '~~~';
|
|
156
|
-
static #ORM_VERSION = '7.2.2-dev.
|
|
156
|
+
static #ORM_VERSION = '7.2.2-dev.7';
|
|
157
157
|
/** Default session variable name backing an RLS filter argument (`current_setting('mikro.<filter>.<arg>')`). */
|
|
158
158
|
static getRlsSettingName(filterName, argName) {
|
|
159
159
|
return `mikro.${filterName}.${argName}`;
|
package/utils/upsert-utils.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { EntityData, EntityKey, EntityMetadata, FilterQuery } from '../typings.js';
|
|
2
2
|
import type { UpsertOptions } from '../drivers/IDatabaseDriver.js';
|
|
3
3
|
import { type Raw } from '../utils/RawQueryFragment.js';
|
|
4
|
+
import type { Platform } from '../platforms/Platform.js';
|
|
4
5
|
/** @internal */
|
|
5
6
|
export declare function getOnConflictFields<T>(meta: EntityMetadata<T> | undefined, data: EntityData<T>, uniqueFields: (keyof T)[] | Raw, options: UpsertOptions<T>): (keyof T)[];
|
|
6
7
|
/**
|
|
@@ -12,7 +13,7 @@ export declare function getOnConflictFields<T>(meta: EntityMetadata<T> | undefin
|
|
|
12
13
|
*/
|
|
13
14
|
export declare function getOnCreateGeneratedFields<T extends object>(meta: EntityMetadata<T>, data: T | EntityData<T>): EntityKey<T>[];
|
|
14
15
|
/** @internal */
|
|
15
|
-
export declare function getOnConflictReturningFields<T, P extends string>(meta: EntityMetadata<T> | undefined, data: EntityData<T>, uniqueFields: (keyof T)[] | Raw, options: UpsertOptions<T, P
|
|
16
|
+
export declare function getOnConflictReturningFields<T, P extends string>(meta: EntityMetadata<T> | undefined, data: EntityData<T>, uniqueFields: (keyof T)[] | Raw, options: UpsertOptions<T, P>, platform?: Platform): (keyof T)[] | '*';
|
|
16
17
|
/** @internal */
|
|
17
18
|
export declare function getWhereCondition<T extends object>(meta: EntityMetadata<T>, onConflictFields: (keyof T)[] | Raw | undefined, data: EntityData<T>, where: FilterQuery<T>): {
|
|
18
19
|
where: FilterQuery<T>;
|
package/utils/upsert-utils.js
CHANGED
|
@@ -89,11 +89,15 @@ export function getOnCreateGeneratedFields(meta, data) {
|
|
|
89
89
|
.map(prop => prop.name);
|
|
90
90
|
}
|
|
91
91
|
/** @internal */
|
|
92
|
-
export function getOnConflictReturningFields(meta, data, uniqueFields, options) {
|
|
92
|
+
export function getOnConflictReturningFields(meta, data, uniqueFields, options, platform) {
|
|
93
93
|
/* v8 ignore next */
|
|
94
94
|
if (!meta) {
|
|
95
95
|
return '*';
|
|
96
96
|
}
|
|
97
|
+
// props with explicit `returning` hint are reloaded even when supplied, as the database might normalize them
|
|
98
|
+
const returning = platform?.usesReturningStatement() || platform?.usesOutputStatement()
|
|
99
|
+
? meta.props.filter(p => p.returning).map(p => p.name)
|
|
100
|
+
: [];
|
|
97
101
|
const keys = meta.comparableProps
|
|
98
102
|
.filter(p => {
|
|
99
103
|
if (p.lazy || p.embeddable) {
|
|
@@ -112,17 +116,17 @@ export function getOnConflictReturningFields(meta, data, uniqueFields, options)
|
|
|
112
116
|
keys.push(meta.versionProperty);
|
|
113
117
|
}
|
|
114
118
|
if (options.onConflictAction === 'ignore') {
|
|
115
|
-
return keys;
|
|
119
|
+
return Utils.unique([...keys, ...returning]);
|
|
116
120
|
}
|
|
117
121
|
if (options.onConflictMergeFields) {
|
|
118
122
|
const onConflictMergeFields = expandFields(meta, options.onConflictMergeFields);
|
|
119
|
-
return keys.filter(key => !onConflictMergeFields.includes(key));
|
|
123
|
+
return Utils.unique([...keys.filter(key => !onConflictMergeFields.includes(key)), ...returning]);
|
|
120
124
|
}
|
|
121
125
|
if (options.onConflictExcludeFields) {
|
|
122
126
|
const onConflictExcludeFields = expandFields(meta, options.onConflictExcludeFields);
|
|
123
|
-
return [...
|
|
127
|
+
return Utils.unique([...keys, ...onConflictExcludeFields, ...returning]);
|
|
124
128
|
}
|
|
125
|
-
return keys.filter(key => !(key in data));
|
|
129
|
+
return Utils.unique([...keys.filter(key => !(key in data)), ...returning]);
|
|
126
130
|
}
|
|
127
131
|
function getPropertyValue(obj, key) {
|
|
128
132
|
if (!key.includes('.')) {
|