@mikro-orm/core 7.1.13-dev.1 → 7.1.13-dev.11
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 +15 -1
- package/entity/EntityLoader.js +6 -2
- package/metadata/MetadataDiscovery.js +15 -8
- package/package.json +1 -1
- package/typings.js +4 -1
- package/utils/Utils.d.ts +1 -1
- package/utils/Utils.js +5 -2
- package/utils/upsert-utils.d.ts +9 -1
- package/utils/upsert-utils.js +16 -0
package/EntityManager.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getOnConflictReturningFields, getWhereCondition, resetUntouchedCollections } from './utils/upsert-utils.js';
|
|
1
|
+
import { getOnConflictReturningFields, getOnCreateGeneratedFields, getWhereCondition, resetUntouchedCollections, } from './utils/upsert-utils.js';
|
|
2
2
|
import { Utils } from './utils/Utils.js';
|
|
3
3
|
import { Cursor } from './utils/Cursor.js';
|
|
4
4
|
import { QueryHelper } from './utils/QueryHelper.js';
|
|
@@ -793,6 +793,7 @@ export class EntityManager {
|
|
|
793
793
|
}
|
|
794
794
|
const meta = this.metadata.get(entityName);
|
|
795
795
|
const convertCustomTypes = !Utils.isEntity(data);
|
|
796
|
+
let generatedFields = [];
|
|
796
797
|
if (Utils.isEntity(data)) {
|
|
797
798
|
entity = data;
|
|
798
799
|
if (helper(entity).__managed && helper(entity).__em === em && !this.config.get('upsertManaged')) {
|
|
@@ -800,6 +801,7 @@ export class EntityManager {
|
|
|
800
801
|
return entity;
|
|
801
802
|
}
|
|
802
803
|
where = helper(entity).getPrimaryKey();
|
|
804
|
+
generatedFields = getOnCreateGeneratedFields(meta, entity);
|
|
803
805
|
em.#entityFactory.assignDefaultValues(entity, meta);
|
|
804
806
|
data = em.#comparator.prepareEntity(entity);
|
|
805
807
|
}
|
|
@@ -812,6 +814,7 @@ export class EntityManager {
|
|
|
812
814
|
return em.assign(exists, data);
|
|
813
815
|
}
|
|
814
816
|
}
|
|
817
|
+
generatedFields = getOnCreateGeneratedFields(meta, data);
|
|
815
818
|
em.#entityFactory.assignDefaultValues(data, meta, true);
|
|
816
819
|
for (const key of Object.keys(data)) {
|
|
817
820
|
const prop = meta.properties[key];
|
|
@@ -820,6 +823,10 @@ export class EntityManager {
|
|
|
820
823
|
}
|
|
821
824
|
}
|
|
822
825
|
}
|
|
826
|
+
// `onCreate` generated values are for the insert clause only, they must not overwrite an existing row
|
|
827
|
+
if (generatedFields.length > 0 && !options.onConflictMergeFields) {
|
|
828
|
+
options.onConflictExcludeFields = [...(options.onConflictExcludeFields ?? []), ...generatedFields];
|
|
829
|
+
}
|
|
823
830
|
where = getWhereCondition(meta, options.onConflictFields, data, where).where;
|
|
824
831
|
data = QueryHelper.processObjectParams(data);
|
|
825
832
|
validateParams(data, 'insert data');
|
|
@@ -939,6 +946,7 @@ export class EntityManager {
|
|
|
939
946
|
}
|
|
940
947
|
const meta = this.metadata.get(entityName);
|
|
941
948
|
const convertCustomTypes = !Utils.isEntity(data[0]);
|
|
949
|
+
const generatedFields = new Set();
|
|
942
950
|
const allData = [];
|
|
943
951
|
const allWhere = [];
|
|
944
952
|
const entities = new Map();
|
|
@@ -956,6 +964,7 @@ export class EntityManager {
|
|
|
956
964
|
continue;
|
|
957
965
|
}
|
|
958
966
|
where = helper(entity).getPrimaryKey();
|
|
967
|
+
getOnCreateGeneratedFields(meta, entity).forEach(field => generatedFields.add(field));
|
|
959
968
|
em.#entityFactory.assignDefaultValues(entity, meta);
|
|
960
969
|
entitiesByAllDataIdx.set(allData.length, entity);
|
|
961
970
|
row = em.#comparator.prepareEntity(entity);
|
|
@@ -972,6 +981,7 @@ export class EntityManager {
|
|
|
972
981
|
continue;
|
|
973
982
|
}
|
|
974
983
|
}
|
|
984
|
+
getOnCreateGeneratedFields(meta, row).forEach(field => generatedFields.add(field));
|
|
975
985
|
em.#entityFactory.assignDefaultValues(row, meta, true);
|
|
976
986
|
for (const key of Object.keys(row)) {
|
|
977
987
|
const prop = meta.properties[key];
|
|
@@ -1000,6 +1010,10 @@ export class EntityManager {
|
|
|
1000
1010
|
if (entities.size === data.length) {
|
|
1001
1011
|
return [...entities.keys()];
|
|
1002
1012
|
}
|
|
1013
|
+
// `onCreate` generated values are for the insert clause only, they must not overwrite existing rows
|
|
1014
|
+
if (generatedFields.size > 0 && !options.onConflictMergeFields) {
|
|
1015
|
+
options.onConflictExcludeFields = [...(options.onConflictExcludeFields ?? []), ...generatedFields];
|
|
1016
|
+
}
|
|
1003
1017
|
if (em.eventManager.hasListeners(EventType.beforeUpsert, meta)) {
|
|
1004
1018
|
for (const dto of data) {
|
|
1005
1019
|
const entity = entitiesByData.get(dto) ?? dto;
|
package/entity/EntityLoader.js
CHANGED
|
@@ -445,7 +445,10 @@ export class EntityLoader {
|
|
|
445
445
|
}
|
|
446
446
|
}
|
|
447
447
|
}
|
|
448
|
-
|
|
448
|
+
// a missing target row means an orphaned reference, unless the query was narrowed by a populate condition
|
|
449
|
+
if ([ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind) &&
|
|
450
|
+
items.length !== children.length &&
|
|
451
|
+
Utils.isEmpty(options.where)) {
|
|
449
452
|
const nullVal = this.#em.config.get('forceUndefined') ? undefined : null;
|
|
450
453
|
const itemsMap = new Set();
|
|
451
454
|
const childrenMap = new Set();
|
|
@@ -667,7 +670,8 @@ export class EntityLoader {
|
|
|
667
670
|
}
|
|
668
671
|
return cond;
|
|
669
672
|
});
|
|
670
|
-
|
|
673
|
+
// partial extraction from `$or` is unsound — the parent may have matched via a dropped branch
|
|
674
|
+
if (child.length > 0 && (op === '$and' || child.length === where[op].length)) {
|
|
671
675
|
subCond[op] = child;
|
|
672
676
|
}
|
|
673
677
|
}
|
|
@@ -451,10 +451,13 @@ export class MetadataDiscovery {
|
|
|
451
451
|
initManyToOneFieldName(prop, name) {
|
|
452
452
|
const meta2 = prop.targetMeta;
|
|
453
453
|
const ret = [];
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
454
|
+
// with `targetKey` on a composite PK target, derive the FK field name from that property
|
|
455
|
+
// instead of the PKs (simple PK targets keep the PK based naming for backwards compatibility)
|
|
456
|
+
const referencedKeys = prop.targetKey && meta2.compositePK ? [prop.targetKey] : meta2.primaryKeys;
|
|
457
|
+
for (const referencedKey of referencedKeys) {
|
|
458
|
+
this.initFieldName(meta2.properties[referencedKey]);
|
|
459
|
+
for (const fieldName of meta2.properties[referencedKey].fieldNames) {
|
|
460
|
+
ret.push(this.#namingStrategy.joinKeyColumnName(name, fieldName, !prop.targetKey && meta2.compositePK));
|
|
458
461
|
}
|
|
459
462
|
}
|
|
460
463
|
return ret;
|
|
@@ -1050,11 +1053,12 @@ export class MetadataDiscovery {
|
|
|
1050
1053
|
}
|
|
1051
1054
|
// TPT children have their own tables that don't contain the parent's columns,
|
|
1052
1055
|
// so propagating parent indexes/uniques/checks/triggers would target missing columns.
|
|
1056
|
+
// deep equality, as the subclass items might be copies of the base class ones (e.g. with TC39 decorators)
|
|
1053
1057
|
if (meta.inheritanceType !== 'tpt' || !meta.tptParent) {
|
|
1054
|
-
meta.indexes = Utils.unique([...base.indexes, ...meta.indexes]);
|
|
1055
|
-
meta.uniques = Utils.unique([...base.uniques, ...meta.uniques]);
|
|
1056
|
-
meta.checks = Utils.unique([...base.checks, ...meta.checks]);
|
|
1057
|
-
meta.triggers = Utils.unique([...base.triggers, ...meta.triggers]);
|
|
1058
|
+
meta.indexes = Utils.unique([...base.indexes, ...meta.indexes], Utils.equals);
|
|
1059
|
+
meta.uniques = Utils.unique([...base.uniques, ...meta.uniques], Utils.equals);
|
|
1060
|
+
meta.checks = Utils.unique([...base.checks, ...meta.checks], Utils.equals);
|
|
1061
|
+
meta.triggers = Utils.unique([...base.triggers, ...meta.triggers], Utils.equals);
|
|
1058
1062
|
}
|
|
1059
1063
|
const pks = Object.values(meta.properties)
|
|
1060
1064
|
.filter(p => p.primary)
|
|
@@ -1672,6 +1676,9 @@ export class MetadataDiscovery {
|
|
|
1672
1676
|
if (prop.persist === false || prop.nativeEnumName || !prop.items?.every(item => typeof item === 'string')) {
|
|
1673
1677
|
continue;
|
|
1674
1678
|
}
|
|
1679
|
+
if (prop.customType instanceof t.json || ['json', 'jsonb'].includes(prop.columnTypes?.[0])) {
|
|
1680
|
+
continue;
|
|
1681
|
+
}
|
|
1675
1682
|
this.initFieldName(prop);
|
|
1676
1683
|
let expression = null;
|
|
1677
1684
|
if (prop.enum) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/core",
|
|
3
|
-
"version": "7.1.13-dev.
|
|
3
|
+
"version": "7.1.13-dev.11",
|
|
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.js
CHANGED
|
@@ -216,7 +216,10 @@ export class EntityMetadata {
|
|
|
216
216
|
if (config) {
|
|
217
217
|
const platform = config.getPlatform();
|
|
218
218
|
for (const prop of this.props) {
|
|
219
|
-
if (prop.enum &&
|
|
219
|
+
if (prop.enum &&
|
|
220
|
+
!prop.nativeEnumName &&
|
|
221
|
+
prop.items?.every(item => typeof item === 'string') &&
|
|
222
|
+
!['json', 'jsonb'].includes(prop.columnTypes?.[0])) {
|
|
220
223
|
const name = platform.getIndexName(this.tableName, prop.fieldNames, 'check');
|
|
221
224
|
const exists = this.checks.findIndex(check => check.name === name);
|
|
222
225
|
if (exists !== -1) {
|
package/utils/Utils.d.ts
CHANGED
|
@@ -58,7 +58,7 @@ export declare class Utils {
|
|
|
58
58
|
/**
|
|
59
59
|
* Gets array without duplicates.
|
|
60
60
|
*/
|
|
61
|
-
static unique<T = string>(items: T[]): T[];
|
|
61
|
+
static unique<T = string>(items: T[], equals?: (a: T, b: T) => boolean): T[];
|
|
62
62
|
/**
|
|
63
63
|
* Merges all sources into the target recursively.
|
|
64
64
|
*/
|
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.13-dev.
|
|
156
|
+
static #ORM_VERSION = '7.1.13-dev.11';
|
|
157
157
|
/**
|
|
158
158
|
* Checks if the argument is instance of `Object`. Returns false for arrays.
|
|
159
159
|
*/
|
|
@@ -216,10 +216,13 @@ export class Utils {
|
|
|
216
216
|
/**
|
|
217
217
|
* Gets array without duplicates.
|
|
218
218
|
*/
|
|
219
|
-
static unique(items) {
|
|
219
|
+
static unique(items, equals) {
|
|
220
220
|
if (items.length < 2) {
|
|
221
221
|
return items;
|
|
222
222
|
}
|
|
223
|
+
if (equals) {
|
|
224
|
+
return items.filter((a, idx) => items.findIndex(b => equals(a, b)) === idx);
|
|
225
|
+
}
|
|
223
226
|
return [...new Set(items)];
|
|
224
227
|
}
|
|
225
228
|
/**
|
package/utils/upsert-utils.d.ts
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
import type { EntityData, EntityMetadata, FilterQuery } from '../typings.js';
|
|
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
4
|
/** @internal */
|
|
5
5
|
export declare function getOnConflictFields<T>(meta: EntityMetadata<T> | undefined, data: EntityData<T>, uniqueFields: (keyof T)[] | Raw, options: UpsertOptions<T>): (keyof T)[];
|
|
6
|
+
/**
|
|
7
|
+
* Detects properties that will get their value generated by an `onCreate` hook during the upsert,
|
|
8
|
+
* i.e. those with an `onCreate` hook and no value provided. Such values are meant for the insert
|
|
9
|
+
* clause only and must not overwrite an existing row via the `on conflict do update set` clause.
|
|
10
|
+
* The property filter mirrors `EntityFactory.assignDefaultValues`.
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
export declare function getOnCreateGeneratedFields<T extends object>(meta: EntityMetadata<T>, data: T | EntityData<T>): EntityKey<T>[];
|
|
6
14
|
/** @internal */
|
|
7
15
|
export declare function getOnConflictReturningFields<T, P extends string>(meta: EntityMetadata<T> | undefined, data: EntityData<T>, uniqueFields: (keyof T)[] | Raw, options: UpsertOptions<T, P>): (keyof T)[] | '*';
|
|
8
16
|
/** @internal */
|
package/utils/upsert-utils.js
CHANGED
|
@@ -72,6 +72,22 @@ export function getOnConflictFields(meta, data, uniqueFields, options) {
|
|
|
72
72
|
}
|
|
73
73
|
return keys;
|
|
74
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Detects properties that will get their value generated by an `onCreate` hook during the upsert,
|
|
77
|
+
* i.e. those with an `onCreate` hook and no value provided. Such values are meant for the insert
|
|
78
|
+
* clause only and must not overwrite an existing row via the `on conflict do update set` clause.
|
|
79
|
+
* The property filter mirrors `EntityFactory.assignDefaultValues`.
|
|
80
|
+
* @internal
|
|
81
|
+
*/
|
|
82
|
+
export function getOnCreateGeneratedFields(meta, data) {
|
|
83
|
+
return meta.props
|
|
84
|
+
.filter(prop => prop.onCreate &&
|
|
85
|
+
!prop.embedded &&
|
|
86
|
+
![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
87
|
+
!(prop.getter && !prop.setter) &&
|
|
88
|
+
data[prop.name] == null)
|
|
89
|
+
.map(prop => prop.name);
|
|
90
|
+
}
|
|
75
91
|
/** @internal */
|
|
76
92
|
export function getOnConflictReturningFields(meta, data, uniqueFields, options) {
|
|
77
93
|
/* v8 ignore next */
|