• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

typeorm / typeorm / 16405830134

21 Jul 2025 12:22AM UTC coverage: 76.442% (+0.002%) from 76.44%
16405830134

Pull #11576

github

web-flow
Merge ccddce2d3 into d57fe3bd8
Pull Request #11576: fix(sqlserver): queries returning multiple record sets

9324 of 12885 branches covered (72.36%)

Branch coverage included in aggregate %.

55 of 78 new or added lines in 25 files covered. (70.51%)

2073 existing lines in 24 files now uncovered.

19016 of 24189 relevant lines covered (78.61%)

119538.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.22
/src/data-source/DataSource.ts
1
import { Driver } from "../driver/Driver"
2
import { registerQueryBuilders } from "../query-builder"
24✔
3
import { Repository } from "../repository/Repository"
4
import { EntitySubscriberInterface } from "../subscriber/EntitySubscriberInterface"
5
import { EntityTarget } from "../common/EntityTarget"
6
import { ObjectType } from "../common/ObjectType"
7
import { EntityManager } from "../entity-manager/EntityManager"
8
import { DefaultNamingStrategy } from "../naming-strategy/DefaultNamingStrategy"
24✔
9
import {
24✔
10
    CannotConnectAlreadyConnectedError,
11
    CannotExecuteNotConnectedError,
12
    EntityMetadataNotFoundError,
13
    QueryRunnerProviderAlreadyReleasedError,
14
    TypeORMError,
15
} from "../error"
16
import { TreeRepository } from "../repository/TreeRepository"
17
import { NamingStrategyInterface } from "../naming-strategy/NamingStrategyInterface"
18
import { EntityMetadata } from "../metadata/EntityMetadata"
19
import { Logger } from "../logger/Logger"
20
import { MigrationInterface } from "../migration/MigrationInterface"
21
import { MigrationExecutor } from "../migration/MigrationExecutor"
24✔
22
import { Migration } from "../migration/Migration"
23
import { MongoRepository } from "../repository/MongoRepository"
24
import { MongoEntityManager } from "../entity-manager/MongoEntityManager"
25
import { EntityMetadataValidator } from "../metadata-builder/EntityMetadataValidator"
24✔
26
import { DataSourceOptions } from "./DataSourceOptions"
27
import { EntityManagerFactory } from "../entity-manager/EntityManagerFactory"
24✔
28
import { DriverFactory } from "../driver/DriverFactory"
24✔
29
import { ConnectionMetadataBuilder } from "../connection/ConnectionMetadataBuilder"
24✔
30
import { QueryRunner } from "../query-runner/QueryRunner"
31
import { SelectQueryBuilder } from "../query-builder/SelectQueryBuilder"
24✔
32
import { LoggerFactory } from "../logger/LoggerFactory"
24✔
33
import { QueryResultCacheFactory } from "../cache/QueryResultCacheFactory"
24✔
34
import { QueryResultCache } from "../cache/QueryResultCache"
35
import { SqljsEntityManager } from "../entity-manager/SqljsEntityManager"
36
import { RelationLoader } from "../query-builder/RelationLoader"
24✔
37
import { ObjectUtils } from "../util/ObjectUtils"
24✔
38
import { IsolationLevel } from "../driver/types/IsolationLevel"
39
import { ReplicationMode } from "../driver/types/ReplicationMode"
40
import { RelationIdLoader } from "../query-builder/RelationIdLoader"
24✔
41
import { DriverUtils } from "../driver/DriverUtils"
24✔
42
import { InstanceChecker } from "../util/InstanceChecker"
24✔
43
import { ObjectLiteral } from "../common/ObjectLiteral"
44
import { buildSqlTag } from "../util/SqlTagUtils"
24✔
45

46
registerQueryBuilders()
24✔
47

48
/**
49
 * DataSource is a pre-defined connection configuration to a specific database.
50
 * You can have multiple data sources connected (with multiple connections in it),
51
 * connected to multiple databases in your application.
52
 *
53
 * Before, it was called `Connection`, but now `Connection` is deprecated
54
 * because `Connection` isn't the best name for what it's actually is.
55
 */
