1 10
import {CockroachDriver} from "../driver/cockroachdb/CockroachDriver";
2 10
import {SapDriver} from "../driver/sap/SapDriver";
3 10
import {EntityMetadata} from "../metadata/EntityMetadata";
4 10
import {ColumnMetadata} from "../metadata/ColumnMetadata";
5 10
import {IndexMetadata} from "../metadata/IndexMetadata";
6 10
import {RelationMetadata} from "../metadata/RelationMetadata";
7 10
import {EmbeddedMetadata} from "../metadata/EmbeddedMetadata";
8
import {MetadataArgsStorage} from "../metadata-args/MetadataArgsStorage";
9
import {EmbeddedMetadataArgs} from "../metadata-args/EmbeddedMetadataArgs";
10 10
import {RelationIdMetadata} from "../metadata/RelationIdMetadata";
11 10
import {RelationCountMetadata} from "../metadata/RelationCountMetadata";
12 10
import { EventListenerTypes } from "../metadata/types/EventListenerTypes";
13 10
import {MetadataUtils} from "./MetadataUtils";
14
import {TableMetadataArgs} from "../metadata-args/TableMetadataArgs";
15 10
import {JunctionEntityMetadataBuilder} from "./JunctionEntityMetadataBuilder";
16 10
import {ClosureJunctionEntityMetadataBuilder} from "./ClosureJunctionEntityMetadataBuilder";
17 10
import {RelationJoinColumnBuilder} from "./RelationJoinColumnBuilder";
18
import {Connection} from "../connection/Connection";
19 10
import {EntityListenerMetadata} from "../metadata/EntityListenerMetadata";
20 10
import {UniqueMetadata} from "../metadata/UniqueMetadata";
21 10
import {MysqlDriver} from "../driver/mysql/MysqlDriver";
22 10
import {CheckMetadata} from "../metadata/CheckMetadata";
23 10
import {SqlServerDriver} from "../driver/sqlserver/SqlServerDriver";
24 10
import {PostgresDriver} from "../driver/postgres/PostgresDriver";
25 10
import {ExclusionMetadata} from "../metadata/ExclusionMetadata";
26 10
import {AuroraDataApiDriver} from "../driver/aurora-data-api/AuroraDataApiDriver";
27

28
/**
29
 * Builds EntityMetadata objects and all its sub-metadatas.
30
 */
