@mikro-orm/core 7.2.2-dev.1 → 7.2.2-dev.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.
package/EntityManager.js CHANGED
@@ -889,7 +889,7 @@ export class EntityManager {
889
889
  const name = Utils.className(entityName);
890
890
  /* v8 ignore next */
891
891
  where = Utils.isEntity(where) ? helper(where).getPrimaryKey() : where;
892
- throw failHandler(name, where);
892
+ throw failHandler(name, QueryHelper.processParams(where));
893
893
  }
894
894
  return entity;
895
895
  }
@@ -995,6 +995,9 @@ export class EntityManager {
995
995
  const uniqueFields = options.onConflictFields ??
996
996
  (Utils.isPlainObject(where) ? Object.keys(where) : meta.primaryKeys);
997
997
  const returning = getOnConflictReturningFields(meta, data, uniqueFields, options);
998
+ if (options.onConflictWhere && !ret.row) {
999
+ returning.push(...Object.keys(data));
1000
+ }
998
1001
  if (options.onConflictAction === 'ignore' ||
999
1002
  !helper(entity).hasPrimaryKey() ||
1000
1003
  (returning.length > 0 && !(this.getPlatform().usesReturningStatement() && ret.row))) {
@@ -1088,6 +1091,8 @@ export class EntityManager {
1088
1091
  }
1089
1092
  return ret;
1090
1093
  }
1094
+ // the rows are replaced with their processed copies below, keep the caller's array intact
1095
+ data = [...data];
1091
1096
  const meta = this.metadata.get(entityName);
1092
1097
  const convertCustomTypes = !Utils.isEntity(data[0]);
1093
1098
  const generatedFields = new Set();
@@ -1147,6 +1152,10 @@ export class EntityManager {
1147
1152
  platform: this.getPlatform(),
1148
1153
  });
1149
1154
  row = QueryHelper.processObjectParams(row);
1155
+ // hooks and the result merging need `data[i]` to be the same object as the processed row
1156
+ if (!Utils.isEntity(data[i])) {
1157
+ data[i] = row;
1158
+ }
1150
1159
  validateParams(row, 'insert data');
1151
1160
  allData.push(row);
1152
1161
  allWhere.push(where);