56
export class DataSource {
24✔
57
    readonly "@instanceof" = Symbol.for("DataSource")
12,218✔
58

59
    // -------------------------------------------------------------------------
60
    // Public Readonly Properties
61
    // -------------------------------------------------------------------------
62

63
    /**
64
     * Connection name.
65
     *
66
     * @deprecated we don't need names anymore since we are going to drop all related methods relying on this property.
67
     */
68
    readonly name: string
69

70
    /**
71
     * Connection options.
72
     */
73
    readonly options: DataSourceOptions
74

75
    /**
76
     * Indicates if DataSource is initialized or not.
77
     */
78
    readonly isInitialized: boolean
79

80
    /**
81
     * Database driver used by this connection.
82
     */
83
    driver: Driver
84

85
    /**
86
     * EntityManager of this connection.
87
     */
88
    readonly manager: EntityManager
89

90
    /**
91
     * Naming strategy used in the connection.
92
     */
93
    namingStrategy: NamingStrategyInterface
94

95
    /**
96
     * Name for the metadata table
97
     */
98
    readonly metadataTableName: string
99

100
    /**
101
     * Logger used to log orm events.
102
     */
103
    logger: Logger
104

105
    /**
106
     * Migration instances that are registered for this connection.
107
     */
108
    readonly migrations: MigrationInterface[] = []
12,218✔
109

110
    /**
111
     * Entity subscriber instances that are registered for this connection.
112
     */
113
    readonly subscribers: EntitySubscriberInterface<any>[] = []
12,218✔
114

115
    /**
116
     * All entity metadatas that are registered for this connection.
117
     */
118
    readonly entityMetadatas: EntityMetadata[] = []
12,218✔
119

120
    /**
121
     * All entity metadatas that are registered for this connection.
122
     * This is a copy of #.entityMetadatas property -> used for more performant searches.
123
     */
124
    readonly entityMetadatasMap = new Map<EntityTarget<any>, EntityMetadata>()
12,218✔
125

126
    /**
127
     * Used to work with query result cache.
128
     */
129
    queryResultCache?: QueryResultCache
130

131
    /**
132
     * Used to load relations and work with lazy relations.
133
     */
134
    readonly relationLoader: RelationLoader
135

136
    readonly relationIdLoader: RelationIdLoader
137

138
    // -------------------------------------------------------------------------
139
    // Constructor
140
    // -------------------------------------------------------------------------
141

142
    constructor(options: DataSourceOptions) {
143
        registerQueryBuilders()
12,218✔
144
        this.name = options.name || "default"
12,218✔
145
        this.options = options
12,218✔
146
        this.logger = new LoggerFactory().create(
12,218✔
147
            this.options.logger,
148
            this.options.logging,
149
        )
150
        this.driver = new DriverFactory().create(this)
12,218✔
151
        this.manager = this.createEntityManager()
12,218✔
152
        this.namingStrategy =
12,218✔
153
            options.namingStrategy || new DefaultNamingStrategy()
24,362✔
154
        this.metadataTableName = options.metadataTableName || "typeorm_metadata"
12,218✔
155
        this.queryResultCache = options.cache
12,218✔
156
            ? new QueryResultCacheFactory(this).create()
157
            : undefined
158
        this.relationLoader = new RelationLoader(this)
12,218✔
159
        this.relationIdLoader = new RelationIdLoader(this)
12,218✔
160
        this.isInitialized = false
12,218✔
161
    }
162

163
    // -------------------------------------------------------------------------
164
    // Public Accessors
165
    // -------------------------------------------------------------------------
166

167
    /**
168
     Indicates if DataSource is initialized or not.
169
     *
170
     * @deprecated use .isInitialized instead
171
     */
172
    get isConnected() {
UNCOV
173
        return this.isInitialized
×
174
    }
175

176
    /**
177
     * Gets the mongodb entity manager that allows to perform mongodb-specific repository operations
178
     * with any entity in this connection.
179
     *
180
     * Available only in mongodb connections.
181
     */
182
    get mongoManager(): MongoEntityManager {
183
        if (!InstanceChecker.isMongoEntityManager(this.manager))
10!
UNCOV
184
            throw new TypeORMError(
×
185
                `MongoEntityManager is only available for MongoDB databases.`,
186
            )
187

188
        return this.manager as MongoEntityManager
10✔
189
    }
190

191
    /**
192
     * Gets a sql.js specific Entity Manager that allows to perform special load and save operations
193
     *
194
     * Available only in connection with the sqljs driver.
195
     */
196
    get sqljsManager(): SqljsEntityManager {
197
        if (!InstanceChecker.isSqljsEntityManager(this.manager))
15!
UNCOV
198
            throw new TypeORMError(
×
199
                `SqljsEntityManager is only available for Sqljs databases.`,
200
            )
201

202
        return this.manager
15✔
203
    }
204

205
    // -------------------------------------------------------------------------
206
    // Public Methods
207
    // -------------------------------------------------------------------------
208
    /**
209
     * Updates current connection options with provided options.
210
     */
211
    setOptions(options: Partial<DataSourceOptions>): this {
212
        Object.assign(this.options, options)
65✔
213

214
        if (options.logger || options.logging) {
65!
UNCOV
215
            this.logger = new LoggerFactory().create(
×
216
                options.logger || this.options.logger,
×
217
                options.logging || this.options.logging,
×
218
            )
219
        }
220

221
        if (options.namingStrategy) {
65!
UNCOV
222
            this.namingStrategy = options.namingStrategy
×
223
        }
224

225
        if (options.cache) {
65!
UNCOV
226
            this.queryResultCache = new QueryResultCacheFactory(this).create()
×
227
        }
228

229
        // todo: we must update the database in the driver as well, if it was set by setOptions method
230
        //  in the future we need to refactor the code and remove "database" from the driver, and instead
231
        //  use database (and options) from a single place - data source.
232
        if (options.database) {
65✔
233
            this.driver.database = DriverUtils.buildDriverOptions(
8✔
234
                this.options,
235
            ).database
236
        }
237

238
        // todo: need to take a look if we need to update schema and other "poor" properties
239

240
        return this
65✔
241
    }
242

243
    /**
244
     * Performs connection to the database.
245
     * This method should be called once on application bootstrap.
246
     * This method not necessarily creates database connection (depend on database type),
247
     * but it also can setup a connection pool with database to use.
248
     */
249
    async initialize(): Promise<this> {
250
        if (this.isInitialized)
12,068✔
251
            throw new CannotConnectAlreadyConnectedError(this.name)
24✔
252

253
        // connect to the database via its driver
254
        await this.driver.connect()
12,044✔
255

256
        // connect to the cache-specific database if cache is enabled
257
        if (this.queryResultCache) await this.queryResultCache.connect()
12,044✔
258

259
        // set connected status for the current connection
260
        ObjectUtils.assign(this, { isInitialized: true })
12,044✔
261

262
        try {
12,044✔
263
            // build all metadatas registered in the current connection
264
            await this.buildMetadatas()
12,044✔
265

266
            await this.driver.afterConnect()
11,994✔
267

268
            // if option is set - drop schema once connection is done
269
            if (this.options.dropSchema) await this.dropDatabase()
11,994✔
270

271
            // if option is set - automatically synchronize a schema
272
            if (this.options.migrationsRun)
11,994!
UNCOV
273
                await this.runMigrations({
×
274
                    transaction: this.options.migrationsTransactionMode,
275
                })
276

277
            // if option is set - automatically synchronize a schema
278
            if (this.options.synchronize) await this.synchronize()
11,994✔
279
        } catch (error) {
280
            // if for some reason build metadata fail (for example validation error during entity metadata check)
281
            // connection needs to be closed
282
            await this.destroy()
50✔
283
            throw error
50✔
284
        }
285

286
        return this
11,994✔
287
    }
288

289
    /**
290
     * Performs connection to the database.
291
     * This method should be called once on application bootstrap.
292
     * This method not necessarily creates database connection (depend on database type),
293
     * but it also can setup a connection pool with database to use.
294
     *
295
     * @deprecated use .initialize method instead
296
     */
297
    async connect(): Promise<this> {
298
        return this.initialize()
10✔
299
    }
300

301
    /**
302
     * Closes connection with the database.
303
     * Once connection is closed, you cannot use repositories or perform any operations except opening connection again.
304
     */
305
    async destroy(): Promise<void> {
306
        if (!this.isInitialized)
12,064✔
307
            throw new CannotExecuteNotConnectedError(this.name)
26✔
308

309
        await this.driver.disconnect()
12,038✔
310

311
        // disconnect from the cache-specific database if cache was enabled
312
        if (this.queryResultCache) await this.queryResultCache.disconnect()
12,038✔
313

314
        ObjectUtils.assign(this, { isInitialized: false })
12,038✔
315
    }
316

317
    /**
318
     * Closes connection with the database.
319
     * Once connection is closed, you cannot use repositories or perform any operations except opening connection again.
320
     *
321
     * @deprecated use .destroy method instead
322
     */
323
    async close(): Promise<void> {
324
        return this.destroy()
33✔
325
    }
326

327
    /**
328
     * Creates database schema for all entities registered in this connection.
329
     * Can be used only after connection to the database is established.
330
     *
331
     * @param dropBeforeSync If set to true then it drops the database with all its tables and data
332
     */
333
    async synchronize(dropBeforeSync: boolean = false): Promise<void> {
4,473✔
334
        if (!this.isInitialized)
33,777✔
335
            throw new CannotExecuteNotConnectedError(this.name)
2✔
336

337
        if (dropBeforeSync) await this.dropDatabase()
33,775✔
338

339
        const schemaBuilder = this.driver.createSchemaBuilder()
33,775✔
340
        await schemaBuilder.build()
33,775✔
341
    }
342

343
    /**
344
     * Drops the database and all its data.
345
     * Be careful with this method on production since this method will erase all your database tables and their data.
346
     * Can be used only after connection to the database is established.
347
     */
348
    // TODO rename
349
    async dropDatabase(): Promise<void> {
350
        const queryRunner = this.createQueryRunner()
33,268✔
351
        try {
33,268✔
352
            if (
33,268✔
353
                this.driver.options.type === "mssql" ||
113,682✔
354
                DriverUtils.isMySQLFamily(this.driver) ||
355
                this.driver.options.type === "aurora-mysql" ||
356
                DriverUtils.isSQLiteFamily(this.driver)
357
            ) {
358
                const databases: string[] = []
19,961✔
359
                this.entityMetadatas.forEach((metadata) => {
19,961✔
360
                    if (
70,056✔
361
                        metadata.database &&
70,152✔
362
                        databases.indexOf(metadata.database) === -1
363
                    )
364
                        databases.push(metadata.database)
65✔
365
                })
366
                if (databases.length === 0 && this.driver.database) {
19,961✔
367
                    databases.push(this.driver.database)
16,282✔
368
                }
369

370
                if (databases.length === 0) {
19,961✔
371
                    await queryRunner.clearDatabase()
3,636✔
372
                } else {
373
                    for (const database of databases) {
16,325✔
374
                        await queryRunner.clearDatabase(database)
16,347✔
375
                    }
376
                }
377
            } else {
378
                await queryRunner.clearDatabase()
13,307✔
379
            }
380
        } finally {
381
            await queryRunner.release()
33,268✔
382
        }
383
    }
384

385
    /**
386
     * Runs all pending migrations.
387
     * Can be used only after connection to the database is established.
388
     */
389
    async runMigrations(options?: {
390
        transaction?: "all" | "none" | "each"
391
        fake?: boolean
392
    }): Promise<Migration[]> {
393
        if (!this.isInitialized)
187!
UNCOV
394
            throw new CannotExecuteNotConnectedError(this.name)
×
395

396
        const migrationExecutor = new MigrationExecutor(this)
187✔
397
        migrationExecutor.transaction =
187✔
398
            options?.transaction ||
353✔
399
            this.options?.migrationsTransactionMode ||
400
            "all"
401
        migrationExecutor.fake = (options && options.fake) || false
187✔
402

403
        const successMigrations =
404
            await migrationExecutor.executePendingMigrations()
187✔
405
        return successMigrations
127✔
406
    }
407

408
    /**
409
     * Reverts last executed migration.
410
     * Can be used only after connection to the database is established.
411
     */
412
    async undoLastMigration(options?: {
413
        transaction?: "all" | "none" | "each"
414
        fake?: boolean
415
    }): Promise<void> {
416
        if (!this.isInitialized)
50!
UNCOV
417
            throw new CannotExecuteNotConnectedError(this.name)
×
418

419
        const migrationExecutor = new MigrationExecutor(this)
50✔
420
        migrationExecutor.transaction =
50✔
421
            (options && options.transaction) || "all"
100✔
422
        migrationExecutor.fake = (options && options.fake) || false
50✔
423

424
        await migrationExecutor.undoLastMigration()
50✔
425
    }
426

427
    /**
428
     * Lists all migrations and whether they have been run.
429
     * Returns true if there are pending migrations
430
     */
431
    async showMigrations(): Promise<boolean> {
432
        if (!this.isInitialized) {
50!
UNCOV
433
            throw new CannotExecuteNotConnectedError(this.name)
×
434
        }
435
        const migrationExecutor = new MigrationExecutor(this)
50✔
436
        return await migrationExecutor.showMigrations()
50✔
437
    }
438

439
    /**
440
     * Checks if entity metadata exist for the given entity class, target name or table name.
441
     */
442
    hasMetadata(target: EntityTarget<any>): boolean {
443
        return !!this.findMetadata(target)
812,727✔
444
    }
445

446
    /**
447
     * Gets entity metadata for the given entity class or schema name.
448
     */
449
    getMetadata(target: EntityTarget<any>): EntityMetadata {
450
        const metadata = this.findMetadata(target)
3,944,065✔
451
        if (!metadata) throw new EntityMetadataNotFoundError(target)
3,944,065!
452

453
        return metadata
3,944,065✔
454
    }
455

456
    /**
457
     * Gets repository for the given entity.
458
     */
459
    getRepository<Entity extends ObjectLiteral>(
460
        target: EntityTarget<Entity>,
461
    ): Repository<Entity> {
462
        return this.manager.getRepository(target)
262,353✔
463
    }
464

465
    /**
466
     * Gets tree repository for the given entity class or name.
467
     * Only tree-type entities can have a TreeRepository, like ones decorated with @Tree decorator.
468
     */
469
    getTreeRepository<Entity extends ObjectLiteral>(
470
        target: EntityTarget<Entity>,
471
    ): TreeRepository<Entity> {
472
        return this.manager.getTreeRepository(target)
2,398✔
473
    }
474

475
    /**
476
     * Gets mongodb-specific repository for the given entity class or name.
477
     * Works only if connection is mongodb-specific.
478
     */
479
    getMongoRepository<Entity extends ObjectLiteral>(
480
        target: EntityTarget<Entity>,
481
    ): MongoRepository<Entity> {
482
        if (!(this.driver.options.type === "mongodb"))
88!
UNCOV
483
            throw new TypeORMError(
×
484
                `You can use getMongoRepository only for MongoDB connections.`,
485
            )
486

487
        return this.manager.getRepository(target) as any
88✔
488
    }
489

490
    /**
491
     * Gets custom entity repository marked with @EntityRepository decorator.
492
     *
493
     * @deprecated use Repository.extend function to create a custom repository
494
     */
495
    getCustomRepository<T>(customRepository: ObjectType<T>): T {
UNCOV
496
        return this.manager.getCustomRepository(customRepository)
×
497
    }
498

499
    /**
500
     * Wraps given function execution (and all operations made there) into a transaction.
501
     * All database operations must be executed using provided entity manager.
502
     */
503
    async transaction<T>(
504
        runInTransaction: (entityManager: EntityManager) => Promise<T>,
505
    ): Promise<T>
506
    async transaction<T>(
507
        isolationLevel: IsolationLevel,
508
        runInTransaction: (entityManager: EntityManager) => Promise<T>,
509
    ): Promise<T>
510
    async transaction<T>(
511
        isolationOrRunInTransaction:
512
            | IsolationLevel
513
            | ((entityManager: EntityManager) => Promise<T>),
514
        runInTransactionParam?: (entityManager: EntityManager) => Promise<T>,
515
    ): Promise<any> {
516
        return this.manager.transaction(
112✔
517
            isolationOrRunInTransaction as any,
518
            runInTransactionParam as any,
519
        )
520
    }
521

522
    /**
523
     * Executes raw SQL query and returns raw database results.
524
     *
525
     * @see [Official docs](https://typeorm.io/data-source-api) for examples.
526
     */
527
    async query<T = any>(
528
        query: string,
529
        parameters?: any[],
530
        queryRunner?: QueryRunner,
531
        useStructuredResult?: boolean,
532
    ): Promise<T> {
533
        if (InstanceChecker.isMongoEntityManager(this.manager))
1,232!
UNCOV
534
            throw new TypeORMError(`Queries aren't supported by MongoDB.`)
×
535

536
        if (queryRunner && queryRunner.isReleased)
1,232!
NEW
537
            throw new QueryRunnerProviderAlreadyReleasedError()
×
538

539
        const usedQueryRunner = queryRunner || this.createQueryRunner()
1,232✔
540

541
        try {
1,232✔
542
            return (await usedQueryRunner.query(
1,232✔
543
                query,
544
                parameters,
545
                (useStructuredResult || false) as any,
2,392✔
546
            )) as T // await is needed here because we are using finally
547
        } finally {
548
            if (!queryRunner) await usedQueryRunner.release()
1,232✔
549
        }
550
    }
551

552
    /**
553
     * Tagged template function that executes raw SQL query and returns raw database results.
554
     * Template expressions are automatically transformed into database parameters.
555
     * Raw query execution is supported only by relational databases (MongoDB is not supported).
556
     * Note: Don't call this as a regular function, it is meant to be used with backticks to tag a template literal.
557
     * Example: dataSource.sql`SELECT * FROM table_name WHERE id = ${id}`
558
     */
559
    async sql<T = any>(
560
        strings: TemplateStringsArray,
561
        ...values: unknown[]
562
    ): Promise<T> {
563
        const { query, parameters } = buildSqlTag({
100✔
564
            driver: this.driver,
565
            strings: strings,
566
            expressions: values,
567
        })
568

569
        return await this.query(query, parameters)
100✔
570
    }
571

572
    /**
573
     * Creates a new query builder that can be used to build a SQL query.
574
     */
575
    createQueryBuilder<Entity extends ObjectLiteral>(
576
        entityClass: EntityTarget<Entity>,
577
        alias: string,
578
        queryRunner?: QueryRunner,
579
    ): SelectQueryBuilder<Entity>
580

581
    /**
582
     * Creates a new query builder that can be used to build a SQL query.
583
     */
584
    createQueryBuilder(queryRunner?: QueryRunner): SelectQueryBuilder<any>
585

586
    /**
587
     * Creates a new query builder that can be used to build a SQL query.
588
     */
589
    createQueryBuilder<Entity extends ObjectLiteral>(
590
        entityOrRunner?: EntityTarget<Entity> | QueryRunner,
591
        alias?: string,
592
        queryRunner?: QueryRunner,
593
    ): SelectQueryBuilder<Entity> {
594
        if (InstanceChecker.isMongoEntityManager(this.manager))
673,943!
UNCOV
595
            throw new TypeORMError(`Query Builder is not supported by MongoDB.`)
×
596

597
        if (alias) {
673,943✔
598
            alias = DriverUtils.buildAlias(this.driver, undefined, alias)
422,721✔
599
            const metadata = this.getMetadata(
422,721✔
600
                entityOrRunner as EntityTarget<Entity>,
601
            )
602
            return new SelectQueryBuilder(this, queryRunner)
422,721✔
603
                .select(alias)
604
                .from(metadata.target, alias)
605
        } else {
606
            return new SelectQueryBuilder(
251,222✔
607
                this,
608
                entityOrRunner as QueryRunner | undefined,
609
            )
610
        }
611
    }
612

613
    /**
614
     * Creates a query runner used for perform queries on a single database connection.
615
     * Using query runners you can control your queries to execute using single database connection and
616
     * manually control your database transaction.
617
     *
618
     * Mode is used in replication mode and indicates whatever you want to connect
619
     * to master database or any of slave databases.
620
     * If you perform writes you must use master database,
621
     * if you perform reads you can use slave databases.
622
     */
623
    createQueryRunner(mode: ReplicationMode = "master"): QueryRunner {
244,365✔
624
        const queryRunner = this.driver.createQueryRunner(mode)
285,049✔
625
        const manager = this.createEntityManager(queryRunner)
285,049✔
626
        Object.assign(queryRunner, { manager: manager })
285,049✔
627
        return queryRunner
285,049✔
628
    }
629

630
    /**
631
     * Gets entity metadata of the junction table (many-to-many table).
632
     */
633
    getManyToManyMetadata(
634
        entityTarget: EntityTarget<any>,
635
        relationPropertyPath: string,
636
    ) {
637
        const relationMetadata =
638
            this.getMetadata(entityTarget).findRelationWithPropertyPath(
32✔
639
                relationPropertyPath,
640
            )
641
        if (!relationMetadata)
32!
UNCOV
642
            throw new TypeORMError(
×
643
                `Relation "${relationPropertyPath}" was not found in ${entityTarget} entity.`,
644
            )
645
        if (!relationMetadata.isManyToMany)
32!
UNCOV
646
            throw new TypeORMError(
×
647
                `Relation "${entityTarget}#${relationPropertyPath}" does not have a many-to-many relationship.` +
648
                    `You can use this method only on many-to-many relations.`,
649
            )
650

651
        return relationMetadata.junctionEntityMetadata
32✔
652
    }
653

654
    /**
655
     * Creates an Entity Manager for the current connection with the help of the EntityManagerFactory.
656
     */
657
    createEntityManager(queryRunner?: QueryRunner): EntityManager {
658
        return new EntityManagerFactory().create(this, queryRunner)
297,471✔
659
    }
660

661
    // -------------------------------------------------------------------------
662
    // Protected Methods
663
    // -------------------------------------------------------------------------
664

665
    /**
666
     * Finds exist entity metadata by the given entity class, target name or table name.
667
     */
668
    protected findMetadata(
669
        target: EntityTarget<any>,
670
    ): EntityMetadata | undefined {
671
        const metadataFromMap = this.entityMetadatasMap.get(target)
4,756,792✔
672
        if (metadataFromMap) return metadataFromMap
4,756,792✔
673

674
        for (const [_, metadata] of this.entityMetadatasMap) {
111,196✔
675
            if (
249,239✔
676
                InstanceChecker.isEntitySchema(target) &&
250,226✔
677
                metadata.name === target.options.name
678
            ) {
679
                return metadata
700✔
680
            }
681
            if (typeof target === "string") {
248,539✔
682
                if (target.indexOf(".") !== -1) {
241,742✔
683
                    if (metadata.tablePath === target) {
25,885✔
684
                        return metadata
336✔
685
                    }
686
                } else {
687
                    if (
215,857✔
688
                        metadata.name === target ||
395,382✔
689
                        metadata.tableName === target
690
                    ) {
691
                        return metadata
95,434✔
692
                    }
693
                }
694
            }
695
            if (
152,769!
696
                ObjectUtils.isObjectWithName(target) &&
152,769!
697
                typeof target.name === "string"
698
            ) {
UNCOV
699
                if (target.name.indexOf(".") !== -1) {
×
UNCOV
700
                    if (metadata.tablePath === target.name) {
×
UNCOV
701
                        return metadata
×
702
                    }
703
                } else {
UNCOV
704
                    if (
×
705
                        metadata.name === target.name ||
×
706
                        metadata.tableName === target.name
707
                    ) {
UNCOV
708
                        return metadata
×
709
                    }
710
                }
711
            }
712
        }
713

714
        return undefined
14,726✔
715
    }
716

717
    /**
718
     * Builds metadatas for all registered classes inside this connection.
719
     */
720
    protected async buildMetadatas(): Promise<void> {
721
        const connectionMetadataBuilder = new ConnectionMetadataBuilder(this)
12,044✔
722
        const entityMetadataValidator = new EntityMetadataValidator()
12,044✔
723

724
        // create subscribers instances if they are not disallowed from high-level (for example they can disallowed from migrations run process)
725
        const flattenedSubscribers = ObjectUtils.mixedListToArray(
12,044✔
726
            this.options.subscribers || [],
12,054✔
727
        )
728
        const subscribers = await connectionMetadataBuilder.buildSubscribers(
12,044✔
729
            flattenedSubscribers,
730
        )
731
        ObjectUtils.assign(this, { subscribers: subscribers })
12,044✔
732

733
        // build entity metadatas
734
        const flattenedEntities = ObjectUtils.mixedListToArray(
12,044✔
735
            this.options.entities || [],
12,050✔
736
        )
737
        const entityMetadatas =
738
            await connectionMetadataBuilder.buildEntityMetadatas(
12,044✔
739
                flattenedEntities,
740
            )
741
        ObjectUtils.assign(this, {
12,040✔
742
            entityMetadatas: entityMetadatas,
743
            entityMetadatasMap: new Map(
744
                entityMetadatas.map((metadata) => [metadata.target, metadata]),
30,473✔
745
            ),
746
        })
747

748
        // create migration instances
749
        const flattenedMigrations = ObjectUtils.mixedListToArray(
12,040✔
750
            this.options.migrations || [],
12,050✔
751
        )
752
        const migrations = await connectionMetadataBuilder.buildMigrations(
12,040✔
753
            flattenedMigrations,
754
        )
755
        ObjectUtils.assign(this, { migrations: migrations })
12,040✔
756

757
        // validate all created entity metadatas to make sure user created entities are valid and correct
758
        entityMetadataValidator.validateMany(
12,040✔
759
            this.entityMetadatas.filter(
760
                (metadata) => metadata.tableType !== "view",
30,473✔
761
            ),
762
            this.driver,
763
        )
764

765
        // set current data source to the entities
766
        for (const entityMetadata of entityMetadatas) {
11,994✔
767
            if (
30,359✔
768
                InstanceChecker.isBaseEntityConstructor(entityMetadata.target)
769
            ) {
770
                entityMetadata.target.useDataSource(this)
1,012✔
771
            }
772
        }
773
    }
774

775
    /**
776
     * Get the replication mode SELECT queries should use for this datasource by default
777
     */
778
    defaultReplicationModeForReads(): ReplicationMode {
779
        if (
40,446✔
780
            "replication" in this.driver.options &&
40,462✔
781
            this.driver.options.replication
782
        ) {
783
            const value = (
784
                this.driver.options.replication as {
8✔
785
                    defaultMode?: ReplicationMode
786
                }
787
            ).defaultMode
788
            if (value) {
8✔
789
                return value
4✔
790
            }
791
        }
792
        return "slave"
40,442✔
793
    }
794
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc