@mikro-orm/core 7.0.0-rc.2 → 7.0.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.
- package/EntityManager.d.ts +4 -16
- package/EntityManager.js +248 -181
- package/MikroORM.d.ts +4 -6
- package/MikroORM.js +24 -24
- package/README.md +5 -4
- package/cache/FileCacheAdapter.d.ts +1 -5
- package/cache/FileCacheAdapter.js +22 -24
- package/cache/GeneratedCacheAdapter.d.ts +1 -1
- package/cache/GeneratedCacheAdapter.js +6 -6
- package/cache/MemoryCacheAdapter.d.ts +1 -2
- package/cache/MemoryCacheAdapter.js +8 -8
- package/cache/index.d.ts +1 -1
- package/cache/index.js +0 -1
- package/connections/Connection.d.ts +1 -0
- package/connections/Connection.js +43 -14
- package/drivers/DatabaseDriver.d.ts +0 -2
- package/drivers/DatabaseDriver.js +28 -12
- package/drivers/IDatabaseDriver.d.ts +43 -0
- package/entity/Collection.d.ts +1 -9
- package/entity/Collection.js +124 -108
- package/entity/EntityAssigner.js +23 -11
- package/entity/EntityFactory.d.ts +1 -8
- package/entity/EntityFactory.js +79 -59
- package/entity/EntityHelper.js +25 -16
- package/entity/EntityLoader.d.ts +1 -3
- package/entity/EntityLoader.js +90 -60
- package/entity/Reference.d.ts +2 -3
- package/entity/Reference.js +48 -19
- package/entity/WrappedEntity.d.ts +4 -2
- package/entity/WrappedEntity.js +5 -1
- package/entity/defineEntity.d.ts +42 -85
- package/entity/utils.js +28 -26
- package/entity/validators.js +2 -1
- package/enums.d.ts +2 -1
- package/enums.js +13 -17
- package/errors.d.ts +11 -11
- package/errors.js +8 -8
- package/events/EventManager.d.ts +1 -4
- package/events/EventManager.js +26 -23
- package/events/index.d.ts +1 -1
- package/events/index.js +0 -1
- package/exceptions.js +9 -2
- package/hydration/ObjectHydrator.d.ts +1 -2
- package/hydration/ObjectHydrator.js +41 -27
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/logging/DefaultLogger.js +6 -7
- package/logging/Logger.d.ts +2 -1
- package/logging/colors.js +2 -5
- package/logging/index.d.ts +1 -1
- package/logging/index.js +0 -1
- package/metadata/EntitySchema.d.ts +3 -3
- package/metadata/EntitySchema.js +12 -2
- package/metadata/MetadataDiscovery.d.ts +1 -9
- package/metadata/MetadataDiscovery.js +251 -179
- package/metadata/MetadataProvider.js +26 -1
- package/metadata/MetadataStorage.d.ts +1 -5
- package/metadata/MetadataStorage.js +37 -39
- package/metadata/MetadataValidator.js +20 -5
- package/metadata/discover-entities.js +1 -1
- package/metadata/index.d.ts +1 -1
- package/metadata/index.js +0 -1
- package/metadata/types.d.ts +2 -2
- package/naming-strategy/AbstractNamingStrategy.js +6 -3
- package/naming-strategy/EntityCaseNamingStrategy.js +1 -1
- package/naming-strategy/index.d.ts +1 -1
- package/naming-strategy/index.js +0 -1
- package/not-supported.js +5 -1
- package/package.json +38 -38
- package/platforms/Platform.d.ts +24 -1
- package/platforms/Platform.js +106 -27
- package/serialization/EntitySerializer.js +8 -4
- package/serialization/EntityTransformer.js +4 -1
- package/serialization/SerializationContext.d.ts +4 -8
- package/serialization/SerializationContext.js +21 -16
- package/types/UuidType.d.ts +2 -0
- package/types/UuidType.js +14 -2
- package/types/index.d.ts +2 -1
- package/typings.d.ts +35 -24
- package/typings.js +9 -9
- package/unit-of-work/ChangeSet.js +4 -4
- package/unit-of-work/ChangeSetComputer.d.ts +1 -6
- package/unit-of-work/ChangeSetComputer.js +29 -27
- package/unit-of-work/ChangeSetPersister.d.ts +1 -9
- package/unit-of-work/ChangeSetPersister.js +63 -58
- package/unit-of-work/CommitOrderCalculator.d.ts +1 -4
- package/unit-of-work/CommitOrderCalculator.js +17 -15
- package/unit-of-work/IdentityMap.d.ts +2 -5
- package/unit-of-work/IdentityMap.js +18 -18
- package/unit-of-work/UnitOfWork.d.ts +12 -20
- package/unit-of-work/UnitOfWork.js +228 -191
- package/utils/AbstractMigrator.d.ts +2 -2
- package/utils/AbstractMigrator.js +10 -12
- package/utils/AbstractSchemaGenerator.js +2 -1
- package/utils/AsyncContext.js +1 -1
- package/utils/Configuration.d.ts +90 -189
- package/utils/Configuration.js +97 -77
- package/utils/Cursor.d.ts +3 -3
- package/utils/Cursor.js +8 -6
- package/utils/DataloaderUtils.js +15 -12
- package/utils/EntityComparator.d.ts +8 -15
- package/utils/EntityComparator.js +100 -92
- package/utils/QueryHelper.d.ts +16 -1
- package/utils/QueryHelper.js +108 -50
- package/utils/RawQueryFragment.d.ts +4 -4
- package/utils/RawQueryFragment.js +3 -2
- package/utils/TransactionManager.js +3 -3
- package/utils/Utils.d.ts +2 -2
- package/utils/Utils.js +39 -32
- package/utils/clone.js +5 -0
- package/utils/env-vars.js +6 -5
- package/utils/fs-utils.d.ts +3 -17
- package/utils/fs-utils.js +2 -5
- package/utils/upsert-utils.js +7 -4
|
@@ -12,68 +12,72 @@ import { colors } from '../logging/colors.js';
|
|
|
12
12
|
import { raw, Raw } from '../utils/RawQueryFragment.js';
|
|
13
13
|
import { BaseEntity } from '../entity/BaseEntity.js';
|
|
14
14
|
export class MetadataDiscovery {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
#namingStrategy;
|
|
16
|
+
#metadataProvider;
|
|
17
|
+
#logger;
|
|
18
|
+
#schemaHelper;
|
|
19
|
+
#validator = new MetadataValidator();
|
|
20
|
+
#discovered = [];
|
|
21
|
+
#metadata;
|
|
22
|
+
#platform;
|
|
23
|
+
#config;
|
|
24
24
|
constructor(metadata, platform, config) {
|
|
25
|
-
this
|
|
26
|
-
this
|
|
27
|
-
this
|
|
28
|
-
this
|
|
29
|
-
this
|
|
30
|
-
this
|
|
31
|
-
this
|
|
25
|
+
this.#metadata = metadata;
|
|
26
|
+
this.#platform = platform;
|
|
27
|
+
this.#config = config;
|
|
28
|
+
this.#namingStrategy = this.#config.getNamingStrategy();
|
|
29
|
+
this.#metadataProvider = this.#config.getMetadataProvider();
|
|
30
|
+
this.#logger = this.#config.getLogger();
|
|
31
|
+
this.#schemaHelper = this.#platform.getSchemaHelper();
|
|
32
32
|
}
|
|
33
33
|
async discover(preferTs = true) {
|
|
34
|
-
this
|
|
34
|
+
this.#discovered.length = 0;
|
|
35
35
|
const startTime = Date.now();
|
|
36
|
-
const suffix = this
|
|
37
|
-
|
|
36
|
+
const suffix = this.#metadataProvider.constructor === MetadataProvider
|
|
37
|
+
? ''
|
|
38
|
+
: `, using ${colors.cyan(this.#metadataProvider.constructor.name)}`;
|
|
39
|
+
this.#logger.log('discovery', `ORM entity discovery started${suffix}`);
|
|
38
40
|
await this.findEntities(preferTs);
|
|
39
|
-
for (const meta of this
|
|
41
|
+
for (const meta of this.#discovered) {
|
|
40
42
|
/* v8 ignore next */
|
|
41
|
-
await this
|
|
43
|
+
await this.#config.get('discovery').onMetadata?.(meta, this.#platform);
|
|
42
44
|
}
|
|
43
|
-
this.processDiscoveredEntities(this
|
|
45
|
+
this.processDiscoveredEntities(this.#discovered);
|
|
44
46
|
const diff = Date.now() - startTime;
|
|
45
|
-
this
|
|
47
|
+
this.#logger.log('discovery', `- entity discovery finished, found ${colors.green('' + this.#discovered.length)} entities, took ${colors.green(`${diff} ms`)}`);
|
|
46
48
|
const storage = this.mapDiscoveredEntities();
|
|
47
49
|
/* v8 ignore next */
|
|
48
|
-
await this
|
|
50
|
+
await this.#config.get('discovery').afterDiscovered?.(storage, this.#platform);
|
|
49
51
|
return storage;
|
|
50
52
|
}
|
|
51
53
|
discoverSync() {
|
|
52
|
-
this
|
|
54
|
+
this.#discovered.length = 0;
|
|
53
55
|
const startTime = Date.now();
|
|
54
|
-
const suffix = this
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
const suffix = this.#metadataProvider.constructor === MetadataProvider
|
|
57
|
+
? ''
|
|
58
|
+
: `, using ${colors.cyan(this.#metadataProvider.constructor.name)}`;
|
|
59
|
+
this.#logger.log('discovery', `ORM entity discovery started${suffix} in sync mode`);
|
|
60
|
+
const refs = this.#config.get('entities');
|
|
57
61
|
this.discoverReferences(refs);
|
|
58
|
-
for (const meta of this
|
|
62
|
+
for (const meta of this.#discovered) {
|
|
59
63
|
/* v8 ignore next */
|
|
60
|
-
void this
|
|
64
|
+
void this.#config.get('discovery').onMetadata?.(meta, this.#platform);
|
|
61
65
|
}
|
|
62
|
-
this.processDiscoveredEntities(this
|
|
66
|
+
this.processDiscoveredEntities(this.#discovered);
|
|
63
67
|
const diff = Date.now() - startTime;
|
|
64
|
-
this
|
|
68
|
+
this.#logger.log('discovery', `- entity discovery finished, found ${colors.green('' + this.#discovered.length)} entities, took ${colors.green(`${diff} ms`)}`);
|
|
65
69
|
const storage = this.mapDiscoveredEntities();
|
|
66
70
|
/* v8 ignore next */
|
|
67
|
-
void this
|
|
71
|
+
void this.#config.get('discovery').afterDiscovered?.(storage, this.#platform);
|
|
68
72
|
return storage;
|
|
69
73
|
}
|
|
70
74
|
mapDiscoveredEntities() {
|
|
71
75
|
const discovered = new MetadataStorage();
|
|
72
|
-
this
|
|
76
|
+
this.#discovered
|
|
73
77
|
.filter(meta => meta.root.name)
|
|
74
78
|
.sort((a, b) => b.root.name.localeCompare(a.root.name))
|
|
75
79
|
.forEach(meta => {
|
|
76
|
-
this
|
|
80
|
+
this.#platform.validateMetadata(meta);
|
|
77
81
|
discovered.set(meta.class, meta);
|
|
78
82
|
});
|
|
79
83
|
for (const meta of discovered) {
|
|
@@ -91,6 +95,7 @@ export class MetadataDiscovery {
|
|
|
91
95
|
}
|
|
92
96
|
const desc = Object.getOwnPropertyDescriptor(meta.prototype, prop.name);
|
|
93
97
|
if (desc?.get || desc?.set) {
|
|
98
|
+
this.initRelation(prop);
|
|
94
99
|
this.initFieldName(prop);
|
|
95
100
|
const accessor = prop.name;
|
|
96
101
|
prop.name = typeof prop.accessor === 'string' ? prop.accessor : prop.name;
|
|
@@ -106,7 +111,10 @@ export class MetadataDiscovery {
|
|
|
106
111
|
}
|
|
107
112
|
else {
|
|
108
113
|
const name = prop.name;
|
|
109
|
-
prop.
|
|
114
|
+
if (prop.kind === ReferenceKind.SCALAR || prop.kind === ReferenceKind.EMBEDDED) {
|
|
115
|
+
prop.name = prop.accessor;
|
|
116
|
+
}
|
|
117
|
+
this.initRelation(prop);
|
|
110
118
|
this.initFieldName(prop);
|
|
111
119
|
prop.serializedName ??= prop.accessor;
|
|
112
120
|
prop.name = name;
|
|
@@ -123,13 +131,13 @@ export class MetadataDiscovery {
|
|
|
123
131
|
// ignore base entities (not annotated with @Entity)
|
|
124
132
|
const filtered = discovered.filter(meta => meta.root.name);
|
|
125
133
|
// sort so we discover entities first to get around issues with nested embeddables
|
|
126
|
-
filtered.sort((a, b) => !a.embeddable === !b.embeddable ? 0 :
|
|
134
|
+
filtered.sort((a, b) => (!a.embeddable === !b.embeddable ? 0 : a.embeddable ? 1 : -1));
|
|
127
135
|
filtered.forEach(meta => this.initSingleTableInheritance(meta, filtered));
|
|
128
136
|
filtered.forEach(meta => this.initTPTRelationships(meta, filtered));
|
|
129
137
|
filtered.forEach(meta => this.defineBaseEntityProperties(meta));
|
|
130
138
|
filtered.forEach(meta => {
|
|
131
139
|
const newMeta = EntitySchema.fromMetadata(meta).init().meta;
|
|
132
|
-
return this
|
|
140
|
+
return this.#metadata.set(newMeta.class, newMeta);
|
|
133
141
|
});
|
|
134
142
|
filtered.forEach(meta => this.initAutoincrement(meta));
|
|
135
143
|
const forEachProp = (cb) => {
|
|
@@ -159,9 +167,9 @@ export class MetadataDiscovery {
|
|
|
159
167
|
discovered.push(...this.processEntity(meta));
|
|
160
168
|
}
|
|
161
169
|
discovered.forEach(meta => meta.sync(true));
|
|
162
|
-
this
|
|
170
|
+
this.#metadataProvider.combineCache();
|
|
163
171
|
return discovered.map(meta => {
|
|
164
|
-
meta = this
|
|
172
|
+
meta = this.#metadata.get(meta.class);
|
|
165
173
|
meta.sync(true);
|
|
166
174
|
this.findReferencingProperties(meta, filtered);
|
|
167
175
|
if (meta.inheritanceType === 'tpt') {
|
|
@@ -171,8 +179,8 @@ export class MetadataDiscovery {
|
|
|
171
179
|
});
|
|
172
180
|
}
|
|
173
181
|
async findEntities(preferTs) {
|
|
174
|
-
const { entities, entitiesTs, baseDir } = this
|
|
175
|
-
const targets =
|
|
182
|
+
const { entities, entitiesTs, baseDir } = this.#config.getAll();
|
|
183
|
+
const targets = preferTs && entitiesTs.length > 0 ? entitiesTs : entities;
|
|
176
184
|
const processed = [];
|
|
177
185
|
const paths = [];
|
|
178
186
|
for (const entity of targets) {
|
|
@@ -185,7 +193,7 @@ export class MetadataDiscovery {
|
|
|
185
193
|
}
|
|
186
194
|
if (paths.length > 0) {
|
|
187
195
|
const { discoverEntities } = await import('@mikro-orm/core/file-discovery');
|
|
188
|
-
processed.push(...await discoverEntities(paths, { baseDir }));
|
|
196
|
+
processed.push(...(await discoverEntities(paths, { baseDir })));
|
|
189
197
|
}
|
|
190
198
|
return this.discoverReferences(processed);
|
|
191
199
|
}
|
|
@@ -195,21 +203,21 @@ export class MetadataDiscovery {
|
|
|
195
203
|
.replace(/\[]$/, '') // remove array suffix
|
|
196
204
|
.replace(/\((.*)\)/, '$1'); // unwrap union types
|
|
197
205
|
const missing = [];
|
|
198
|
-
this
|
|
206
|
+
this.#discovered.forEach(meta => Object.values(meta.properties).forEach(prop => {
|
|
199
207
|
if (prop.kind === ReferenceKind.MANY_TO_MANY && prop.pivotEntity) {
|
|
200
208
|
const pivotEntity = prop.pivotEntity;
|
|
201
209
|
const target = typeof pivotEntity === 'function' && !pivotEntity.prototype
|
|
202
210
|
? pivotEntity()
|
|
203
211
|
: pivotEntity;
|
|
204
|
-
if (!this
|
|
212
|
+
if (!this.#discovered.find(m => m.className === Utils.className(target))) {
|
|
205
213
|
missing.push(target);
|
|
206
214
|
}
|
|
207
215
|
}
|
|
208
216
|
if (prop.kind !== ReferenceKind.SCALAR) {
|
|
209
|
-
const target = typeof prop.entity === 'function' && !prop.entity.prototype
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
217
|
+
const target = typeof prop.entity === 'function' && !prop.entity.prototype ? prop.entity() : prop.type;
|
|
218
|
+
if (!unwrap(prop.type)
|
|
219
|
+
.split(/ ?\| ?/)
|
|
220
|
+
.every(type => this.#discovered.find(m => m.className === type))) {
|
|
213
221
|
missing.push(...Utils.asArray(target));
|
|
214
222
|
}
|
|
215
223
|
}
|
|
@@ -225,7 +233,7 @@ export class MetadataDiscovery {
|
|
|
225
233
|
if (isDiscoverable && target.name) {
|
|
226
234
|
// Get the actual class for EntitySchema, or use target directly for classes
|
|
227
235
|
const targetClass = schema ? schema.meta.class : target;
|
|
228
|
-
if (!this
|
|
236
|
+
if (!this.#metadata.has(targetClass)) {
|
|
229
237
|
this.discoverReferences([target], false);
|
|
230
238
|
this.discoverMissingTargets();
|
|
231
239
|
}
|
|
@@ -240,16 +248,16 @@ export class MetadataDiscovery {
|
|
|
240
248
|
}
|
|
241
249
|
const schema = this.getSchema(entity);
|
|
242
250
|
const meta = schema.init().meta;
|
|
243
|
-
this
|
|
251
|
+
this.#metadata.set(meta.class, meta);
|
|
244
252
|
found.push(schema);
|
|
245
253
|
}
|
|
246
254
|
// discover parents (base entities) automatically
|
|
247
|
-
for (const meta of this
|
|
255
|
+
for (const meta of this.#metadata) {
|
|
248
256
|
let parent = meta.extends;
|
|
249
|
-
if (parent instanceof EntitySchema && !this
|
|
257
|
+
if (parent instanceof EntitySchema && !this.#metadata.has(parent.init().meta.class)) {
|
|
250
258
|
this.discoverReferences([parent], false);
|
|
251
259
|
}
|
|
252
|
-
if (typeof parent === 'function' && parent.name && !this
|
|
260
|
+
if (typeof parent === 'function' && parent.name && !this.#metadata.has(parent)) {
|
|
253
261
|
this.discoverReferences([parent], false);
|
|
254
262
|
}
|
|
255
263
|
/* v8 ignore next */
|
|
@@ -258,7 +266,10 @@ export class MetadataDiscovery {
|
|
|
258
266
|
}
|
|
259
267
|
parent = Object.getPrototypeOf(meta.class);
|
|
260
268
|
// Skip if parent is the auto-generated base class for the same entity (from setClass usage)
|
|
261
|
-
if (parent.name !== '' &&
|
|
269
|
+
if (parent.name !== '' &&
|
|
270
|
+
parent.name !== meta.className &&
|
|
271
|
+
!this.#metadata.has(parent) &&
|
|
272
|
+
parent !== BaseEntity) {
|
|
262
273
|
this.discoverReferences([parent], false);
|
|
263
274
|
}
|
|
264
275
|
}
|
|
@@ -267,15 +278,15 @@ export class MetadataDiscovery {
|
|
|
267
278
|
}
|
|
268
279
|
this.discoverMissingTargets();
|
|
269
280
|
if (validate) {
|
|
270
|
-
this
|
|
281
|
+
this.#validator.validateDiscovered(this.#discovered, this.#config.get('discovery'));
|
|
271
282
|
}
|
|
272
|
-
return this
|
|
283
|
+
return this.#discovered.filter(meta => found.find(m => m.name === meta.className));
|
|
273
284
|
}
|
|
274
285
|
reset(entityName) {
|
|
275
|
-
const exists = this
|
|
286
|
+
const exists = this.#discovered.findIndex(m => m.class === entityName || m.className === Utils.className(entityName));
|
|
276
287
|
if (exists !== -1) {
|
|
277
|
-
this
|
|
278
|
-
this
|
|
288
|
+
this.#metadata.reset(this.#discovered[exists].class);
|
|
289
|
+
this.#discovered.splice(exists, 1);
|
|
279
290
|
}
|
|
280
291
|
}
|
|
281
292
|
getSchema(entity) {
|
|
@@ -290,18 +301,19 @@ export class MetadataDiscovery {
|
|
|
290
301
|
if (path) {
|
|
291
302
|
const meta = Utils.copy(MetadataStorage.getMetadata(entity.name, path), false);
|
|
292
303
|
meta.path = path;
|
|
293
|
-
this
|
|
304
|
+
this.#metadata.set(entity, meta);
|
|
294
305
|
}
|
|
295
|
-
const exists = this
|
|
296
|
-
const meta = this
|
|
306
|
+
const exists = this.#metadata.has(entity);
|
|
307
|
+
const meta = this.#metadata.get(entity, true);
|
|
297
308
|
meta.abstract ??= !(exists && meta.name);
|
|
298
309
|
const schema = EntitySchema.fromMetadata(meta);
|
|
299
310
|
schema.setClass(entity);
|
|
300
311
|
return schema;
|
|
301
312
|
}
|
|
302
313
|
getRootEntity(meta) {
|
|
303
|
-
const base = meta.extends && this
|
|
304
|
-
if (!base || base === meta) {
|
|
314
|
+
const base = meta.extends && this.#metadata.find(meta.extends);
|
|
315
|
+
if (!base || base === meta) {
|
|
316
|
+
// make sure we do not fall into infinite loop
|
|
305
317
|
return meta;
|
|
306
318
|
}
|
|
307
319
|
const root = this.getRootEntity(base);
|
|
@@ -315,13 +327,13 @@ export class MetadataDiscovery {
|
|
|
315
327
|
discoverEntity(schema) {
|
|
316
328
|
const meta = schema.meta;
|
|
317
329
|
const path = meta.path;
|
|
318
|
-
this
|
|
330
|
+
this.#logger.log('discovery', `- processing entity ${colors.cyan(meta.className)}${colors.grey(path ? ` (${path})` : '')}`);
|
|
319
331
|
const root = this.getRootEntity(meta);
|
|
320
332
|
schema.meta.path = meta.path;
|
|
321
|
-
const cache = this
|
|
333
|
+
const cache = this.#metadataProvider.getCachedMetadata(meta, root);
|
|
322
334
|
if (cache) {
|
|
323
|
-
this
|
|
324
|
-
this
|
|
335
|
+
this.#logger.log('discovery', `- using cached metadata for entity ${colors.cyan(meta.className)}`);
|
|
336
|
+
this.#discovered.push(meta);
|
|
325
337
|
return;
|
|
326
338
|
}
|
|
327
339
|
// infer default value from property initializer early, as the metadata provider might use some defaults, e.g. string for reflect-metadata
|
|
@@ -329,14 +341,14 @@ export class MetadataDiscovery {
|
|
|
329
341
|
this.inferDefaultValue(meta, prop);
|
|
330
342
|
}
|
|
331
343
|
// if the definition is using EntitySchema we still want it to go through the metadata provider to validate no types are missing
|
|
332
|
-
this
|
|
344
|
+
this.#metadataProvider.loadEntityMetadata(meta);
|
|
333
345
|
if (!meta.tableName && meta.name) {
|
|
334
346
|
const entityName = root.discriminatorColumn ? root.name : meta.name;
|
|
335
|
-
meta.tableName = this
|
|
347
|
+
meta.tableName = this.#namingStrategy.classToTableName(entityName);
|
|
336
348
|
}
|
|
337
|
-
this
|
|
349
|
+
this.#metadataProvider.saveToCache(meta);
|
|
338
350
|
meta.root = root;
|
|
339
|
-
this
|
|
351
|
+
this.#discovered.push(meta);
|
|
340
352
|
}
|
|
341
353
|
initNullability(prop) {
|
|
342
354
|
if (prop.kind === ReferenceKind.ONE_TO_ONE) {
|
|
@@ -361,7 +373,10 @@ export class MetadataDiscovery {
|
|
|
361
373
|
initOwnColumns(meta) {
|
|
362
374
|
meta.sync();
|
|
363
375
|
for (const prop of meta.props) {
|
|
364
|
-
if (!prop.joinColumns ||
|
|
376
|
+
if (!prop.joinColumns ||
|
|
377
|
+
!prop.columnTypes ||
|
|
378
|
+
prop.ownColumns ||
|
|
379
|
+
![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
|
|
365
380
|
continue;
|
|
366
381
|
}
|
|
367
382
|
// For polymorphic relations, ownColumns should include all fieldNames
|
|
@@ -398,7 +413,7 @@ export class MetadataDiscovery {
|
|
|
398
413
|
return;
|
|
399
414
|
}
|
|
400
415
|
if (prop.kind === ReferenceKind.SCALAR || prop.kind === ReferenceKind.EMBEDDED) {
|
|
401
|
-
prop.fieldNames = [this
|
|
416
|
+
prop.fieldNames = [this.#namingStrategy.propertyToColumnName(prop.name, object)];
|
|
402
417
|
}
|
|
403
418
|
else if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && !prop.polymorphic) {
|
|
404
419
|
prop.fieldNames = this.initManyToOneFieldName(prop, prop.name);
|
|
@@ -413,19 +428,19 @@ export class MetadataDiscovery {
|
|
|
413
428
|
for (const primaryKey of meta2.primaryKeys) {
|
|
414
429
|
this.initFieldName(meta2.properties[primaryKey]);
|
|
415
430
|
for (const fieldName of meta2.properties[primaryKey].fieldNames) {
|
|
416
|
-
ret.push(this
|
|
431
|
+
ret.push(this.#namingStrategy.joinKeyColumnName(name, fieldName, meta2.compositePK, tableName));
|
|
417
432
|
}
|
|
418
433
|
}
|
|
419
434
|
return ret;
|
|
420
435
|
}
|
|
421
436
|
initManyToManyFieldName(prop, name) {
|
|
422
437
|
const meta2 = prop.targetMeta;
|
|
423
|
-
return meta2.primaryKeys.map(() => this
|
|
438
|
+
return meta2.primaryKeys.map(() => this.#namingStrategy.propertyToColumnName(name));
|
|
424
439
|
}
|
|
425
440
|
initManyToManyFields(meta, prop) {
|
|
426
441
|
const meta2 = prop.targetMeta;
|
|
427
442
|
Utils.defaultValue(prop, 'fixedOrder', !!prop.fixedOrderColumn);
|
|
428
|
-
const pivotMeta = this
|
|
443
|
+
const pivotMeta = this.#metadata.find(prop.pivotEntity);
|
|
429
444
|
const props = Object.values(pivotMeta?.properties ?? {});
|
|
430
445
|
const pks = props.filter(p => p.primary);
|
|
431
446
|
const fks = props.filter(p => p.kind === ReferenceKind.MANY_TO_ONE);
|
|
@@ -443,8 +458,8 @@ export class MetadataDiscovery {
|
|
|
443
458
|
prop.joinColumns ??= first.fieldNames;
|
|
444
459
|
prop.inverseJoinColumns ??= second.fieldNames;
|
|
445
460
|
}
|
|
446
|
-
if (!prop.pivotTable && prop.owner && this
|
|
447
|
-
prop.pivotTable = this
|
|
461
|
+
if (!prop.pivotTable && prop.owner && this.#platform.usesPivotTable()) {
|
|
462
|
+
prop.pivotTable = this.#namingStrategy.joinTableName(meta.className, meta2.tableName, prop.name, meta.tableName);
|
|
448
463
|
}
|
|
449
464
|
if (prop.mappedBy) {
|
|
450
465
|
const prop2 = meta2.properties[prop.mappedBy];
|
|
@@ -463,22 +478,22 @@ export class MetadataDiscovery {
|
|
|
463
478
|
prop.referencedColumnNames ??= Utils.flatten(meta.primaryKeys.map(primaryKey => meta.properties[primaryKey].fieldNames));
|
|
464
479
|
// For polymorphic M:N, use discriminator base name for FK column (e.g., taggable_id instead of post_id)
|
|
465
480
|
if (prop.polymorphic && prop.discriminator) {
|
|
466
|
-
prop.joinColumns ??= prop.referencedColumnNames.map(referencedColumnName => this
|
|
481
|
+
prop.joinColumns ??= prop.referencedColumnNames.map(referencedColumnName => this.#namingStrategy.joinKeyColumnName(prop.discriminator, referencedColumnName, prop.referencedColumnNames.length > 1));
|
|
467
482
|
}
|
|
468
483
|
else {
|
|
469
484
|
const ownerTableName = this.isExplicitTableName(meta.root) ? meta.root.tableName : undefined;
|
|
470
|
-
prop.joinColumns ??= prop.referencedColumnNames.map(referencedColumnName => this
|
|
485
|
+
prop.joinColumns ??= prop.referencedColumnNames.map(referencedColumnName => this.#namingStrategy.joinKeyColumnName(meta.root.className, referencedColumnName, meta.compositePK, ownerTableName));
|
|
471
486
|
}
|
|
472
487
|
const inverseTableName = this.isExplicitTableName(meta2.root) ? meta2.root.tableName : undefined;
|
|
473
488
|
prop.inverseJoinColumns ??= this.initManyToOneFieldName(prop, meta2.root.className, inverseTableName);
|
|
474
489
|
}
|
|
475
490
|
isExplicitTableName(meta) {
|
|
476
|
-
return meta.tableName !== this
|
|
491
|
+
return meta.tableName !== this.#namingStrategy.classToTableName(meta.className);
|
|
477
492
|
}
|
|
478
493
|
initManyToOneFields(prop) {
|
|
479
494
|
if (prop.polymorphic && prop.polymorphTargets) {
|
|
480
495
|
const fieldNames1 = prop.targetMeta.getPrimaryProps().flatMap(pk => pk.fieldNames);
|
|
481
|
-
const idColumns = fieldNames1.map(fieldName => this
|
|
496
|
+
const idColumns = fieldNames1.map(fieldName => this.#namingStrategy.joinKeyColumnName(prop.discriminator, fieldName, fieldNames1.length > 1));
|
|
482
497
|
prop.fieldNames ??= [prop.discriminatorColumn, ...idColumns];
|
|
483
498
|
prop.joinColumns ??= idColumns;
|
|
484
499
|
prop.referencedColumnNames ??= fieldNames1;
|
|
@@ -497,7 +512,7 @@ export class MetadataDiscovery {
|
|
|
497
512
|
}
|
|
498
513
|
Utils.defaultValue(prop, 'referencedTableName', meta2.tableName);
|
|
499
514
|
if (!prop.joinColumns) {
|
|
500
|
-
prop.joinColumns = fieldNames.map(fieldName => this
|
|
515
|
+
prop.joinColumns = fieldNames.map(fieldName => this.#namingStrategy.joinKeyColumnName(prop.name, fieldName, fieldNames.length > 1));
|
|
501
516
|
}
|
|
502
517
|
if (!prop.referencedColumnNames) {
|
|
503
518
|
prop.referencedColumnNames = fieldNames;
|
|
@@ -516,7 +531,7 @@ export class MetadataDiscovery {
|
|
|
516
531
|
initOneToManyFields(prop) {
|
|
517
532
|
const meta2 = prop.targetMeta;
|
|
518
533
|
if (!prop.joinColumns) {
|
|
519
|
-
prop.joinColumns = [this
|
|
534
|
+
prop.joinColumns = [this.#namingStrategy.joinColumnName(prop.name)];
|
|
520
535
|
}
|
|
521
536
|
if (!prop.referencedColumnNames) {
|
|
522
537
|
meta2.getPrimaryProps().forEach(pk => this.applyNamingStrategy(meta2, pk));
|
|
@@ -537,7 +552,7 @@ export class MetadataDiscovery {
|
|
|
537
552
|
}
|
|
538
553
|
}
|
|
539
554
|
meta.forceConstructor ??= this.shouldForceConstructorUsage(meta);
|
|
540
|
-
this
|
|
555
|
+
this.#validator.validateEntityDefinition(this.#metadata, meta.class, this.#config.get('discovery'));
|
|
541
556
|
for (const prop of Object.values(meta.properties)) {
|
|
542
557
|
this.initNullability(prop);
|
|
543
558
|
this.applyNamingStrategy(meta, prop);
|
|
@@ -549,12 +564,13 @@ export class MetadataDiscovery {
|
|
|
549
564
|
this.initRelation(prop);
|
|
550
565
|
}
|
|
551
566
|
this.initOwnColumns(meta);
|
|
552
|
-
meta.simplePK =
|
|
567
|
+
meta.simplePK =
|
|
568
|
+
pks.length === 1 && pks[0].kind === ReferenceKind.SCALAR && !pks[0].customType && pks[0].runtimeType !== 'Date';
|
|
553
569
|
meta.serializedPrimaryKey ??= meta.props.find(prop => prop.serializedPrimaryKey)?.name;
|
|
554
570
|
if (meta.serializedPrimaryKey && meta.serializedPrimaryKey !== meta.primaryKeys[0]) {
|
|
555
571
|
meta.properties[meta.serializedPrimaryKey].persist ??= false;
|
|
556
572
|
}
|
|
557
|
-
if (this
|
|
573
|
+
if (this.#platform.usesPivotTable()) {
|
|
558
574
|
return Object.values(meta.properties)
|
|
559
575
|
.filter(prop => prop.kind === ReferenceKind.MANY_TO_MANY && prop.owner && prop.pivotTable)
|
|
560
576
|
.map(prop => {
|
|
@@ -581,7 +597,7 @@ export class MetadataDiscovery {
|
|
|
581
597
|
['mappedBy', 'inversedBy', 'pivotEntity'].forEach(type => {
|
|
582
598
|
const value = prop[type];
|
|
583
599
|
if (value instanceof Function) {
|
|
584
|
-
const meta2 = prop.targetMeta ?? this
|
|
600
|
+
const meta2 = prop.targetMeta ?? this.#metadata.get(prop.target);
|
|
585
601
|
prop[type] = value(meta2.properties)?.name;
|
|
586
602
|
if (type === 'pivotEntity' && value) {
|
|
587
603
|
prop[type] = value(meta2.properties);
|
|
@@ -618,8 +634,8 @@ export class MetadataDiscovery {
|
|
|
618
634
|
}
|
|
619
635
|
definePivotTableEntity(meta, prop) {
|
|
620
636
|
const pivotMeta = prop.pivotEntity
|
|
621
|
-
? this
|
|
622
|
-
: this
|
|
637
|
+
? this.#metadata.find(prop.pivotEntity)
|
|
638
|
+
: this.#metadata.getByClassName(prop.pivotTable, false);
|
|
623
639
|
// ensure inverse side exists so we can join it when populating via pivot tables
|
|
624
640
|
if (!prop.inversedBy && prop.targetMeta) {
|
|
625
641
|
const inverseName = `${meta.className}_${prop.name}__inverse`;
|
|
@@ -681,18 +697,19 @@ export class MetadataDiscovery {
|
|
|
681
697
|
pivotMeta2.compositePK = true;
|
|
682
698
|
}
|
|
683
699
|
// handle self-referenced m:n with same default field names
|
|
684
|
-
if (meta.className === targetType &&
|
|
700
|
+
if (meta.className === targetType &&
|
|
701
|
+
prop.joinColumns.every((joinColumn, idx) => joinColumn === prop.inverseJoinColumns[idx])) {
|
|
685
702
|
// use tableName only when explicitly provided by user, otherwise use className for backwards compatibility
|
|
686
703
|
const baseName = this.isExplicitTableName(meta) ? meta.tableName : meta.className;
|
|
687
|
-
prop.joinColumns = prop.referencedColumnNames.map(name => this
|
|
688
|
-
prop.inverseJoinColumns = prop.referencedColumnNames.map(name => this
|
|
704
|
+
prop.joinColumns = prop.referencedColumnNames.map(name => this.#namingStrategy.joinKeyColumnName(baseName + '_1', name, meta.compositePK));
|
|
705
|
+
prop.inverseJoinColumns = prop.referencedColumnNames.map(name => this.#namingStrategy.joinKeyColumnName(baseName + '_2', name, meta.compositePK));
|
|
689
706
|
if (prop.inversedBy) {
|
|
690
707
|
const prop2 = targetMeta.properties[prop.inversedBy];
|
|
691
708
|
prop2.inverseJoinColumns = prop.joinColumns;
|
|
692
709
|
prop2.joinColumns = prop.inverseJoinColumns;
|
|
693
710
|
}
|
|
694
711
|
// propagate updated joinColumns to all child entities that inherit this property (STI)
|
|
695
|
-
for (const childMeta of this
|
|
712
|
+
for (const childMeta of this.#discovered.filter(m => m.root === meta && m !== meta)) {
|
|
696
713
|
const childProp = childMeta.properties[prop.name];
|
|
697
714
|
if (childProp) {
|
|
698
715
|
childProp.joinColumns = prop.joinColumns;
|
|
@@ -708,7 +725,7 @@ export class MetadataDiscovery {
|
|
|
708
725
|
pivotMeta2.properties[meta.name + '_owner'] = this.definePivotProperty(prop, meta.name + '_owner', meta.class, targetType + '_inverse', true, meta.className === targetType);
|
|
709
726
|
pivotMeta2.properties[targetType + '_inverse'] = this.definePivotProperty(prop, targetType + '_inverse', targetMeta.class, meta.name + '_owner', false, meta.className === targetType);
|
|
710
727
|
}
|
|
711
|
-
return this
|
|
728
|
+
return this.#metadata.set(pivotMeta2.class, EntitySchema.fromMetadata(pivotMeta2).init().meta);
|
|
712
729
|
}
|
|
713
730
|
/**
|
|
714
731
|
* Create a scalar property for a pivot table column.
|
|
@@ -764,14 +781,16 @@ export class MetadataDiscovery {
|
|
|
764
781
|
pivotMeta.properties[primaryProp.name] = primaryProp;
|
|
765
782
|
pivotMeta.compositePK = false;
|
|
766
783
|
}
|
|
767
|
-
const discriminatorProp = this.createPivotScalarProperty(discriminatorColumn, [this
|
|
784
|
+
const discriminatorProp = this.createPivotScalarProperty(discriminatorColumn, [this.#platform.getVarcharTypeDeclarationSQL(prop)], [discriminatorColumn], { type: 'string', primary: !isCompositePK, nullable: false });
|
|
768
785
|
this.initFieldName(discriminatorProp);
|
|
769
786
|
pivotMeta.properties[discriminatorColumn] = discriminatorProp;
|
|
770
787
|
const columnTypes = this.getPrimaryKeyColumnTypes(meta);
|
|
771
788
|
if (isCompositePK) {
|
|
772
789
|
// Create separate properties for each PK column (nullable for other entity types)
|
|
773
790
|
for (let i = 0; i < prop.joinColumns.length; i++) {
|
|
774
|
-
pivotMeta.properties[prop.joinColumns[i]] = this.createPivotScalarProperty(prop.joinColumns[i], [
|
|
791
|
+
pivotMeta.properties[prop.joinColumns[i]] = this.createPivotScalarProperty(prop.joinColumns[i], [
|
|
792
|
+
columnTypes[i],
|
|
793
|
+
]);
|
|
775
794
|
}
|
|
776
795
|
// Virtual property combining all columns (for compatibility)
|
|
777
796
|
pivotMeta.properties[prop.discriminator] = this.createPivotScalarProperty(prop.discriminator, columnTypes, [...prop.joinColumns], { type: meta.className, persist: false });
|
|
@@ -815,21 +834,22 @@ export class MetadataDiscovery {
|
|
|
815
834
|
ret.precision = pkProp.precision;
|
|
816
835
|
ret.scale = pkProp.scale;
|
|
817
836
|
}
|
|
818
|
-
const schema = targetMeta.schema ?? this
|
|
837
|
+
const schema = targetMeta.schema ?? this.#config.get('schema') ?? this.#platform.getDefaultSchemaName();
|
|
819
838
|
ret.referencedTableName = schema && schema !== '*' ? schema + '.' + targetMeta.tableName : targetMeta.tableName;
|
|
820
839
|
this.initColumnType(ret);
|
|
821
840
|
this.initRelation(ret);
|
|
822
841
|
return ret;
|
|
823
842
|
}
|
|
824
843
|
defineFixedOrderProperty(prop, targetMeta) {
|
|
825
|
-
const pk = prop.fixedOrderColumn || this
|
|
844
|
+
const pk = prop.fixedOrderColumn || this.#namingStrategy.referenceColumnName();
|
|
826
845
|
const primaryProp = {
|
|
827
846
|
name: pk,
|
|
828
847
|
type: 'number',
|
|
848
|
+
runtimeType: 'number',
|
|
829
849
|
kind: ReferenceKind.SCALAR,
|
|
830
850
|
primary: true,
|
|
831
851
|
autoincrement: true,
|
|
832
|
-
unsigned: this
|
|
852
|
+
unsigned: this.#platform.supportsUnsigned(),
|
|
833
853
|
};
|
|
834
854
|
this.initFieldName(primaryProp);
|
|
835
855
|
this.initColumnType(primaryProp);
|
|
@@ -850,21 +870,21 @@ export class MetadataDiscovery {
|
|
|
850
870
|
cascade: [Cascade.ALL],
|
|
851
871
|
fixedOrder: prop.fixedOrder,
|
|
852
872
|
fixedOrderColumn: prop.fixedOrderColumn,
|
|
853
|
-
index: this
|
|
873
|
+
index: this.#platform.indexForeignKeys(),
|
|
854
874
|
primary: !prop.fixedOrder,
|
|
855
875
|
autoincrement: false,
|
|
856
876
|
updateRule: prop.updateRule,
|
|
857
877
|
deleteRule: prop.deleteRule,
|
|
858
878
|
createForeignKeyConstraint: prop.createForeignKeyConstraint,
|
|
859
879
|
};
|
|
860
|
-
const defaultRule = selfReferencing && !this
|
|
880
|
+
const defaultRule = selfReferencing && !this.#platform.supportsMultipleCascadePaths() ? 'no action' : 'cascade';
|
|
861
881
|
ret.updateRule ??= defaultRule;
|
|
862
882
|
ret.deleteRule ??= defaultRule;
|
|
863
|
-
const meta = this
|
|
883
|
+
const meta = this.#metadata.get(type);
|
|
864
884
|
ret.targetMeta = meta;
|
|
865
885
|
ret.joinColumns = [];
|
|
866
886
|
ret.inverseJoinColumns = [];
|
|
867
|
-
const schema = meta.schema ?? this
|
|
887
|
+
const schema = meta.schema ?? this.#config.get('schema') ?? this.#platform.getDefaultSchemaName();
|
|
868
888
|
ret.referencedTableName = schema && schema !== '*' ? schema + '.' + meta.tableName : meta.tableName;
|
|
869
889
|
if (owner) {
|
|
870
890
|
ret.owner = true;
|
|
@@ -910,8 +930,9 @@ export class MetadataDiscovery {
|
|
|
910
930
|
});
|
|
911
931
|
}
|
|
912
932
|
defineBaseEntityProperties(meta) {
|
|
913
|
-
const base = meta.extends && this
|
|
914
|
-
if (!base || base === meta) {
|
|
933
|
+
const base = meta.extends && this.#metadata.get(meta.extends);
|
|
934
|
+
if (!base || base === meta) {
|
|
935
|
+
// make sure we do not fall into infinite loop
|
|
915
936
|
return 0;
|
|
916
937
|
}
|
|
917
938
|
let order = this.defineBaseEntityProperties(base);
|
|
@@ -923,10 +944,12 @@ export class MetadataDiscovery {
|
|
|
923
944
|
meta.properties[prop.name] = prop;
|
|
924
945
|
}
|
|
925
946
|
});
|
|
926
|
-
ownProps.forEach(prop => meta.properties[prop.name] = prop);
|
|
947
|
+
ownProps.forEach(prop => (meta.properties[prop.name] = prop));
|
|
927
948
|
meta.filters = { ...base.filters, ...meta.filters };
|
|
928
949
|
if (!meta.discriminatorValue) {
|
|
929
|
-
Object.values(base.properties)
|
|
950
|
+
Object.values(base.properties)
|
|
951
|
+
.filter(prop => !old.includes(prop.name))
|
|
952
|
+
.forEach(prop => {
|
|
930
953
|
meta.properties[prop.name] = { ...prop };
|
|
931
954
|
meta.propertyOrder.set(prop.name, (order += 0.01));
|
|
932
955
|
});
|
|
@@ -934,7 +957,9 @@ export class MetadataDiscovery {
|
|
|
934
957
|
meta.indexes = Utils.unique([...base.indexes, ...meta.indexes]);
|
|
935
958
|
meta.uniques = Utils.unique([...base.uniques, ...meta.uniques]);
|
|
936
959
|
meta.checks = Utils.unique([...base.checks, ...meta.checks]);
|
|
937
|
-
const pks = Object.values(meta.properties)
|
|
960
|
+
const pks = Object.values(meta.properties)
|
|
961
|
+
.filter(p => p.primary)
|
|
962
|
+
.map(p => p.name);
|
|
938
963
|
if (pks.length > 0 && meta.primaryKeys.length === 0) {
|
|
939
964
|
meta.primaryKeys = pks;
|
|
940
965
|
}
|
|
@@ -952,8 +977,8 @@ export class MetadataDiscovery {
|
|
|
952
977
|
}
|
|
953
978
|
visited.add(embeddedProp);
|
|
954
979
|
const types = embeddedProp.type.split(/ ?\| ?/);
|
|
955
|
-
let embeddable = this
|
|
956
|
-
const polymorphs = this
|
|
980
|
+
let embeddable = this.#discovered.find(m => m.name === embeddedProp.type);
|
|
981
|
+
const polymorphs = this.#discovered.filter(m => types.includes(m.name));
|
|
957
982
|
// create virtual polymorphic entity
|
|
958
983
|
if (!embeddable && polymorphs.length > 0) {
|
|
959
984
|
const properties = {};
|
|
@@ -965,13 +990,17 @@ export class MetadataDiscovery {
|
|
|
965
990
|
if (properties[prop.name] && properties[prop.name].type !== prop.type) {
|
|
966
991
|
properties[prop.name].type = `${properties[prop.name].type} | ${prop.type}`;
|
|
967
992
|
properties[prop.name].runtimeType = 'any';
|
|
993
|
+
properties[prop.name].stiMerged = true;
|
|
968
994
|
return properties[prop.name];
|
|
969
995
|
}
|
|
970
|
-
|
|
996
|
+
// Deep copy to prevent mutating the original entity's property —
|
|
997
|
+
// both from the merge path above (GH #6522/#6523) and from
|
|
998
|
+
// downstream code that mutates nested arrays like fieldNames.
|
|
999
|
+
return (properties[prop.name] = Utils.copy(prop));
|
|
971
1000
|
});
|
|
972
1001
|
};
|
|
973
1002
|
const processExtensions = (meta) => {
|
|
974
|
-
const parent = this
|
|
1003
|
+
const parent = this.#discovered.find(m => {
|
|
975
1004
|
return meta.extends && Utils.className(meta.extends) === m.className;
|
|
976
1005
|
});
|
|
977
1006
|
if (!parent) {
|
|
@@ -985,7 +1014,10 @@ export class MetadataDiscovery {
|
|
|
985
1014
|
inlineProperties(meta);
|
|
986
1015
|
processExtensions(meta);
|
|
987
1016
|
});
|
|
988
|
-
const name = polymorphs
|
|
1017
|
+
const name = polymorphs
|
|
1018
|
+
.map(t => t.className)
|
|
1019
|
+
.sort()
|
|
1020
|
+
.join(' | ');
|
|
989
1021
|
embeddable = new EntityMetadata({
|
|
990
1022
|
name,
|
|
991
1023
|
className: name,
|
|
@@ -997,7 +1029,7 @@ export class MetadataDiscovery {
|
|
|
997
1029
|
});
|
|
998
1030
|
embeddable.sync();
|
|
999
1031
|
discovered.push(embeddable);
|
|
1000
|
-
polymorphs.forEach(meta => meta.root = embeddable);
|
|
1032
|
+
polymorphs.forEach(meta => (meta.root = embeddable));
|
|
1001
1033
|
}
|
|
1002
1034
|
}
|
|
1003
1035
|
initPolymorphicRelation(meta, prop, discovered) {
|
|
@@ -1006,7 +1038,7 @@ export class MetadataDiscovery {
|
|
|
1006
1038
|
}
|
|
1007
1039
|
prop.polymorphic = true;
|
|
1008
1040
|
prop.discriminator ??= prop.name;
|
|
1009
|
-
prop.discriminatorColumn ??= this
|
|
1041
|
+
prop.discriminatorColumn ??= this.#namingStrategy.discriminatorColumnName(prop.discriminator);
|
|
1010
1042
|
prop.createForeignKeyConstraint = false;
|
|
1011
1043
|
const isToOne = [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind);
|
|
1012
1044
|
if (isToOne) {
|
|
@@ -1018,7 +1050,7 @@ export class MetadataDiscovery {
|
|
|
1018
1050
|
if (prop.discriminatorMap) {
|
|
1019
1051
|
const normalizedMap = {};
|
|
1020
1052
|
for (const [key, value] of Object.entries(prop.discriminatorMap)) {
|
|
1021
|
-
const targetMeta = this
|
|
1053
|
+
const targetMeta = this.#metadata.getByClassName(value, false);
|
|
1022
1054
|
if (!targetMeta) {
|
|
1023
1055
|
throw MetadataError.fromUnknownEntity(value, `${meta.className}.${prop.name} discriminatorMap`);
|
|
1024
1056
|
}
|
|
@@ -1053,7 +1085,7 @@ export class MetadataDiscovery {
|
|
|
1053
1085
|
return;
|
|
1054
1086
|
}
|
|
1055
1087
|
visited.add(embeddedProp);
|
|
1056
|
-
const embeddable = this
|
|
1088
|
+
const embeddable = this.#discovered.find(m => m.name === embeddedProp.type);
|
|
1057
1089
|
if (!embeddable) {
|
|
1058
1090
|
throw MetadataError.fromUnknownEntity(embeddedProp.type, `${meta.className}.${embeddedProp.name}`);
|
|
1059
1091
|
}
|
|
@@ -1125,7 +1157,7 @@ export class MetadataDiscovery {
|
|
|
1125
1157
|
meta.properties[name].fieldNames = prop.fieldNames;
|
|
1126
1158
|
meta.properties[name].embeddedPath = path;
|
|
1127
1159
|
const targetProp = prop.targetMeta?.getPrimaryProp() ?? prop;
|
|
1128
|
-
const fieldName = raw(this
|
|
1160
|
+
const fieldName = raw(this.#platform.getSearchJsonPropertySQL(path.join('->'), targetProp.runtimeType ?? targetProp.type, true));
|
|
1129
1161
|
meta.properties[name].fieldNameRaw = fieldName.sql; // for querying in SQL drivers
|
|
1130
1162
|
meta.properties[name].persist = false; // only virtual as we store the whole object
|
|
1131
1163
|
meta.properties[name].userDefined = false; // mark this as a generated/internal property, so we can distinguish from user-defined non-persist properties
|
|
@@ -1167,7 +1199,7 @@ export class MetadataDiscovery {
|
|
|
1167
1199
|
const map = meta.root.discriminatorMap;
|
|
1168
1200
|
Object.keys(map)
|
|
1169
1201
|
.filter(key => typeof map[key] === 'string')
|
|
1170
|
-
.forEach(key => map[key] = this
|
|
1202
|
+
.forEach(key => (map[key] = this.#metadata.getByClassName(map[key]).class));
|
|
1171
1203
|
}
|
|
1172
1204
|
else {
|
|
1173
1205
|
meta.root.discriminatorMap = {};
|
|
@@ -1175,7 +1207,7 @@ export class MetadataDiscovery {
|
|
|
1175
1207
|
.filter(m => m.root.class === meta.root.class && !m.abstract)
|
|
1176
1208
|
.sort((a, b) => a.className.localeCompare(b.className));
|
|
1177
1209
|
for (const m of children) {
|
|
1178
|
-
const name = m.discriminatorValue ?? this
|
|
1210
|
+
const name = m.discriminatorValue ?? this.#namingStrategy.classToTableName(m.className);
|
|
1179
1211
|
meta.root.discriminatorMap[name] = m.class;
|
|
1180
1212
|
}
|
|
1181
1213
|
}
|
|
@@ -1191,7 +1223,14 @@ export class MetadataDiscovery {
|
|
|
1191
1223
|
Object.values(meta.properties).forEach(prop => {
|
|
1192
1224
|
const newProp = { ...prop };
|
|
1193
1225
|
const rootProp = meta.root.properties[prop.name];
|
|
1194
|
-
|
|
1226
|
+
// stiMerged is set during inlineProperties when a property was merged
|
|
1227
|
+
// from multiple polymorphic variants with different types. The flag is
|
|
1228
|
+
// cleared implicitly when the first child claims the root property via
|
|
1229
|
+
// addProperty below, so subsequent children correctly trigger renaming.
|
|
1230
|
+
const typesMatch = rootProp?.type === prop.type || rootProp?.stiMerged === true;
|
|
1231
|
+
if (rootProp &&
|
|
1232
|
+
(!typesMatch ||
|
|
1233
|
+
(rootProp.fieldNames && prop.fieldNames && !compareArrays(rootProp.fieldNames, prop.fieldNames)))) {
|
|
1195
1234
|
const name = newProp.name;
|
|
1196
1235
|
this.initFieldName(newProp, newProp.object);
|
|
1197
1236
|
newProp.renamedFrom = name;
|
|
@@ -1205,8 +1244,9 @@ export class MetadataDiscovery {
|
|
|
1205
1244
|
rootProp.stiFieldNameMap = {};
|
|
1206
1245
|
// Find which discriminator owns the original fieldNames
|
|
1207
1246
|
for (const [discValue, childClass] of Object.entries(meta.root.discriminatorMap)) {
|
|
1208
|
-
const childMeta = this
|
|
1209
|
-
if (childMeta?.properties[prop.name]?.fieldNames &&
|
|
1247
|
+
const childMeta = this.#metadata.find(childClass);
|
|
1248
|
+
if (childMeta?.properties[prop.name]?.fieldNames &&
|
|
1249
|
+
compareArrays(childMeta.properties[prop.name].fieldNames, rootProp.fieldNames)) {
|
|
1210
1250
|
rootProp.stiFieldNameMap[discValue] = rootProp.fieldNames[0];
|
|
1211
1251
|
break;
|
|
1212
1252
|
}
|
|
@@ -1272,11 +1312,11 @@ export class MetadataDiscovery {
|
|
|
1272
1312
|
if (meta.tptChildren) {
|
|
1273
1313
|
meta.tptChildren = meta.tptChildren.map(child => metadata.find(m => m.class === child.class) ?? child);
|
|
1274
1314
|
}
|
|
1275
|
-
const registryMeta = this
|
|
1315
|
+
const registryMeta = this.#metadata.get(meta.class);
|
|
1276
1316
|
if (registryMeta && registryMeta !== meta) {
|
|
1277
1317
|
registryMeta.inheritanceType = meta.inheritanceType;
|
|
1278
|
-
registryMeta.tptParent = meta.tptParent ? this
|
|
1279
|
-
registryMeta.tptChildren = meta.tptChildren?.map(child => this
|
|
1318
|
+
registryMeta.tptParent = meta.tptParent ? this.#metadata.get(meta.tptParent.class) : undefined;
|
|
1319
|
+
registryMeta.tptChildren = meta.tptChildren?.map(child => this.#metadata.get(child.class));
|
|
1280
1320
|
}
|
|
1281
1321
|
this.initTPTDiscriminator(meta, metadata);
|
|
1282
1322
|
}
|
|
@@ -1294,12 +1334,12 @@ export class MetadataDiscovery {
|
|
|
1294
1334
|
}
|
|
1295
1335
|
meta.root.discriminatorMap = {};
|
|
1296
1336
|
for (const m of allDescendants) {
|
|
1297
|
-
const name = this
|
|
1337
|
+
const name = this.#namingStrategy.classToTableName(m.className);
|
|
1298
1338
|
meta.root.discriminatorMap[name] = m.class;
|
|
1299
1339
|
m.discriminatorValue = name;
|
|
1300
1340
|
}
|
|
1301
1341
|
if (!meta.abstract) {
|
|
1302
|
-
const name = this
|
|
1342
|
+
const name = this.#namingStrategy.classToTableName(meta.className);
|
|
1303
1343
|
meta.root.discriminatorMap[name] = meta.class;
|
|
1304
1344
|
meta.discriminatorValue = name;
|
|
1305
1345
|
}
|
|
@@ -1412,7 +1452,7 @@ export class MetadataDiscovery {
|
|
|
1412
1452
|
}
|
|
1413
1453
|
initAutoincrement(meta) {
|
|
1414
1454
|
const pks = meta.getPrimaryProps();
|
|
1415
|
-
if (pks.length === 1 && this
|
|
1455
|
+
if (pks.length === 1 && this.#platform.isNumericProperty(pks[0])) {
|
|
1416
1456
|
/* v8 ignore next */
|
|
1417
1457
|
pks[0].autoincrement ??= true;
|
|
1418
1458
|
}
|
|
@@ -1431,19 +1471,22 @@ export class MetadataDiscovery {
|
|
|
1431
1471
|
const table = this.createSchemaTable(meta);
|
|
1432
1472
|
for (const check of meta.checks) {
|
|
1433
1473
|
const fieldNames = check.property ? meta.properties[check.property].fieldNames : [];
|
|
1434
|
-
check.name ??= this
|
|
1474
|
+
check.name ??= this.#namingStrategy.indexName(meta.tableName, fieldNames, 'check');
|
|
1435
1475
|
if (check.expression instanceof Function) {
|
|
1436
1476
|
check.expression = check.expression(columns, table);
|
|
1437
1477
|
}
|
|
1438
1478
|
}
|
|
1439
|
-
if (this
|
|
1479
|
+
if (this.#platform.usesEnumCheckConstraints() && !meta.embeddable) {
|
|
1440
1480
|
for (const prop of meta.props) {
|
|
1441
|
-
if (prop.enum &&
|
|
1481
|
+
if (prop.enum &&
|
|
1482
|
+
prop.persist !== false &&
|
|
1483
|
+
!prop.nativeEnumName &&
|
|
1484
|
+
prop.items?.every(item => typeof item === 'string')) {
|
|
1442
1485
|
this.initFieldName(prop);
|
|
1443
1486
|
meta.checks.push({
|
|
1444
|
-
name: this
|
|
1487
|
+
name: this.#namingStrategy.indexName(meta.tableName, prop.fieldNames, 'check'),
|
|
1445
1488
|
property: prop.name,
|
|
1446
|
-
expression: `${this
|
|
1489
|
+
expression: `${this.#platform.quoteIdentifier(prop.fieldNames[0])} in ('${prop.items.join("', '")}')`,
|
|
1447
1490
|
});
|
|
1448
1491
|
}
|
|
1449
1492
|
}
|
|
@@ -1451,13 +1494,13 @@ export class MetadataDiscovery {
|
|
|
1451
1494
|
}
|
|
1452
1495
|
initGeneratedColumn(meta, prop) {
|
|
1453
1496
|
if (!prop.generated && prop.columnTypes) {
|
|
1454
|
-
const match =
|
|
1497
|
+
const match = /(.*) generated always as (.*)/i.exec(prop.columnTypes[0]);
|
|
1455
1498
|
if (match) {
|
|
1456
1499
|
prop.columnTypes[0] = match[1];
|
|
1457
1500
|
prop.generated = match[2];
|
|
1458
1501
|
return;
|
|
1459
1502
|
}
|
|
1460
|
-
const match2 = prop.columnTypes[0]?.trim()
|
|
1503
|
+
const match2 = /^as (.*)/i.exec(prop.columnTypes[0]?.trim());
|
|
1461
1504
|
if (match2) {
|
|
1462
1505
|
prop.generated = match2[1];
|
|
1463
1506
|
}
|
|
@@ -1474,13 +1517,13 @@ export class MetadataDiscovery {
|
|
|
1474
1517
|
}
|
|
1475
1518
|
/* v8 ignore next */
|
|
1476
1519
|
if (prop.default != null) {
|
|
1477
|
-
return '' + this
|
|
1520
|
+
return '' + this.#platform.convertVersionValue(prop.default, prop);
|
|
1478
1521
|
}
|
|
1479
1522
|
this.initCustomType(meta, prop, true);
|
|
1480
1523
|
const type = prop.customType?.runtimeType ?? prop.runtimeType ?? prop.type;
|
|
1481
1524
|
if (type === 'Date') {
|
|
1482
|
-
prop.length ??= this
|
|
1483
|
-
return this
|
|
1525
|
+
prop.length ??= this.#platform.getDefaultVersionLength();
|
|
1526
|
+
return this.#platform.getCurrentTimestampSQL(prop.length);
|
|
1484
1527
|
}
|
|
1485
1528
|
return '1';
|
|
1486
1529
|
}
|
|
@@ -1491,7 +1534,11 @@ export class MetadataDiscovery {
|
|
|
1491
1534
|
const entity1 = new meta.class();
|
|
1492
1535
|
const entity2 = new meta.class();
|
|
1493
1536
|
// we compare the two values by reference, this will discard things like `new Date()` or `Date.now()`
|
|
1494
|
-
if (this
|
|
1537
|
+
if (this.#config.get('discovery').inferDefaultValues &&
|
|
1538
|
+
prop.default === undefined &&
|
|
1539
|
+
entity1[prop.name] != null &&
|
|
1540
|
+
entity1[prop.name] === entity2[prop.name] &&
|
|
1541
|
+
entity1[prop.name] !== now) {
|
|
1495
1542
|
prop.default ??= entity1[prop.name];
|
|
1496
1543
|
}
|
|
1497
1544
|
// if the default value is null, infer nullability
|
|
@@ -1514,11 +1561,11 @@ export class MetadataDiscovery {
|
|
|
1514
1561
|
let val = prop.default;
|
|
1515
1562
|
const raw = Raw.getKnownFragment(val);
|
|
1516
1563
|
if (raw) {
|
|
1517
|
-
prop.defaultRaw = this
|
|
1564
|
+
prop.defaultRaw = this.#platform.formatQuery(raw.sql, raw.params);
|
|
1518
1565
|
return;
|
|
1519
1566
|
}
|
|
1520
1567
|
if (Array.isArray(prop.default) && prop.customType) {
|
|
1521
|
-
val = prop.customType.convertToDatabaseValue(prop.default, this
|
|
1568
|
+
val = prop.customType.convertToDatabaseValue(prop.default, this.#platform);
|
|
1522
1569
|
}
|
|
1523
1570
|
prop.defaultRaw = typeof val === 'string' ? `'${val}'` : '' + val;
|
|
1524
1571
|
}
|
|
@@ -1558,7 +1605,9 @@ export class MetadataDiscovery {
|
|
|
1558
1605
|
prop.type = prop.customType.constructor.name;
|
|
1559
1606
|
}
|
|
1560
1607
|
// `prop.type` might also be custom type class (not instance), so `typeof MyType` will give us `function`, not `object`
|
|
1561
|
-
if (typeof prop.type === 'function' &&
|
|
1608
|
+
if (typeof prop.type === 'function' &&
|
|
1609
|
+
Type.isMappedType(prop.type.prototype) &&
|
|
1610
|
+
!prop.customType) {
|
|
1562
1611
|
// if the type is an ORM defined mapped type without `ensureComparable: true`,
|
|
1563
1612
|
// we use just the type name, to have more performant hydration code
|
|
1564
1613
|
const type = Utils.keys(t).find(type => {
|
|
@@ -1578,7 +1627,10 @@ export class MetadataDiscovery {
|
|
|
1578
1627
|
if (!prop.customType && ['json', 'jsonb'].includes(prop.type?.toLowerCase())) {
|
|
1579
1628
|
prop.customType = new t.json();
|
|
1580
1629
|
}
|
|
1581
|
-
if (prop.kind === ReferenceKind.SCALAR &&
|
|
1630
|
+
if (prop.kind === ReferenceKind.SCALAR &&
|
|
1631
|
+
!prop.customType &&
|
|
1632
|
+
prop.columnTypes &&
|
|
1633
|
+
['json', 'jsonb'].includes(prop.columnTypes[0])) {
|
|
1582
1634
|
prop.customType = new t.json();
|
|
1583
1635
|
}
|
|
1584
1636
|
if (prop.kind === ReferenceKind.EMBEDDED && !prop.customType && (prop.object || prop.array)) {
|
|
@@ -1609,10 +1661,12 @@ export class MetadataDiscovery {
|
|
|
1609
1661
|
if (prop.fieldNames?.length === 1 && !prop.customType) {
|
|
1610
1662
|
[t.bigint, t.double, t.decimal, t.interval, t.date]
|
|
1611
1663
|
.filter(type => mappedType instanceof type)
|
|
1612
|
-
.forEach((type) => prop.customType = new type());
|
|
1664
|
+
.forEach((type) => (prop.customType = new type()));
|
|
1613
1665
|
}
|
|
1614
1666
|
if (prop.customType && !prop.columnTypes) {
|
|
1615
|
-
const mappedType = this.getMappedType({
|
|
1667
|
+
const mappedType = this.getMappedType({
|
|
1668
|
+
columnTypes: [prop.customType.getColumnType(prop, this.#platform)],
|
|
1669
|
+
});
|
|
1616
1670
|
if (prop.customType.compareAsType() === 'any' && ![t.json].some(t => prop.customType instanceof t)) {
|
|
1617
1671
|
prop.runtimeType ??= mappedType.runtimeType;
|
|
1618
1672
|
}
|
|
@@ -1627,27 +1681,37 @@ export class MetadataDiscovery {
|
|
|
1627
1681
|
prop.runtimeType ??= mappedType.runtimeType;
|
|
1628
1682
|
}
|
|
1629
1683
|
if (prop.customType) {
|
|
1630
|
-
prop.customType.platform = this
|
|
1684
|
+
prop.customType.platform = this.#platform;
|
|
1631
1685
|
prop.customType.meta = meta;
|
|
1632
1686
|
prop.customType.prop = prop;
|
|
1633
|
-
prop.columnTypes ??= [prop.customType.getColumnType(prop, this
|
|
1634
|
-
prop.hasConvertToJSValueSQL =
|
|
1635
|
-
|
|
1636
|
-
|
|
1687
|
+
prop.columnTypes ??= [prop.customType.getColumnType(prop, this.#platform)];
|
|
1688
|
+
prop.hasConvertToJSValueSQL =
|
|
1689
|
+
!!prop.customType.convertToJSValueSQL && prop.customType.convertToJSValueSQL('', this.#platform) !== '';
|
|
1690
|
+
prop.hasConvertToDatabaseValueSQL =
|
|
1691
|
+
!!prop.customType.convertToDatabaseValueSQL &&
|
|
1692
|
+
prop.customType.convertToDatabaseValueSQL('', this.#platform) !== '';
|
|
1693
|
+
if (prop.customType instanceof t.bigint &&
|
|
1694
|
+
['string', 'bigint', 'number'].includes(prop.runtimeType.toLowerCase())) {
|
|
1637
1695
|
prop.customType.mode = prop.runtimeType.toLowerCase();
|
|
1638
1696
|
}
|
|
1639
1697
|
}
|
|
1640
1698
|
if (Type.isMappedType(prop.customType) && prop.kind === ReferenceKind.SCALAR && !isArray) {
|
|
1641
1699
|
prop.type = prop.customType.name;
|
|
1642
1700
|
}
|
|
1643
|
-
if (!prop.customType &&
|
|
1701
|
+
if (!prop.customType &&
|
|
1702
|
+
[ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind) &&
|
|
1703
|
+
!prop.polymorphic &&
|
|
1704
|
+
prop.targetMeta.compositePK) {
|
|
1644
1705
|
prop.customTypes = [];
|
|
1645
1706
|
for (const pk of prop.targetMeta.getPrimaryProps()) {
|
|
1646
1707
|
if (pk.customType) {
|
|
1647
1708
|
prop.customTypes.push(pk.customType);
|
|
1648
|
-
prop.hasConvertToJSValueSQL ||=
|
|
1709
|
+
prop.hasConvertToJSValueSQL ||=
|
|
1710
|
+
!!pk.customType.convertToJSValueSQL && pk.customType.convertToJSValueSQL('', this.#platform) !== '';
|
|
1649
1711
|
/* v8 ignore next */
|
|
1650
|
-
prop.hasConvertToDatabaseValueSQL ||=
|
|
1712
|
+
prop.hasConvertToDatabaseValueSQL ||=
|
|
1713
|
+
!!pk.customType.convertToDatabaseValueSQL &&
|
|
1714
|
+
pk.customType.convertToDatabaseValueSQL('', this.#platform) !== '';
|
|
1651
1715
|
}
|
|
1652
1716
|
else {
|
|
1653
1717
|
prop.customTypes.push(undefined);
|
|
@@ -1655,11 +1719,15 @@ export class MetadataDiscovery {
|
|
|
1655
1719
|
}
|
|
1656
1720
|
}
|
|
1657
1721
|
if (prop.kind === ReferenceKind.SCALAR && !(mappedType instanceof t.unknown)) {
|
|
1658
|
-
if (!prop.columnTypes &&
|
|
1722
|
+
if (!prop.columnTypes &&
|
|
1723
|
+
prop.nativeEnumName &&
|
|
1724
|
+
meta.schema !== this.#platform.getDefaultSchemaName() &&
|
|
1725
|
+
meta.schema &&
|
|
1726
|
+
!prop.nativeEnumName.includes('.')) {
|
|
1659
1727
|
prop.columnTypes = [`${meta.schema}.${prop.nativeEnumName}`];
|
|
1660
1728
|
}
|
|
1661
1729
|
else {
|
|
1662
|
-
prop.columnTypes ??= [mappedType.getColumnType(prop, this
|
|
1730
|
+
prop.columnTypes ??= [mappedType.getColumnType(prop, this.#platform)];
|
|
1663
1731
|
}
|
|
1664
1732
|
// use only custom types provided by user, we don't need to use the ones provided by ORM,
|
|
1665
1733
|
// with exception for ArrayType and JsonType, those two are handled in
|
|
@@ -1673,7 +1741,7 @@ export class MetadataDiscovery {
|
|
|
1673
1741
|
return;
|
|
1674
1742
|
}
|
|
1675
1743
|
// when the target is a polymorphic embedded entity, `prop.target` is an array of classes, we need to get the metadata by the type name instead
|
|
1676
|
-
const meta2 = this
|
|
1744
|
+
const meta2 = this.#metadata.find(prop.target) ?? this.#metadata.getByClassName(prop.type);
|
|
1677
1745
|
// If targetKey is specified, use that property instead of PKs for referencedPKs
|
|
1678
1746
|
prop.referencedPKs = prop.targetKey ? [prop.targetKey] : meta2.primaryKeys;
|
|
1679
1747
|
prop.targetMeta = meta2;
|
|
@@ -1682,10 +1750,13 @@ export class MetadataDiscovery {
|
|
|
1682
1750
|
}
|
|
1683
1751
|
// Auto-generate formula for persist: false relations, but only for single-column FKs
|
|
1684
1752
|
// Composite FK relations need standard JOIN conditions, not formula-based
|
|
1685
|
-
if (!prop.formula &&
|
|
1753
|
+
if (!prop.formula &&
|
|
1754
|
+
prop.persist === false &&
|
|
1755
|
+
[ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
1756
|
+
!prop.embedded) {
|
|
1686
1757
|
this.initFieldName(prop);
|
|
1687
1758
|
if (prop.fieldNames?.length === 1) {
|
|
1688
|
-
prop.formula = table => `${table}.${this
|
|
1759
|
+
prop.formula = table => `${table}.${this.#platform.quoteIdentifier(prop.fieldNames[0])}`;
|
|
1689
1760
|
}
|
|
1690
1761
|
}
|
|
1691
1762
|
}
|
|
@@ -1693,7 +1764,9 @@ export class MetadataDiscovery {
|
|
|
1693
1764
|
this.initUnsigned(prop);
|
|
1694
1765
|
// Get the target properties for FK relations - use targetKey property if specified, otherwise PKs
|
|
1695
1766
|
const targetProps = prop.targetMeta
|
|
1696
|
-
?
|
|
1767
|
+
? prop.targetKey
|
|
1768
|
+
? [prop.targetMeta.properties[prop.targetKey]]
|
|
1769
|
+
: prop.targetMeta.getPrimaryProps()
|
|
1697
1770
|
: [];
|
|
1698
1771
|
targetProps.map(targetProp => {
|
|
1699
1772
|
prop.length ??= targetProp.length;
|
|
@@ -1705,38 +1778,36 @@ export class MetadataDiscovery {
|
|
|
1705
1778
|
const mappedType = this.getMappedType(prop);
|
|
1706
1779
|
prop.type = mappedType.compareAsType();
|
|
1707
1780
|
}
|
|
1708
|
-
if (prop.columnTypes || !this
|
|
1781
|
+
if (prop.columnTypes || !this.#schemaHelper) {
|
|
1709
1782
|
return;
|
|
1710
1783
|
}
|
|
1711
1784
|
if (prop.kind === ReferenceKind.SCALAR) {
|
|
1712
1785
|
const mappedType = this.getMappedType(prop);
|
|
1713
1786
|
const SCALAR_TYPES = ['string', 'number', 'boolean', 'bigint', 'Date', 'Buffer', 'RegExp', 'any', 'unknown'];
|
|
1714
|
-
if (mappedType instanceof t.unknown
|
|
1787
|
+
if (mappedType instanceof t.unknown &&
|
|
1715
1788
|
// it could be a runtime type from reflect-metadata
|
|
1716
|
-
|
|
1789
|
+
!SCALAR_TYPES.includes(prop.type) &&
|
|
1717
1790
|
// or it might be inferred via ts-morph to some generic type alias
|
|
1718
|
-
|
|
1791
|
+
!/[<>:"';{}]/.exec(prop.type)) {
|
|
1719
1792
|
const type = prop.length != null && !prop.type.endsWith(`(${prop.length})`) ? `${prop.type}(${prop.length})` : prop.type;
|
|
1720
1793
|
prop.columnTypes = [type];
|
|
1721
1794
|
}
|
|
1722
1795
|
else {
|
|
1723
|
-
prop.columnTypes = [mappedType.getColumnType(prop, this
|
|
1796
|
+
prop.columnTypes = [mappedType.getColumnType(prop, this.#platform)];
|
|
1724
1797
|
}
|
|
1725
1798
|
return;
|
|
1726
1799
|
}
|
|
1727
1800
|
/* v8 ignore next */
|
|
1728
1801
|
if (prop.kind === ReferenceKind.EMBEDDED && prop.object) {
|
|
1729
|
-
prop.columnTypes = [this
|
|
1802
|
+
prop.columnTypes = [this.#platform.getJsonDeclarationSQL()];
|
|
1730
1803
|
return;
|
|
1731
1804
|
}
|
|
1732
1805
|
const targetMeta = prop.targetMeta;
|
|
1733
1806
|
prop.columnTypes = [];
|
|
1734
1807
|
// Use targetKey property if specified, otherwise use primary key properties
|
|
1735
|
-
const referencedProps = prop.targetKey
|
|
1736
|
-
? [targetMeta.properties[prop.targetKey]]
|
|
1737
|
-
: targetMeta.getPrimaryProps();
|
|
1808
|
+
const referencedProps = prop.targetKey ? [targetMeta.properties[prop.targetKey]] : targetMeta.getPrimaryProps();
|
|
1738
1809
|
if (prop.polymorphic && prop.polymorphTargets) {
|
|
1739
|
-
prop.columnTypes.push(this
|
|
1810
|
+
prop.columnTypes.push(this.#platform.getVarcharTypeDeclarationSQL(prop));
|
|
1740
1811
|
}
|
|
1741
1812
|
for (const referencedProp of referencedProps) {
|
|
1742
1813
|
this.initCustomType(targetMeta, referencedProp);
|
|
@@ -1744,7 +1815,7 @@ export class MetadataDiscovery {
|
|
|
1744
1815
|
const mappedType = this.getMappedType(referencedProp);
|
|
1745
1816
|
let columnTypes = referencedProp.columnTypes;
|
|
1746
1817
|
if (referencedProp.autoincrement) {
|
|
1747
|
-
columnTypes = [mappedType.getColumnType({ ...referencedProp, autoincrement: false }, this
|
|
1818
|
+
columnTypes = [mappedType.getColumnType({ ...referencedProp, autoincrement: false }, this.#platform)];
|
|
1748
1819
|
}
|
|
1749
1820
|
prop.columnTypes.push(...columnTypes);
|
|
1750
1821
|
if (!targetMeta.compositePK || prop.targetKey) {
|
|
@@ -1767,7 +1838,7 @@ export class MetadataDiscovery {
|
|
|
1767
1838
|
if (t === 'Date') {
|
|
1768
1839
|
t = 'datetime';
|
|
1769
1840
|
}
|
|
1770
|
-
return this
|
|
1841
|
+
return this.#platform.getMappedType(t);
|
|
1771
1842
|
}
|
|
1772
1843
|
getPrefix(prop, parent) {
|
|
1773
1844
|
const { embeddedPath = [], fieldNames, prefix = true, prefixMode } = prop;
|
|
@@ -1778,7 +1849,7 @@ export class MetadataDiscovery {
|
|
|
1778
1849
|
if (prefix === false) {
|
|
1779
1850
|
return prefixParent;
|
|
1780
1851
|
}
|
|
1781
|
-
const mode = prefixMode ?? this
|
|
1852
|
+
const mode = prefixMode ?? this.#config.get('embeddables').prefixMode;
|
|
1782
1853
|
return mode === 'absolute' ? prefix : prefixParent + prefix;
|
|
1783
1854
|
}
|
|
1784
1855
|
initUnsigned(prop) {
|
|
@@ -1792,16 +1863,17 @@ export class MetadataDiscovery {
|
|
|
1792
1863
|
});
|
|
1793
1864
|
return;
|
|
1794
1865
|
}
|
|
1795
|
-
prop.unsigned ??=
|
|
1866
|
+
prop.unsigned ??=
|
|
1867
|
+
(prop.primary || prop.unsigned) && this.#platform.isNumericProperty(prop) && this.#platform.supportsUnsigned();
|
|
1796
1868
|
}
|
|
1797
1869
|
initIndexes(meta, prop) {
|
|
1798
1870
|
const hasIndex = meta.indexes.some(idx => idx.properties?.length === 1 && idx.properties[0] === prop.name);
|
|
1799
|
-
if (prop.kind === ReferenceKind.MANY_TO_ONE && this
|
|
1871
|
+
if (prop.kind === ReferenceKind.MANY_TO_ONE && this.#platform.indexForeignKeys() && !hasIndex) {
|
|
1800
1872
|
prop.index ??= true;
|
|
1801
1873
|
}
|
|
1802
1874
|
}
|
|
1803
1875
|
shouldForceConstructorUsage(meta) {
|
|
1804
|
-
const forceConstructor = this
|
|
1876
|
+
const forceConstructor = this.#config.get('forceEntityConstructor');
|
|
1805
1877
|
if (Array.isArray(forceConstructor)) {
|
|
1806
1878
|
return forceConstructor.some(cls => Utils.className(cls) === meta.className);
|
|
1807
1879
|
}
|