31 10
export class EntityMetadataBuilder {
32

33
    // -------------------------------------------------------------------------
34
    // Protected Properties
35
    // -------------------------------------------------------------------------
36

37
    /**
38
     * Used to build entity metadatas of the junction entities.
39
     */
40
    protected junctionEntityMetadataBuilder: JunctionEntityMetadataBuilder;
41

42
    /**
43
     * Used to build entity metadatas of the closure junction entities.
44
     */
45
    protected closureJunctionEntityMetadataBuilder: ClosureJunctionEntityMetadataBuilder;
46

47
    /**
48
     * Used to build join columns of the relations.
49
     */
50
    protected relationJoinColumnBuilder: RelationJoinColumnBuilder;
51

52
    // -------------------------------------------------------------------------
53
    // Constructor
54
    // -------------------------------------------------------------------------
55

56 10
    constructor(private connection: Connection,
57 10
                private metadataArgsStorage: MetadataArgsStorage) {
58

59 10
        this.junctionEntityMetadataBuilder = new JunctionEntityMetadataBuilder(connection);
60 10
        this.closureJunctionEntityMetadataBuilder = new ClosureJunctionEntityMetadataBuilder(connection);
61 10
        this.relationJoinColumnBuilder = new RelationJoinColumnBuilder(connection);
62
    }
63

64
    // -------------------------------------------------------------------------
65
    // Public Methods
66
    // -------------------------------------------------------------------------
67

68
    /**
69
     * Builds a complete entity metadatas for the given entity classes.
70
     */
71 10
    build(entityClasses?: Function[]): EntityMetadata[] {
72

73
        // if entity classes to filter entities by are given then do filtering, otherwise use all
74 10
        const allTables = entityClasses ? this.metadataArgsStorage.filterTables(entityClasses) : this.metadataArgsStorage.tables;
75

76
        // filter out table metadata args for those we really create entity metadatas and tables in the db
77 10
        const realTables = allTables.filter(table => table.type === "regular" || table.type === "closure" || table.type === "entity-child" || table.type === "view");
78

79
        // create entity metadatas for a user defined entities (marked with @Entity decorator or loaded from entity schemas)
80 10
        const entityMetadatas = realTables.map(tableArgs => this.createEntityMetadata(tableArgs));
81

82
        // compute parent entity metadatas for table inheritance
83 10
        entityMetadatas.forEach(entityMetadata => this.computeParentEntityMetadata(entityMetadatas, entityMetadata));
84

85
        // after all metadatas created we set child entity metadatas for table inheritance
86 10
        entityMetadatas.forEach(metadata => {
87 10
            metadata.childEntityMetadatas = entityMetadatas.filter(childMetadata => {
88 10
                return metadata.target instanceof Function
89 10
                    && childMetadata.target instanceof Function
90 10
                    && MetadataUtils.isInherited(childMetadata.target, metadata.target);
91
            });
92
        });
93

94
        // build entity metadata (step0), first for non-single-table-inherited entity metadatas (dependant)
95 10
        entityMetadatas
96 10
            .filter(entityMetadata => entityMetadata.tableType !== "entity-child")
97 10
            .forEach(entityMetadata => entityMetadata.build());
98

99
        // build entity metadata (step0), now for single-table-inherited entity metadatas (dependant)
100 10
        entityMetadatas
101 10
            .filter(entityMetadata => entityMetadata.tableType === "entity-child")
102 10
            .forEach(entityMetadata => entityMetadata.build());
103

104
        // compute entity metadata columns, relations, etc. first for the regular, non-single-table-inherited entity metadatas
105 10
        entityMetadatas
106 10
            .filter(entityMetadata => entityMetadata.tableType !== "entity-child")
107 10
            .forEach(entityMetadata => this.computeEntityMetadataStep1(entityMetadatas, entityMetadata));
108

109
        // then do it for single table inheritance children (since they are depend on their parents to be built)
110 10
        entityMetadatas
111 10
            .filter(entityMetadata => entityMetadata.tableType === "entity-child")
112 10
            .forEach(entityMetadata => this.computeEntityMetadataStep1(entityMetadatas, entityMetadata));
113

114
        // calculate entity metadata computed properties and all its sub-metadatas
115 10
        entityMetadatas.forEach(entityMetadata => this.computeEntityMetadataStep2(entityMetadata));
116

117
        // calculate entity metadata's inverse properties
118 10
        entityMetadatas.forEach(entityMetadata => this.computeInverseProperties(entityMetadata, entityMetadatas));
119

120
        // go through all entity metadatas and create foreign keys / junction entity metadatas for their relations
121 10
        entityMetadatas
122 10
            .filter(entityMetadata => entityMetadata.tableType !== "entity-child")
123 10
            .forEach(entityMetadata => {
124

125
                // create entity's relations join columns (for many-to-one and one-to-one owner)
126 10
                entityMetadata.relations.filter(relation => relation.isOneToOne || relation.isManyToOne).forEach(relation => {
127 10
                    const joinColumns = this.metadataArgsStorage.filterJoinColumns(relation.target, relation.propertyName);
128 10
                    const { foreignKey, uniqueConstraint } = this.relationJoinColumnBuilder.build(joinColumns, relation); // create a foreign key based on its metadata args
129 10
                    if (foreignKey) {
130 10
                        relation.registerForeignKeys(foreignKey); // push it to the relation and thus register there a join column
131 10
                        entityMetadata.foreignKeys.push(foreignKey);
132
                    }
133 10
                    if (uniqueConstraint) {
134 10
                        if (this.connection.driver instanceof MysqlDriver || this.connection.driver instanceof AuroraDataApiDriver
135 10
                            || this.connection.driver instanceof SqlServerDriver || this.connection.driver instanceof SapDriver) {
136 10
                            const index = new IndexMetadata({
137
                                entityMetadata: uniqueConstraint.entityMetadata,
138
                                columns: uniqueConstraint.columns,
139
                                args: {
140
                                    target: uniqueConstraint.target!,
141
                                    name: uniqueConstraint.name,
142
                                    unique: true,
143
                                    synchronize: true
144
                                }
145
                            });
146

147 10
                            if (this.connection.driver instanceof SqlServerDriver) {
148 6
                                index.where = index.columns.map(column => {
149 6
                                    return `${this.connection.driver.escape(column.databaseName)} IS NOT NULL`;
150
                                }).join(" AND ");
151
                            }
152

153 10
                            if (relation.embeddedMetadata) {
154 6
                                relation.embeddedMetadata.indices.push(index);
155
                            } else {
156 10
                                relation.entityMetadata.ownIndices.push(index);
157
                            }
158 10
                            this.computeEntityMetadataStep2(entityMetadata);
159

160
                        } else {
161 10
                            if (relation.embeddedMetadata) {
162 10
                                relation.embeddedMetadata.uniques.push(uniqueConstraint);
163
                            } else {
164 10
                                relation.entityMetadata.ownUniques.push(uniqueConstraint);
165
                            }
166 10
                            this.computeEntityMetadataStep2(entityMetadata);
167
                        }
168
                    }
169

170 10
                    if (foreignKey && this.connection.driver instanceof CockroachDriver) {
171 2
                        const index = new IndexMetadata({
172
                            entityMetadata: relation.entityMetadata,
173
                            columns: foreignKey.columns,
174
                            args: {
175
                                target: relation.entityMetadata.target!,
176
                                synchronize: true
177
                            }
178
                        });
179 2
                        if (relation.embeddedMetadata) {
180 2
                            relation.embeddedMetadata.indices.push(index);
181
                        } else {
182 2
                            relation.entityMetadata.ownIndices.push(index);
183
                        }
184 2
                        this.computeEntityMetadataStep2(entityMetadata);
185
                    }
186
                });
187

188
                // create junction entity metadatas for entity many-to-many relations
189 10
                entityMetadata.relations.filter(relation => relation.isManyToMany).forEach(relation => {
190 10
                    const joinTable = this.metadataArgsStorage.findJoinTable(relation.target, relation.propertyName)!;
191 10
                    if (!joinTable) return; // no join table set - no need to do anything (it means this is many-to-many inverse side)
192

193
                    // here we create a junction entity metadata for a new junction table of many-to-many relation
194 10
                    const junctionEntityMetadata = this.junctionEntityMetadataBuilder.build(relation, joinTable);
195 10
                    relation.registerForeignKeys(...junctionEntityMetadata.foreignKeys);
196 10
                    relation.registerJunctionEntityMetadata(junctionEntityMetadata);
197

198
                    // compute new entity metadata properties and push it to entity metadatas pool
199 10
                    this.computeEntityMetadataStep2(junctionEntityMetadata);
200 10
                    this.computeInverseProperties(junctionEntityMetadata, entityMetadatas);
201 10
                    entityMetadatas.push(junctionEntityMetadata);
202
                });
203

204
        });
205

206
        // update entity metadata depend properties
207 10
        entityMetadatas
208 10
            .forEach(entityMetadata => {
209 10
                entityMetadata.relationsWithJoinColumns = entityMetadata.relations.filter(relation => relation.isWithJoinColumn);
210 10
                entityMetadata.hasNonNullableRelations = entityMetadata.relationsWithJoinColumns.some(relation => !relation.isNullable || relation.isPrimary);
211
            });
212

213
        // generate closure junction tables for all closure tables
214 10
        entityMetadatas
215 10
            .filter(metadata => metadata.treeType === "closure-table")
216 10
            .forEach(entityMetadata => {
217 10
                const closureJunctionEntityMetadata = this.closureJunctionEntityMetadataBuilder.build(entityMetadata);
218 10
                entityMetadata.closureJunctionTable = closureJunctionEntityMetadata;
219 10
                this.computeEntityMetadataStep2(closureJunctionEntityMetadata);
220 10
                this.computeInverseProperties(closureJunctionEntityMetadata, entityMetadatas);
221 10
                entityMetadatas.push(closureJunctionEntityMetadata);
222
            });
223

224
        // generate keys for tables with single-table inheritance
225 10
        entityMetadatas
226 10
            .filter(metadata => metadata.inheritancePattern === "STI" && metadata.discriminatorColumn)
227 10
            .forEach(entityMetadata => this.createKeysForTableInheritance(entityMetadata));
228

229
        // build all indices (need to do it after relations and their join columns are built)
230 10
        entityMetadatas.forEach(entityMetadata => {
231 10
            entityMetadata.indices.forEach(index => index.build(this.connection.namingStrategy));
232
        });
233

234
        // build all unique constraints (need to do it after relations and their join columns are built)
235 10
        entityMetadatas.forEach(entityMetadata => {
236 10
            entityMetadata.uniques.forEach(unique => unique.build(this.connection.namingStrategy));
237
        });
238

239
        // build all check constraints
240 10
        entityMetadatas.forEach(entityMetadata => {
241 10
            entityMetadata.checks.forEach(check => check.build(this.connection.namingStrategy));
242
        });
243

244
        // build all exclusion constraints
245 10
        entityMetadatas.forEach(entityMetadata => {
246 10
            entityMetadata.exclusions.forEach(exclusion => exclusion.build(this.connection.namingStrategy));
247
        });
248

249
        // add lazy initializer for entity relations
250 10
        entityMetadatas
251 10
            .filter(metadata => metadata.target instanceof Function)
252 10
            .forEach(entityMetadata => {
253 10
                entityMetadata.relations
254 10
                    .filter(relation => relation.isLazy)
255 10
                    .forEach(relation => {
256 10
                        this.connection.relationLoader.enableLazyLoad(relation, (entityMetadata.target as Function).prototype);
257
                    });
258
            });
259

260 10
        entityMetadatas.forEach(entityMetadata => {
261 10
            entityMetadata.columns.forEach(column => {
262
                // const target = column.embeddedMetadata ? column.embeddedMetadata.type : column.target;
263 10
                const generated = this.metadataArgsStorage.findGenerated(column.target, column.propertyName);
264 10
                if (generated) {
265 10
                    column.isGenerated = true;
266 10
                    column.generationStrategy = generated.strategy;
267 10
                    if (generated.strategy === "uuid") {
268 10
                        column.type = "uuid";
269 10
                    } else if (generated.strategy === "rowid") {
270 2
                        column.type = "int";
271
                    } else {
272 10
                        column.type = column.type || Number;
273
                    }
274 10
                    column.build(this.connection);
275 10
                    this.computeEntityMetadataStep2(entityMetadata);
276
                }
277
            });
278

279
        });
280

281 10
        return entityMetadatas;
282
    }
283

284
    // -------------------------------------------------------------------------
285
    // Protected Methods
286
    // -------------------------------------------------------------------------
287

288
    /**
289
     * Creates entity metadata from the given table args.
290
     * Creates column, relation, etc. metadatas for everything this entity metadata owns.
291
     */
292 10
    protected createEntityMetadata(tableArgs: TableMetadataArgs): EntityMetadata {
293

294
        // we take all "inheritance tree" from a target entity to collect all stored metadata args
295
        // (by decorators or inside entity schemas). For example for target Post < ContentModel < Unit
296
        // it will be an array of [Post, ContentModel, Unit] and we can then get all metadata args of those classes
297 10
        const inheritanceTree: any[] = tableArgs.target instanceof Function
298 10
            ? MetadataUtils.getInheritanceTree(tableArgs.target)
299 10
            : [tableArgs.target]; // todo: implement later here inheritance for string-targets
300

301 10
        const tableInheritance = this.metadataArgsStorage.findInheritanceType(tableArgs.target);
302 10
        const tableTree = this.metadataArgsStorage.findTree(tableArgs.target);
303

304
        // if single table inheritance used, we need to copy all children columns in to parent table
305
        let singleTableChildrenTargets: any[];
306 10
        if ((tableInheritance && tableInheritance.pattern === "STI") || tableArgs.type === "entity-child") {
307 10
            singleTableChildrenTargets = this.metadataArgsStorage
308
                .filterSingleTableChildren(tableArgs.target)
309 10
                .map(args => args.target)
310 10
                .filter(target => target instanceof Function);
311

312 10
            inheritanceTree.push(...singleTableChildrenTargets);
313
        }
314

315 10
        return new EntityMetadata({
316
            connection: this.connection,
317
            args: tableArgs,
318
            inheritanceTree: inheritanceTree,
319
            tableTree: tableTree,
320 10
            inheritancePattern: tableInheritance ? tableInheritance.pattern : undefined
321
        });
322
    }
323

324 10
    protected computeParentEntityMetadata(allEntityMetadatas: EntityMetadata[], entityMetadata: EntityMetadata) {
325

326
        // after all metadatas created we set parent entity metadata for table inheritance
327 10
        if (entityMetadata.tableType === "entity-child") {
328 10
            entityMetadata.parentEntityMetadata = allEntityMetadatas.find(allEntityMetadata => {
329 10
                return allEntityMetadata.inheritanceTree.indexOf(entityMetadata.target as Function) !== -1 && allEntityMetadata.inheritancePattern === "STI";
330
            })!;
331
        }
332
    }
333

334 10
    protected computeEntityMetadataStep1(allEntityMetadatas: EntityMetadata[], entityMetadata: EntityMetadata) {
335

336 10
        const entityInheritance = this.metadataArgsStorage.findInheritanceType(entityMetadata.target);
337

338 10
        const discriminatorValue = this.metadataArgsStorage.findDiscriminatorValue(entityMetadata.target);
339

340 10
        if (typeof discriminatorValue !== "undefined") {
341 10
            entityMetadata.discriminatorValue = discriminatorValue.value;
342
        } else {
343 10
            entityMetadata.discriminatorValue = (entityMetadata.target as any).name;
344
        }
345

346
        // if single table inheritance is used, we need to mark all embedded columns as nullable
347 10
        entityMetadata.embeddeds = this.createEmbeddedsRecursively(entityMetadata, this.metadataArgsStorage.filterEmbeddeds(entityMetadata.inheritanceTree))
348 10
            .map((embedded: EmbeddedMetadata): EmbeddedMetadata => {
349 10
                 if (entityMetadata.inheritancePattern === "STI") {
350 10
                     embedded.columns = embedded.columns.map((column: ColumnMetadata): ColumnMetadata => {
351 10
                         column.isNullable = true;
352 10
                         return column;
353
                     });
354
                 }
355 10
                 return embedded;
356
            });
357

358 10
        entityMetadata.ownColumns = this.metadataArgsStorage
359
            .filterColumns(entityMetadata.inheritanceTree)
360 10
            .map(args => {
361

362
                // for single table children we reuse columns created for their parents
363 10
                if (entityMetadata.tableType === "entity-child")
364 10
                    return entityMetadata.parentEntityMetadata.ownColumns.find(column => column.propertyName === args.propertyName)!;
365

366 10
                const column = new ColumnMetadata({ connection: this.connection, entityMetadata, args });
367

368
                // if single table inheritance used, we need to mark all inherit table columns as nullable
369 10
                const columnInSingleTableInheritedChild = allEntityMetadatas.find(otherEntityMetadata => otherEntityMetadata.tableType === "entity-child" && otherEntityMetadata.target === args.target);
370 10
                if (columnInSingleTableInheritedChild)
371 10
                    column.isNullable = true;
372 10
                return column;
373
            });
374

375
        // for table inheritance we need to add a discriminator column
376
        //
377 10
        if (entityInheritance && entityInheritance.column) {
378 10
            const discriminatorColumnName = entityInheritance.column && entityInheritance.column.name ? entityInheritance.column.name : "type";
379 10
            let discriminatorColumn = entityMetadata.ownColumns.find(column => column.propertyName === discriminatorColumnName);
380 10
            if (!discriminatorColumn) {
381 10
                discriminatorColumn = new ColumnMetadata({
382
                    connection: this.connection,
383
                    entityMetadata: entityMetadata,
384
                    args: {
385
                        target: entityMetadata.target,
386
                        mode: "virtual",
387
                        propertyName: discriminatorColumnName,
388 10
                        options: entityInheritance.column || {
389
                            name: discriminatorColumnName,
390
                            type: "varchar",
391
                            nullable: false
392
                        }
393
                    }
394
                });
395 10
                discriminatorColumn.isVirtual = true;
396 10
                discriminatorColumn.isDiscriminator = true;
397 10
                entityMetadata.ownColumns.push(discriminatorColumn);
398
            } else {
399 10
                discriminatorColumn.isDiscriminator = true;
400
            }
401
        }
402

403
        // add discriminator column to the child entity metadatas
404
        // discriminator column will not be there automatically since we are creating it in the code above
405 10
        if (entityMetadata.tableType === "entity-child") {
406 10
            const discriminatorColumn = entityMetadata.parentEntityMetadata.ownColumns.find(column => column.isDiscriminator);
407 10
            if (discriminatorColumn && !entityMetadata.ownColumns.find(column => column === discriminatorColumn)) {
408 10
                entityMetadata.ownColumns.push(discriminatorColumn);
409
            }
410
        }
411

412 10
        const { namingStrategy } = this.connection;
413

414
        // check if tree is used then we need to add extra columns for specific tree types
415 10
        if (entityMetadata.treeType === "materialized-path") {
416 10
            entityMetadata.ownColumns.push(new ColumnMetadata({
417
                connection: this.connection,
418
                entityMetadata: entityMetadata,
419
                materializedPath: true,
420
                args: {
421
                    target: entityMetadata.target,
422
                    mode: "virtual",
423
                    propertyName: "mpath",
424
                    options: /*tree.column || */ {
425
                        name: namingStrategy.materializedPathColumnName,
426
                        type: "varchar",
427
                        nullable: true,
428
                        default: ""
429
                    }
430
                }
431
            }));
432

433 10
        } else if (entityMetadata.treeType === "nested-set") {
434 10
            const { left, right } = namingStrategy.nestedSetColumnNames;
435 10
            entityMetadata.ownColumns.push(new ColumnMetadata({
436
                connection: this.connection,
437
                entityMetadata: entityMetadata,
438
                nestedSetLeft: true,
439
                args: {
440
                    target: entityMetadata.target,
441
                    mode: "virtual",
442
                    propertyName: left,
443
                    options: /*tree.column || */ {
444
                        name: left,
445
                        type: "integer",
446
                        nullable: false,
447
                        default: 1
448
                    }
449
                }
450
            }));
451 10
            entityMetadata.ownColumns.push(new ColumnMetadata({
452
                connection: this.connection,
453
                entityMetadata: entityMetadata,
454
                nestedSetRight: true,
455
                args: {
456
                    target: entityMetadata.target,
457
                    mode: "virtual",
458
                    propertyName: right,
459
                    options: /*tree.column || */ {
460
                        name: right,
461
                        type: "integer",
462
                        nullable: false,
463
                        default: 2
464
                    }
465
                }
466
            }));
467
        }
468

469 10
        entityMetadata.ownRelations = this.metadataArgsStorage.filterRelations(entityMetadata.inheritanceTree).map(args => {
470

471
            // for single table children we reuse relations created for their parents
472 10
            if (entityMetadata.tableType === "entity-child")
473 10
                return entityMetadata.parentEntityMetadata.ownRelations.find(relation => relation.propertyName === args.propertyName)!;
474

475 10
            return new RelationMetadata({ entityMetadata, args });
476
        });
477 10
        entityMetadata.relationIds = this.metadataArgsStorage.filterRelationIds(entityMetadata.inheritanceTree).map(args => {
478

479
            // for single table children we reuse relation ids created for their parents
480 10
            if (entityMetadata.tableType === "entity-child")
481 0
                return entityMetadata.parentEntityMetadata.relationIds.find(relationId => relationId.propertyName === args.propertyName)!;
482

483 10
            return new RelationIdMetadata({ entityMetadata, args });
484
        });
485 10
        entityMetadata.relationCounts = this.metadataArgsStorage.filterRelationCounts(entityMetadata.inheritanceTree).map(args => {
486

487
            // for single table children we reuse relation counts created for their parents
488 10
            if (entityMetadata.tableType === "entity-child")
489 0
                return entityMetadata.parentEntityMetadata.relationCounts.find(relationCount => relationCount.propertyName === args.propertyName)!;
490

491 10
            return new RelationCountMetadata({ entityMetadata, args });
492
        });
493 10
        entityMetadata.ownListeners = this.metadataArgsStorage.filterListeners(entityMetadata.inheritanceTree).map(args => {
494 10
            return new EntityListenerMetadata({ entityMetadata: entityMetadata, args: args });
495
        });
496 10
        entityMetadata.checks = this.metadataArgsStorage.filterChecks(entityMetadata.inheritanceTree).map(args => {
497 10
            return new CheckMetadata({ entityMetadata, args });
498
        });
499

500
        // Only PostgreSQL supports exclusion constraints.
501 10
        if (this.connection.driver instanceof PostgresDriver) {
502 6
            entityMetadata.exclusions = this.metadataArgsStorage.filterExclusions(entityMetadata.inheritanceTree).map(args => {
503 6
                return new ExclusionMetadata({ entityMetadata, args });
504
            });
505
        }
506

507 10
        if (this.connection.driver instanceof CockroachDriver) {
508 2
            entityMetadata.ownIndices = this.metadataArgsStorage.filterIndices(entityMetadata.inheritanceTree)
509 2
                .filter(args => !args.unique)
510 2
                .map(args => {
511 2
                    return new IndexMetadata({entityMetadata, args});
512
                });
513

514 2
            const uniques = this.metadataArgsStorage.filterIndices(entityMetadata.inheritanceTree)
515 2
                .filter(args => args.unique)
516 2
                .map(args => {
517 2
                    return new UniqueMetadata({
518
                        entityMetadata: entityMetadata,
519
                        args: {
520
                            target: args.target,
521
                            name: args.name,
522
                            columns: args.columns,
523
                        }
524
                    });
525
                });
526 2
            entityMetadata.ownUniques.push(...uniques);
527

528
        } else {
529 10
            entityMetadata.ownIndices = this.metadataArgsStorage.filterIndices(entityMetadata.inheritanceTree).map(args => {
530 8
                return new IndexMetadata({entityMetadata, args});
531
            });
532
        }
533

534
        // Mysql and SAP HANA stores unique constraints as unique indices.
535 10
        if (this.connection.driver instanceof MysqlDriver || this.connection.driver instanceof AuroraDataApiDriver || this.connection.driver instanceof SapDriver) {
536 10
            const indices = this.metadataArgsStorage.filterUniques(entityMetadata.inheritanceTree).map(args => {
537 6
                return new IndexMetadata({
538
                    entityMetadata: entityMetadata,
539
                    args: {
540
                        target: args.target,
541
                        name: args.name,
542
                        columns: args.columns,
543
                        unique: true,
544
                        synchronize: true
545
                    }
546
                });
547
            });
548 10
            entityMetadata.ownIndices.push(...indices);
549

550
        } else {
551 10
            const uniques = this.metadataArgsStorage.filterUniques(entityMetadata.inheritanceTree).map(args => {
552 10
                return new UniqueMetadata({ entityMetadata, args });
553
            });
554 10
            entityMetadata.ownUniques.push(...uniques);
555
        }
556
    }
557

558
    /**
559
     * Creates from the given embedded metadata args real embedded metadatas with its columns and relations,
560
     * and does the same for all its sub-embeddeds (goes recursively).
561
     */
562 10
    protected createEmbeddedsRecursively(entityMetadata: EntityMetadata, embeddedArgs: EmbeddedMetadataArgs[]): EmbeddedMetadata[] {
563 10
        return embeddedArgs.map(embeddedArgs => {
564 10
            const embeddedMetadata = new EmbeddedMetadata({ entityMetadata: entityMetadata, args: embeddedArgs });
565 10
            const targets = MetadataUtils.getInheritanceTree(embeddedMetadata.type);
566

567 10
            embeddedMetadata.columns = this.metadataArgsStorage.filterColumns(targets).map(args => {
568 10
                return new ColumnMetadata({ connection: this.connection, entityMetadata, embeddedMetadata, args});
569
            });
570 10
            embeddedMetadata.relations = this.metadataArgsStorage.filterRelations(targets).map(args => {
571 10
                return new RelationMetadata({ entityMetadata, embeddedMetadata, args });
572
            });
573 10
            embeddedMetadata.listeners = this.metadataArgsStorage.filterListeners(targets).map(args => {
574 10
                return new EntityListenerMetadata({ entityMetadata, embeddedMetadata, args });
575
            });
576 10
            embeddedMetadata.indices = this.metadataArgsStorage.filterIndices(targets).map(args => {
577 10
                return new IndexMetadata({ entityMetadata, embeddedMetadata, args });
578
            });
579 10
            embeddedMetadata.uniques = this.metadataArgsStorage.filterUniques(targets).map(args => {
580 6
                return new UniqueMetadata({ entityMetadata, embeddedMetadata, args });
581
            });
582 10
            embeddedMetadata.relationIds = this.metadataArgsStorage.filterRelationIds(targets).map(args => {
583 0
                return new RelationIdMetadata({ entityMetadata, args });
584
            });
585 10
            embeddedMetadata.relationCounts = this.metadataArgsStorage.filterRelationCounts(targets).map(args => {
586 0
                return new RelationCountMetadata({ entityMetadata, args });
587
            });
588 10
            embeddedMetadata.embeddeds = this.createEmbeddedsRecursively(entityMetadata, this.metadataArgsStorage.filterEmbeddeds(targets));
589 10
            embeddedMetadata.embeddeds.forEach(subEmbedded => subEmbedded.parentEmbeddedMetadata = embeddedMetadata);
590 10
            entityMetadata.allEmbeddeds.push(embeddedMetadata);
591 10
            return embeddedMetadata;
592
        });
593
    }
594

595
    /**
596
     * Computes all entity metadata's computed properties, and all its sub-metadatas (relations, columns, embeds, etc).
597
     */
598 10
    protected computeEntityMetadataStep2(entityMetadata: EntityMetadata) {
599 10
        entityMetadata.embeddeds.forEach(embedded => embedded.build(this.connection));
600 10
        entityMetadata.embeddeds.forEach(embedded => {
601 10
            embedded.columnsFromTree.forEach(column => column.build(this.connection));
602 10
            embedded.relationsFromTree.forEach(relation => relation.build());
603
        });
604 10
        entityMetadata.ownColumns.forEach(column => column.build(this.connection));
605 10
        entityMetadata.ownRelations.forEach(relation => relation.build());
606 10
        entityMetadata.relations = entityMetadata.embeddeds.reduce((relations, embedded) => relations.concat(embedded.relationsFromTree), entityMetadata.ownRelations);
607 10
        entityMetadata.eagerRelations = entityMetadata.relations.filter(relation => relation.isEager);
608 10
        entityMetadata.lazyRelations = entityMetadata.relations.filter(relation => relation.isLazy);
609 10
        entityMetadata.oneToOneRelations = entityMetadata.relations.filter(relation => relation.isOneToOne);
610 10
        entityMetadata.oneToManyRelations = entityMetadata.relations.filter(relation => relation.isOneToMany);
611 10
        entityMetadata.manyToOneRelations = entityMetadata.relations.filter(relation => relation.isManyToOne);
612 10
        entityMetadata.manyToManyRelations = entityMetadata.relations.filter(relation => relation.isManyToMany);
613 10
        entityMetadata.ownerOneToOneRelations = entityMetadata.relations.filter(relation => relation.isOneToOneOwner);
614 10
        entityMetadata.ownerManyToManyRelations = entityMetadata.relations.filter(relation => relation.isManyToManyOwner);
615 10
        entityMetadata.treeParentRelation = entityMetadata.relations.find(relation => relation.isTreeParent);
616 10
        entityMetadata.treeChildrenRelation = entityMetadata.relations.find(relation => relation.isTreeChildren);
617 10
        entityMetadata.columns = entityMetadata.embeddeds.reduce((columns, embedded) => columns.concat(embedded.columnsFromTree), entityMetadata.ownColumns);
618 10
        entityMetadata.listeners = entityMetadata.embeddeds.reduce((columns, embedded) => columns.concat(embedded.listenersFromTree), entityMetadata.ownListeners);
619 10
        entityMetadata.afterLoadListeners = entityMetadata.listeners.filter(listener => listener.type === EventListenerTypes.AFTER_LOAD);
620 10
        entityMetadata.afterInsertListeners = entityMetadata.listeners.filter(listener => listener.type === EventListenerTypes.AFTER_INSERT);
621 10
        entityMetadata.afterUpdateListeners = entityMetadata.listeners.filter(listener => listener.type === EventListenerTypes.AFTER_UPDATE);
622 10
        entityMetadata.afterRemoveListeners = entityMetadata.listeners.filter(listener => listener.type === EventListenerTypes.AFTER_REMOVE);
623 10
        entityMetadata.beforeInsertListeners = entityMetadata.listeners.filter(listener => listener.type === EventListenerTypes.BEFORE_INSERT);
624 10
        entityMetadata.beforeUpdateListeners = entityMetadata.listeners.filter(listener => listener.type === EventListenerTypes.BEFORE_UPDATE);
625 10
        entityMetadata.beforeRemoveListeners = entityMetadata.listeners.filter(listener => listener.type === EventListenerTypes.BEFORE_REMOVE);
626 10
        entityMetadata.indices = entityMetadata.embeddeds.reduce((columns, embedded) => columns.concat(embedded.indicesFromTree), entityMetadata.ownIndices);
627 10
        entityMetadata.uniques = entityMetadata.embeddeds.reduce((columns, embedded) => columns.concat(embedded.uniquesFromTree), entityMetadata.ownUniques);
628 10
        entityMetadata.primaryColumns = entityMetadata.columns.filter(column => column.isPrimary);
629 10
        entityMetadata.nonVirtualColumns = entityMetadata.columns.filter(column => !column.isVirtual);
630 10
        entityMetadata.ancestorColumns = entityMetadata.columns.filter(column => column.closureType === "ancestor");
631 10
        entityMetadata.descendantColumns = entityMetadata.columns.filter(column => column.closureType === "descendant");
632 10
        entityMetadata.hasMultiplePrimaryKeys = entityMetadata.primaryColumns.length > 1;
633 10
        entityMetadata.generatedColumns = entityMetadata.columns.filter(column => column.isGenerated || column.isObjectId);
634 10
        entityMetadata.hasUUIDGeneratedColumns = entityMetadata.columns.filter(column => column.isGenerated || column.generationStrategy === "uuid").length > 0;
635 10
        entityMetadata.createDateColumn = entityMetadata.columns.find(column => column.isCreateDate);
636 10
        entityMetadata.updateDateColumn = entityMetadata.columns.find(column => column.isUpdateDate);
637 10
        entityMetadata.deleteDateColumn = entityMetadata.columns.find(column => column.isDeleteDate);
638 10
        entityMetadata.versionColumn = entityMetadata.columns.find(column => column.isVersion);
639 10
        entityMetadata.discriminatorColumn = entityMetadata.columns.find(column => column.isDiscriminator);
640 10
        entityMetadata.treeLevelColumn = entityMetadata.columns.find(column => column.isTreeLevel);
641 10
        entityMetadata.nestedSetLeftColumn = entityMetadata.columns.find(column => column.isNestedSetLeft);
642 10
        entityMetadata.nestedSetRightColumn = entityMetadata.columns.find(column => column.isNestedSetRight);
643 10
        entityMetadata.materializedPathColumn = entityMetadata.columns.find(column => column.isMaterializedPath);
644 10
        entityMetadata.objectIdColumn = entityMetadata.columns.find(column => column.isObjectId);
645 10
        entityMetadata.foreignKeys.forEach(foreignKey => foreignKey.build(this.connection.namingStrategy));
646 10
        entityMetadata.propertiesMap = entityMetadata.createPropertiesMap();
647 10
        entityMetadata.relationIds.forEach(relationId => relationId.build());
648 10
        entityMetadata.relationCounts.forEach(relationCount => relationCount.build());
649 10
        entityMetadata.embeddeds.forEach(embedded => {
650 10
            embedded.relationIdsFromTree.forEach(relationId => relationId.build());
651 10
            embedded.relationCountsFromTree.forEach(relationCount => relationCount.build());
652
        });
653
    }
654

655
    /**
656
     * Computes entity metadata's relations inverse side properties.
657
     */
658 10
    protected computeInverseProperties(entityMetadata: EntityMetadata, entityMetadatas: EntityMetadata[]) {
659 10
        entityMetadata.relations.forEach(relation => {
660

661
            // compute inverse side (related) entity metadatas for all relation metadatas
662 10
            const inverseEntityMetadata = entityMetadatas.find(m => m.target === relation.type || (typeof relation.type === "string" && m.targetName === relation.type));
663 10
            if (!inverseEntityMetadata)
664 0
                throw new Error("Entity metadata for " + entityMetadata.name + "#" + relation.propertyPath + " was not found. Check if you specified a correct entity object and if it's connected in the connection options.");
665

666 10
            relation.inverseEntityMetadata = inverseEntityMetadata;
667 10
            relation.inverseSidePropertyPath = relation.buildInverseSidePropertyPath();
668

669
            // and compute inverse relation and mark if it has such
670 10
            relation.inverseRelation = inverseEntityMetadata.relations.find(foundRelation => foundRelation.propertyPath === relation.inverseSidePropertyPath);
671
        });
672
    }
673

674
    /**
675
     * Creates indices for the table of single table inheritance.
676
     */
677 10
    protected createKeysForTableInheritance(entityMetadata: EntityMetadata) {
678 10
        entityMetadata.indices.push(
679
            new IndexMetadata({
680
                entityMetadata: entityMetadata,
681
                columns: [entityMetadata.discriminatorColumn!],
682
                args: {
683
                    target: entityMetadata.target,
684
                    unique: false
685
                }
686
            }),
687
        );
688
    }
689

690 10
}

Read our documentation on viewing source code .

Loading