@mikro-orm/core 7.1.15-dev.14 → 7.1.15-dev.16

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
- "version": "7.1.15-dev.14",
3
+ "version": "7.1.15-dev.16",
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/typings.d.ts CHANGED
@@ -1052,6 +1052,10 @@ export declare class EntityMetadata<Entity = any, Class extends EntityCtor<Entit
1052
1052
  removeProperty(name: string, sync?: boolean): void;
1053
1053
  /** For TPT entities, the version column exists only on the table of the entity that declares it. */
1054
1054
  ownsVersionProperty(): boolean;
1055
+ /** For TPT entities, concurrency check columns exist only on the table of the entity that declares them. */
1056
+ getOwnConcurrencyCheckKeys(): EntityKey<Entity>[];
1057
+ /** Whether updates of this table are guarded by a version property or concurrency check columns it owns. */
1058
+ hasOptimisticLock(): boolean;
1055
1059
  getPrimaryProps(flatten?: boolean): EntityProperty<Entity>[];
1056
1060
  getPrimaryProp(): EntityProperty<Entity>;
1057
1061
  /**
package/typings.js CHANGED
@@ -86,6 +86,18 @@ export class EntityMetadata {
86
86
  }
87
87
  return this.inheritanceType !== 'tpt' || !this.ownProps || this.ownProps.some(p => p.name === this.versionProperty);
88
88
  }
89
+ /** For TPT entities, concurrency check columns exist only on the table of the entity that declares them. */
90
+ getOwnConcurrencyCheckKeys() {
91
+ const keys = [...this.concurrencyCheckKeys];
92
+ if (this.inheritanceType !== 'tpt' || !this.ownProps) {
93
+ return keys;
94
+ }
95
+ return keys.filter(key => this.ownProps.some(p => p.name === key));
96
+ }
97
+ /** Whether updates of this table are guarded by a version property or concurrency check columns it owns. */
98
+ hasOptimisticLock() {
99
+ return this.ownsVersionProperty() || this.getOwnConcurrencyCheckKeys().length > 0;
100
+ }
89
101
  getPrimaryProps(flatten = false) {
90
102
  const pks = this.primaryKeys.map(pk => this.properties[pk]);
91
103
  if (flatten) {
@@ -204,13 +204,14 @@ export class ChangeSetPersister {
204
204
  }
205
205
  checkConcurrencyKeys(meta, changeSet, cond) {
206
206
  const tmp = [];
207
- for (const key of meta.concurrencyCheckKeys) {
207
+ const keys = meta.getOwnConcurrencyCheckKeys();
208
+ for (const key of keys) {
208
209
  cond[key] = changeSet.originalEntity[key];
209
210
  if (changeSet.payload[key]) {
210
211
  tmp.push(key);
211
212
  }
212
213
  }
213
- if (tmp.length === 0 && meta.concurrencyCheckKeys.size > 0) {
214
+ if (tmp.length === 0 && keys.length > 0) {
214
215
  throw OptimisticLockError.lockFailed(changeSet.entity);
215
216
  }
216
217
  }
@@ -303,7 +304,7 @@ export class ChangeSetPersister {
303
304
  options = this.prepareOptions(meta, options, {
304
305
  convertCustomTypes: false,
305
306
  });
306
- if (meta.concurrencyCheckKeys.size === 0 &&
307
+ if (meta.getOwnConcurrencyCheckKeys().length === 0 &&
307
308
  (!meta.ownsVersionProperty() || changeSet.entity[meta.versionProperty] == null)) {
308
309
  return this.#driver.nativeUpdate(changeSet.meta.class, cond, changeSet.payload, options);
309
310
  }
@@ -314,21 +315,22 @@ export class ChangeSetPersister {
314
315
  return this.#driver.nativeUpdate(changeSet.meta.class, cond, changeSet.payload, options);
315
316
  }
316
317
  async checkOptimisticLocks(meta, changeSets, options) {
317
- if (meta.concurrencyCheckKeys.size === 0 &&
318
+ const concurrencyCheckKeys = meta.getOwnConcurrencyCheckKeys();
319
+ if (concurrencyCheckKeys.length === 0 &&
318
320
  (!meta.ownsVersionProperty() || changeSets.every(cs => cs.entity[meta.versionProperty] == null))) {
319
321
  return;
320
322
  }
321
323
  // skip entity references as they don't have version values loaded
322
324
  changeSets = changeSets.filter(cs => helper(cs.entity).__initialized);
325
+ const primaryKeys = meta.primaryKeys.concat(...concurrencyCheckKeys);
323
326
  const $or = changeSets.map(cs => {
324
- const cond = Utils.getPrimaryKeyCond(cs.originalEntity, meta.primaryKeys.concat(...meta.concurrencyCheckKeys));
327
+ const cond = Utils.getPrimaryKeyCond(cs.originalEntity, primaryKeys);
325
328
  if (meta.ownsVersionProperty()) {
326
329
  // @ts-ignore
327
330
  cond[meta.versionProperty] = this.#platform.convertVersionValue(cs.entity[meta.versionProperty], meta.properties[meta.versionProperty]);
328
331
  }
329
332
  return cond;
330
333
  });
331
- const primaryKeys = meta.primaryKeys.concat(...meta.concurrencyCheckKeys);
332
334
  options = this.prepareOptions(meta, options, {
333
335
  fields: primaryKeys,
334
336
  orderBy: meta.primaryKeys.reduce((o, pk) => {
@@ -349,7 +351,7 @@ export class ChangeSetPersister {
349
351
  }
350
352
  }
351
353
  checkOptimisticLock(meta, changeSet, res) {
352
- if ((meta.versionProperty || meta.concurrencyCheckKeys.size > 0) && res && !res.affectedRows) {
354
+ if ((meta.ownsVersionProperty() || meta.getOwnConcurrencyCheckKeys().length > 0) && res && !res.affectedRows) {
353
355
  throw OptimisticLockError.lockFailed(changeSet.entity);
354
356
  }
355
357
  }
@@ -706,8 +706,8 @@ export class UnitOfWork {
706
706
  payload[pk] = identifiers[i] ?? originalChangeSet.payload[pk];
707
707
  }
708
708
  }
709
- // the table declaring the version property still needs its bump and lock check
710
- if (!isCreate && Object.keys(payload).length === 0 && !current.ownsVersionProperty()) {
709
+ // the table declaring the version property or a concurrency check still needs its bump and lock check
710
+ if (!isCreate && Object.keys(payload).length === 0 && !current.hasOptimisticLock()) {
711
711
  current = current.tptParent;
712
712
  continue;
713
713
  }
@@ -1211,7 +1211,7 @@ export class UnitOfWork {
1211
1211
  // Skip stub TPT changesets with empty payload (e.g. leaf with no own-property changes on UPDATE)
1212
1212
  if ((cs.type === ChangeSetType.UPDATE || cs.type === ChangeSetType.UPDATE_EARLY) &&
1213
1213
  !Utils.hasObjectKeys(cs.payload) &&
1214
- !cs.meta.ownsVersionProperty()) {
1214
+ !cs.meta.hasOptimisticLock()) {
1215
1215
  return;
1216
1216
  }
1217
1217
  const group = groups[cs.type];
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.15-dev.14';
156
+ static #ORM_VERSION = '7.1.15-dev.16';
157
157
  /**
158
158
  * Checks if the argument is instance of `Object`. Returns false for arrays.
159
159
  */
@@ -138,7 +138,14 @@ function getPropertyValue(obj, key) {
138
138
  }
139
139
  /** @internal */
140
140
  export function getWhereCondition(meta, onConflictFields, data, where) {
141
- const unique = onConflictFields ?? meta.props.filter(p => p.unique).map(p => p.name);
141
+ // TPT children do not inherit the unique flags and indexes of their parent tables
142
+ const uniqueProps = new Set();
143
+ const uniques = [];
144
+ for (let current = meta; current; current = current.tptParent) {
145
+ current.props.filter(p => p.unique).forEach(p => uniqueProps.add(p.name));
146
+ uniques.push(...current.uniques);
147
+ }
148
+ const unique = onConflictFields ?? [...uniqueProps];
142
149
  const propIndex = !isRaw(unique) &&
143
150
  unique.findIndex(p => data[p] ?? data[p.substring(0, p.indexOf('.'))] != null);
144
151
  if (onConflictFields || where == null) {
@@ -152,8 +159,8 @@ export function getWhereCondition(meta, onConflictFields, data, where) {
152
159
  }
153
160
  where = { [key]: getPropertyValue(data, unique[propIndex]) };
154
161
  }
155
- else if (meta.uniques.length > 0) {
156
- for (const u of meta.uniques) {
162
+ else {
163
+ for (const u of uniques) {
157
164
  if (Utils.asArray(u.properties).every(p => data[p] != null)) {
158
165
  where = Utils.asArray(u.properties).reduce((o, key) => {
159
166
  o[key] = data[key];