@@ -1177,9 +1186,9 @@ export class EntityManager {
1177
1186
  entitiesByData.clear();
1178
1187
  const loadPK = new Map();
1179
1188
  allData.forEach((row, i) => {
1180
- em.#unitOfWork
1181
- .getChangeSetPersister()
1182
- .mapReturnedValues(Utils.isEntity(data[i]) ? data[i] : null, Utils.isEntity(data[i]) ? {} : data[i], res.rows?.[i], meta, true);
1189
+ em.#unitOfWork.getChangeSetPersister().mapReturnedValues(Utils.isEntity(data[i]) ? data[i] : null, Utils.isEntity(data[i]) ? {} : data[i],
1190
+ // A suppressed conflict omits its row; positional mapping would hydrate a different entity.
1191
+ res.rows?.length === allData.length ? res.rows[i] : undefined, meta, true);
1183
1192
  const entity = Utils.isEntity(data[i])
1184
1193
  ? data[i]
1185
1194
  : em.#entityFactory.create(entityName, row, {
@@ -1198,6 +1207,9 @@ export class EntityManager {
1198
1207
  // oxfmt-ignore
1199
1208
  const uniqueFields = options.onConflictFields ?? (Utils.isPlainObject(allWhere[0]) ? Object.keys(allWhere[0]).flatMap(key => Utils.splitPrimaryKeys(key)) : meta.primaryKeys);
1200
1209
  const returning = getOnConflictReturningFields(meta, data[0], uniqueFields, options);
1210
+ if (options.onConflictWhere && res.rows?.length !== allData.length) {
1211
+ returning.push(...meta.comparableProps.filter(p => !p.lazy && !p.embeddable).map(p => p.name));
1212
+ }
1201
1213
  const reloadFields = returning.length > 0 && !(this.getPlatform().usesReturningStatement() && res.rows?.length === data.length);
1202
1214
  if (options.onConflictAction === 'ignore' || (!res.rows?.length && loadPK.size > 0) || reloadFields) {
1203
1215
  const unique = meta.hydrateProps.filter(p => !p.lazy).map(p => p.name);
@@ -1229,8 +1241,9 @@ export class EntityManager {
1229
1241
  const row = data2.find(row => {
1230
1242
  const tmp = {};
1231
1243
  add.forEach(k => {
1232
- if (!meta.properties[k]?.primary) {
1233
- tmp[k] = row[k];
1244
+ const prop = meta.properties[k];
1245
+ if (!prop?.primary) {
1246
+ tmp[k] = prop?.customType ? prop.customType.convertToDatabaseValue(row[k], this.getPlatform()) : row[k];
1234
1247
  }
1235
1248
  });
1236
1249
  return this.#comparator.matching(entityName, cond, tmp);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
- "version": "7.2.2-dev.1",
3
+ "version": "7.2.2-dev.3",
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",
@@ -35,6 +35,8 @@ export declare class QueryHelper {
35
35
  */
36
36
  static liftGroupOperators<T extends object>(where: Dictionary, meta: EntityMetadata<T>, metadata: MetadataStorage, key?: string): string | undefined;
37
37
  static inlinePrimaryKeyObjects<T extends object>(where: Dictionary, meta: EntityMetadata<T>, metadata: MetadataStorage, key?: string): boolean;
38
+ /** Copies the plain object and array structure of the condition, keeping the leaf values (e.g. entities) by reference. */
39
+ private static cloneWhere;
38
40
  static processWhere<T extends object>(options: ProcessWhereOptions<T>): FilterQuery<T>;
39
41
  static getActiveFilters<T>(meta: EntityMetadata<T>, options: FilterOptions | undefined, filters: Dictionary<FilterDef>): FilterDef[];
40
42
  /** @internal Sentinel wrapping for arguments accessed while statically resolving an `rls` filter condition. */
@@ -56,15 +56,16 @@ export class QueryHelper {
56
56
  return params.map(item => QueryHelper.processParams(item));
57
57
  }
58
58
  if (Utils.isPlainObject(params)) {
59
- QueryHelper.processObjectParams(params);
59
+ return QueryHelper.processObjectParams(params);
60
60
  }
61
61
  return params;
62
62
  }
63
63
  static processObjectParams(params = {}) {
64
+ const ret = (Array.isArray(params) ? [...params] : { ...params });
64
65
  Utils.getObjectQueryKeys(params).forEach(k => {
65
- params[k] = QueryHelper.processParams(params[k]);
66
+ ret[k] = QueryHelper.processParams(params[k]);
66
67
  });
67
- return params;
68
+ return ret;
68
69
  }
69
70
  /**
70
71
  * converts `{ account: { $or: [ [Object], [Object] ] } }`
@@ -167,10 +168,26 @@ export class QueryHelper {
167
168
  });
168
169
  return false;
169
170
  }
171
+ /** Copies the plain object and array structure of the condition, keeping the leaf values (e.g. entities) by reference. */
172
+ static cloneWhere(where) {
173
+ if (Array.isArray(where)) {
174
+ return where.map(item => QueryHelper.cloneWhere(item));
175
+ }
176
+ if (!Utils.isPlainObject(where)) {
177
+ return where;
178
+ }
179
+ const ret = {};
180
+ Utils.getObjectQueryKeys(where).forEach(k => (ret[k] = QueryHelper.cloneWhere(where[k])));
181
+ return ret;
182
+ }
170
183
  static processWhere(options) {
171
184
  // eslint-disable-next-line prefer-const
172
185
  let { where, entityName, metadata, platform, aliased = true, convertCustomTypes = true, root = true } = options;
173
186
  const meta = metadata.find(entityName);
187
+ // the condition is normalized in place, so work on a copy to keep the one the caller passed in intact
188
+ if (root) {
189
+ where = QueryHelper.cloneWhere(where);
190
+ }
174
191
  // inline PK-only objects in M:N queries, so we don't join the target entity when not needed
175
192
  if (meta && root) {
176
193
  QueryHelper.liftGroupOperators(where, meta, metadata);
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.1';
156
+ static #ORM_VERSION = '7.2.2-dev.3';
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}`;