@mikro-orm/core 7.1.9-dev.9 → 7.1.10-dev.0

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.
@@ -834,7 +834,7 @@ export class MetadataDiscovery {
834
834
  pivotMeta.properties[primaryProp.name] = primaryProp;
835
835
  pivotMeta.compositePK = false;
836
836
  }
837
- const discriminatorProp = this.createPivotScalarProperty(discriminatorColumn, [this.#platform.getVarcharTypeDeclarationSQL(prop)], [discriminatorColumn], { type: 'string', primary: !isCompositePK, nullable: false });
837
+ const discriminatorProp = this.createPivotScalarProperty(discriminatorColumn, [this.#platform.getVarcharTypeDeclarationSQL(prop)], [discriminatorColumn], { type: 'string', primary: !prop.fixedOrder, nullable: false });
838
838
  this.initFieldName(discriminatorProp);
839
839
  pivotMeta.properties[discriminatorColumn] = discriminatorProp;
840
840
  const columnTypes = this.getPrimaryKeyColumnTypes(meta);
@@ -849,7 +849,7 @@ export class MetadataDiscovery {
849
849
  pivotMeta.properties[prop.discriminator] = this.createPivotScalarProperty(prop.discriminator, columnTypes, [...prop.joinColumns], { type: meta.className, persist: false });
850
850
  }
851
851
  else {
852
- pivotMeta.properties[prop.discriminator] = this.createPivotScalarProperty(prop.discriminator, columnTypes, [...prop.joinColumns], { type: meta.className, primary: true, nullable: false });
852
+ pivotMeta.properties[prop.discriminator] = this.createPivotScalarProperty(prop.discriminator, columnTypes, [...prop.joinColumns], { type: meta.className, primary: !prop.fixedOrder, nullable: false });
853
853
  }
854
854
  pivotMeta.properties[targetMeta.className + '_inverse'] = this.definePivotProperty(prop, targetMeta.className + '_inverse', targetMeta.class, prop.discriminator, false, false);
855
855
  // Create virtual M:1 relation to the polymorphic owner for single-query join loading
@@ -871,11 +871,11 @@ export class MetadataDiscovery {
871
871
  const discriminatorColumn = prop.discriminatorColumn;
872
872
  const targets = prop.polymorphTargets;
873
873
  pivotMeta.properties[meta.name + '_owner'] = this.definePivotProperty(prop, meta.name + '_owner', meta.class, prop.discriminator, true, false);
874
- const discriminatorProp = this.createPivotScalarProperty(discriminatorColumn, [this.#platform.getVarcharTypeDeclarationSQL(prop)], [discriminatorColumn], { type: 'string', primary: true, nullable: false });
874
+ const discriminatorProp = this.createPivotScalarProperty(discriminatorColumn, [this.#platform.getVarcharTypeDeclarationSQL(prop)], [discriminatorColumn], { type: 'string', primary: !prop.fixedOrder, nullable: false });
875
875
  this.initFieldName(discriminatorProp);
876
876
  pivotMeta.properties[discriminatorColumn] = discriminatorProp;
877
877
  const firstTargetColumnTypes = this.getPrimaryKeyColumnTypes(targets[0]);
878
- pivotMeta.properties[prop.discriminator] = this.createPivotScalarProperty(prop.discriminator, firstTargetColumnTypes, [...prop.inverseJoinColumns], { type: targets[0].className, primary: true, nullable: false });
878
+ pivotMeta.properties[prop.discriminator] = this.createPivotScalarProperty(prop.discriminator, firstTargetColumnTypes, [...prop.inverseJoinColumns], { type: targets[0].className, primary: !prop.fixedOrder, nullable: false });
879
879
  pivotMeta.polymorphicDiscriminatorMap ??= {};
880
880
  for (const targetMeta of targets) {
881
881
  const relationName = `${prop.discriminator}_${targetMeta.tableName}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
- "version": "7.1.9-dev.9",
3
+ "version": "7.1.10-dev.0",
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",
@@ -29,15 +29,18 @@ export class ChangeSet {
29
29
  else {
30
30
  this.primaryKey = this.originalEntity[this.meta.primaryKeys[0]];
31
31
  }
32
- if (!this.meta.compositePK &&
33
- this.meta.getPrimaryProp().targetMeta?.compositePK &&
34
- typeof this.primaryKey === 'object' &&
35
- this.primaryKey !== null) {
36
- this.primaryKey = this.meta.getPrimaryProp().targetMeta.primaryKeys.map(childPK => {
37
- return this.primaryKey[childPK];
38
- });
32
+ const primaryProp = this.meta.getPrimaryProp();
33
+ const relationPK = !this.meta.compositePK && !!primaryProp.targetMeta?.compositePK;
34
+ // arrays are already the ordered tuple of the target's primary keys
35
+ if (relationPK && Utils.isPlainObject(this.primaryKey)) {
36
+ const pk = this.primaryKey;
37
+ this.primaryKey = primaryProp.targetMeta.primaryKeys.map(childPK => pk[childPK]);
39
38
  }
40
39
  if (object && this.primaryKey != null) {
40
+ // the whole tuple belongs to the single relation PK, it must not be spread over the (single) PK prop
41
+ if (relationPK) {
42
+ return { [primaryProp.name]: this.primaryKey };
43
+ }
41
44
  return Utils.primaryKeyToObject(this.meta, this.primaryKey);
42
45
  }
43
46
  return this.primaryKey ?? null;
@@ -3,7 +3,7 @@ import { PolymorphicRef } from '../entity/PolymorphicRef.js';
3
3
  import { helper } from '../entity/wrap.js';
4
4
  import { ChangeSetType } from './ChangeSet.js';
5
5
  import { isRaw } from '../utils/RawQueryFragment.js';
6
- import { Utils } from '../utils/Utils.js';
6
+ import { equals, Utils } from '../utils/Utils.js';
7
7
  import { OptimisticLockError, ValidationError } from '../errors.js';
8
8
  import { ReferenceKind } from '../enums.js';
9
9
  /** @internal Executes change sets against the database, handling inserts, updates, and deletes. */
@@ -237,7 +237,8 @@ export class ChangeSetPersister {
237
237
  for (const changeSet of changeSets) {
238
238
  if (res.rows) {
239
239
  const row = map.get(helper(changeSet.entity).getSerializedPrimaryKey());
240
- this.mapReturnedValues(changeSet.entity, changeSet.payload, row, meta);
240
+ // STI batches can mix child types, so map through the change set's own metadata
241
+ this.mapReturnedValues(changeSet.entity, changeSet.payload, row, changeSet.meta);
241
242
  }
242
243
  changeSet.persisted = true;
243
244
  }
@@ -337,7 +338,8 @@ export class ChangeSetPersister {
337
338
  });
338
339
  const res = await this.#driver.find(meta.root.class, { $or }, options);
339
340
  if (res.length !== changeSets.length) {
340
- const compare = (a, b, keys) => keys.every(k => a[k] === b[k]);
341
+ // a FK pointing to a composite PK is an array, so the values need to be compared deeply
342
+ const compare = (a, b, keys) => keys.every(k => equals(a[k], b[k]));
341
343
  const entity = changeSets.find(cs => {
342
344
  return !res.some(row => compare(Utils.getPrimaryKeyCond(cs.entity, primaryKeys), row, primaryKeys));
343
345
  }).entity;
@@ -378,7 +380,8 @@ export class ChangeSetPersister {
378
380
  changeSets.forEach(cs => {
379
381
  Utils.keys(cs.payload).forEach(k => {
380
382
  if (isRaw(cs.payload[k]) && isRaw(cs.entity[k])) {
381
- returning.add(meta.properties[k]);
383
+ // STI batches can mix child types, so the property might not exist on `meta`
384
+ returning.add(cs.meta.properties[k]);
382
385
  }
383
386
  });
384
387
  });
@@ -403,12 +406,14 @@ export class ChangeSetPersister {
403
406
  options = this.prepareOptions(meta, options, {
404
407
  fields: Utils.unique(reloadProps.map(prop => prop.name)),
405
408
  });
406
- const data = await this.#driver.find(meta.class, { [pk]: { $in: pks } }, options);
409
+ // a mixed STI batch shares one table but the child discriminator would filter out the siblings
410
+ const target = changeSets.some(cs => cs.meta !== meta) && meta.root.discriminatorColumn ? meta.root : meta;
411
+ const data = await this.#driver.find(target.class, { [pk]: { $in: pks } }, options);
407
412
  const map = new Map();
408
413
  data.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, false, this.#platform, true), item));
409
414
  for (const changeSet of changeSets) {
410
415
  const data = map.get(helper(changeSet.entity).getSerializedPrimaryKey());
411
- this.#hydrator.hydrate(changeSet.entity, meta, data, this.#factory, 'full', false, true);
416
+ this.#hydrator.hydrate(changeSet.entity, changeSet.meta, data, this.#factory, 'full', false, true);
412
417
  Object.assign(changeSet.payload, data); // merge to the changeset payload, so it gets saved to the entity snapshot
413
418
  }
414
419
  }
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.1.9-dev.9';
156
+ static #ORM_VERSION = '7.1.10-dev.0';
157
157
  /**
158
158
  * Checks if the argument is instance of `Object`. Returns false for arrays.
159
159
  */
@@ -462,7 +462,9 @@ export class Utils {
462
462
  }
463
463
  static getPrimaryKeyCond(entity, primaryKeys) {
464
464
  const cond = primaryKeys.reduce((o, pk) => {
465
- o[pk] = Utils.extractPK(entity[pk]);
465
+ const value = entity[pk];
466
+ // FKs pointing to a composite PK are arrays, which `extractPK` rejects
467
+ o[pk] = Utils.isPrimaryKey(value, true) ? value : Utils.extractPK(value);
466
468
  return o;
467
469
  }, {});
468
470
  if (Object.values(cond).some(v => v === null)) {