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

typeorm / typeorm / 13684615749

05 Mar 2025 08:09PM UTC coverage: 72.349% (-0.003%) from 72.352%
13684615749

push

github

web-flow
chore: don't use version in docker-compose files (#11320)

8653 of 12650 branches covered (68.4%)

Branch coverage included in aggregate %.

0 of 8 new or added lines in 1 file covered. (0.0%)

2 existing lines in 2 files now uncovered.

17876 of 24018 relevant lines covered (74.43%)

147954.48 hits per line

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

87.65
/src/driver/sqlserver/SqlServerQueryRunner.ts
1
import { ObjectLiteral } from "../../common/ObjectLiteral"
2
import { QueryResult } from "../../query-runner/QueryResult"
32✔
3
import { QueryFailedError } from "../../error/QueryFailedError"
32✔
4
import { QueryRunnerAlreadyReleasedError } from "../../error/QueryRunnerAlreadyReleasedError"
32✔
5
import { TransactionNotStartedError } from "../../error/TransactionNotStartedError"
32✔
6
import { ColumnType } from "../types/ColumnTypes"
7
import { ReadStream } from "../../platform/PlatformTools"
8
import { BaseQueryRunner } from "../../query-runner/BaseQueryRunner"
32✔
9
import { QueryRunner } from "../../query-runner/QueryRunner"
10
import { TableIndexOptions } from "../../schema-builder/options/TableIndexOptions"
11
import { Table } from "../../schema-builder/table/Table"
32✔
12
import { TableCheck } from "../../schema-builder/table/TableCheck"
32✔
13
import { TableColumn } from "../../schema-builder/table/TableColumn"
32✔
14
import { TableExclusion } from "../../schema-builder/table/TableExclusion"
15
import { TableForeignKey } from "../../schema-builder/table/TableForeignKey"
32✔
16
import { TableIndex } from "../../schema-builder/table/TableIndex"
32✔
17
import { TableUnique } from "../../schema-builder/table/TableUnique"
32✔
18
import { View } from "../../schema-builder/view/View"
32✔
19
import { Broadcaster } from "../../subscriber/Broadcaster"
32✔
20
import { OrmUtils } from "../../util/OrmUtils"
32✔
21
import { Query } from "../Query"
32✔
22
import { IsolationLevel } from "../types/IsolationLevel"
23
import { MssqlParameter } from "./MssqlParameter"
24
import { SqlServerDriver } from "./SqlServerDriver"
25
import { ReplicationMode } from "../types/ReplicationMode"
26
import { TypeORMError } from "../../error"
32✔
27
import { QueryLock } from "../../query-runner/QueryLock"
32✔
28
import { MetadataTableType } from "../types/MetadataTableType"
32✔
29
import { InstanceChecker } from "../../util/InstanceChecker"
32✔
30
import { BroadcasterResult } from "../../subscriber/BroadcasterResult"
32✔
31

32
/**
33
 * Runs queries on a single SQL Server database connection.
34
 */
35
export class SqlServerQueryRunner
32✔
36
    extends BaseQueryRunner
37
    implements QueryRunner
38
{
39
    // -------------------------------------------------------------------------
40
    // Public Implemented Properties
41
    // -------------------------------------------------------------------------
42

43
    /**
44
     * Database driver used by connection.
45
     */
46
    driver: SqlServerDriver
47

48
    // -------------------------------------------------------------------------
49
    // Private Properties
50
    // -------------------------------------------------------------------------
51

52
    private lock: QueryLock = new QueryLock()
105,831✔
53

54
    // -------------------------------------------------------------------------
55
    // Constructor
56
    // -------------------------------------------------------------------------
57

58
    constructor(driver: SqlServerDriver, mode: ReplicationMode) {
59
        super()
105,831✔
60
        this.driver = driver
105,831✔
61
        this.connection = driver.connection
105,831✔
62
        this.broadcaster = new Broadcaster(this)
105,831✔
63
        this.mode = mode
105,831✔
64
    }
65

66
    // -------------------------------------------------------------------------
67
    // Public Methods
68
    // -------------------------------------------------------------------------
69

70
    /**
71
     * Creates/uses database connection from the connection pool to perform further operations.
72
     * Returns obtained database connection.
73
     */
74
    connect(): Promise<void> {
75
        return Promise.resolve()
9✔
76
    }
77

78
    /**
79
     * Releases used database connection.
80
     * You cannot use query runner methods once its released.
81
     */
82
    release(): Promise<void> {
83
        this.isReleased = true
105,831✔
84
        return Promise.resolve()
105,831✔
85
    }
86

87
    /**
88
     * Starts transaction.
89
     */
90
    async startTransaction(isolationLevel?: IsolationLevel): Promise<void> {
91
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
79,695!
92

93
        this.isTransactionActive = true
79,695✔
94
        try {
79,695✔
95
            await this.broadcaster.broadcast("BeforeTransactionStart")
79,695✔
96
        } catch (err) {
97
            this.isTransactionActive = false
×
98
            throw err
×
99
        }
100
        await new Promise<void>(async (ok, fail) => {
79,695✔
101
            const transactionCallback = (err: any) => {
79,695✔
102
                if (err) {
79,650!
103
                    this.isTransactionActive = false
×
104
                    return fail(err)
×
105
                }
106
                ok()
79,650✔
107
            }
108

109
            if (this.transactionDepth === 0) {
79,695✔
110
                const pool = await (this.mode === "slave"
79,650!
111
                    ? this.driver.obtainSlaveConnection()
112
                    : this.driver.obtainMasterConnection())
113
                this.databaseConnection = pool.transaction()
79,650✔
114
                this.connection.logger.logQuery("BEGIN TRANSACTION")
79,650✔
115
                if (isolationLevel) {
79,650✔
116
                    this.databaseConnection.begin(
54✔
117
                        this.convertIsolationLevel(isolationLevel),
118
                        transactionCallback,
119
                    )
120
                    this.connection.logger.logQuery(
54✔
121
                        "SET TRANSACTION ISOLATION LEVEL " + isolationLevel,
122
                    )
123
                } else {
124
                    this.databaseConnection.begin(transactionCallback)
79,596✔
125
                }
126
            } else {
127
                await this.query(
45✔
128
                    `SAVE TRANSACTION typeorm_${this.transactionDepth}`,
129
                )
130
                ok()
45✔
131
            }
132
            this.transactionDepth += 1
79,695✔
133
        })
134

135
        await this.broadcaster.broadcast("AfterTransactionStart")
79,695✔
136
    }
137

138
    /**
139
     * Commits transaction.
140
     * Error will be thrown if transaction was not started.
141
     */
142
    async commitTransaction(): Promise<void> {
143
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
79,479!
144

145
        if (!this.isTransactionActive) throw new TransactionNotStartedError()
79,479!
146

147
        await this.broadcaster.broadcast("BeforeTransactionCommit")
79,479✔
148

149
        if (this.transactionDepth === 1) {
79,479✔
150
            return new Promise<void>((ok, fail) => {
79,461✔
151
                this.databaseConnection.commit(async (err: any) => {
79,461✔
152
                    if (err) return fail(err)
79,461!
153
                    this.isTransactionActive = false
79,461✔
154
                    this.databaseConnection = null
79,461✔
155

156
                    await this.broadcaster.broadcast("AfterTransactionCommit")
79,461✔
157

158
                    ok()
79,461✔
159
                    this.connection.logger.logQuery("COMMIT")
79,461✔
160
                    this.transactionDepth -= 1
79,461✔
161
                })
162
            })
163
        }
164
        this.transactionDepth -= 1
18✔
165
    }
166

167
    /**
168
     * Rollbacks transaction.
169
     * Error will be thrown if transaction was not started.
170
     */
171
    async rollbackTransaction(): Promise<void> {
172
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
216!
173

174
        if (!this.isTransactionActive) throw new TransactionNotStartedError()
216!
175

176
        await this.broadcaster.broadcast("BeforeTransactionRollback")
216✔
177

178
        if (this.transactionDepth > 1) {
216✔
179
            await this.query(
27✔
180
                `ROLLBACK TRANSACTION typeorm_${this.transactionDepth - 1}`,
181
            )
182
            this.transactionDepth -= 1
27✔
183
        } else {
184
            return new Promise<void>((ok, fail) => {
189✔
185
                this.databaseConnection.rollback(async (err: any) => {
189✔
186
                    if (err) return fail(err)
189✔
187
                    this.isTransactionActive = false
180✔
188
                    this.databaseConnection = null
180✔
189

190
                    await this.broadcaster.broadcast("AfterTransactionRollback")
180✔
191

192
                    ok()
180✔
193
                    this.connection.logger.logQuery("ROLLBACK")
180✔
194
                    this.transactionDepth -= 1
180✔
195
                })
196
            })
197
        }
198
    }
199

200
    /**
201
     * Executes a given SQL query.
202
     */
203
    async query(
204
        query: string,
205
        parameters?: any[],
206
        useStructuredResult = false,
322,515✔
207
    ): Promise<any> {
208
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
457,776!
209

210
        const release = await this.lock.acquire()
457,776✔
211

212
        const broadcasterResult = new BroadcasterResult()
457,776✔
213

214
        try {
457,776✔
215
            this.driver.connection.logger.logQuery(query, parameters, this)
457,776✔
216
            this.broadcaster.broadcastBeforeQueryEvent(
457,776✔
217
                broadcasterResult,
218
                query,
219
                parameters,
220
            )
221

222
            const pool = await (this.mode === "slave"
457,776✔
223
                ? this.driver.obtainSlaveConnection()
224
                : this.driver.obtainMasterConnection())
225
            const request = new this.driver.mssql.Request(
457,776✔
226
                this.isTransactionActive ? this.databaseConnection : pool,
457,776✔
227
            )
228
            if (parameters && parameters.length) {
457,776✔
229
                parameters.forEach((parameter, index) => {
130,959✔
230
                    const parameterName = index.toString()
308,313✔
231
                    if (InstanceChecker.isMssqlParameter(parameter)) {
308,313✔
232
                        const mssqlParameter =
233
                            this.mssqlParameterToNativeParameter(parameter)
221,607✔
234
                        if (mssqlParameter) {
221,607✔
235
                            request.input(
221,454✔
236
                                parameterName,
237
                                mssqlParameter,
238
                                parameter.value,
239
                            )
240
                        } else {
241
                            request.input(parameterName, parameter.value)
153✔
242
                        }
243
                    } else {
244
                        request.input(parameterName, parameter)
86,706✔
245
                    }
246
                })
247
            }
248
            const queryStartTime = +new Date()
457,776✔
249

250
            const raw = await new Promise<any>((ok, fail) => {
457,776✔
251
                request.query(query, (err: any, raw: any) => {
457,776✔
252
                    // log slow queries if maxQueryExecution time is set
253
                    const maxQueryExecutionTime =
254
                        this.driver.options.maxQueryExecutionTime
457,776✔
255
                    const queryEndTime = +new Date()
457,776✔
256
                    const queryExecutionTime = queryEndTime - queryStartTime
457,776✔
257

258
                    this.broadcaster.broadcastAfterQueryEvent(
457,776✔
259
                        broadcasterResult,
260
                        query,
261
                        parameters,
262
                        true,
263
                        queryExecutionTime,
264
                        raw,
265
                        undefined,
266
                    )
267

268
                    if (
457,776!
269
                        maxQueryExecutionTime &&
457,776!
270
                        queryExecutionTime > maxQueryExecutionTime
271
                    ) {
272
                        this.driver.connection.logger.logQuerySlow(
×
273
                            queryExecutionTime,
274
                            query,
275
                            parameters,
276
                            this,
277
                        )
278
                    }
279

280
                    if (err) {
457,776✔
281
                        fail(new QueryFailedError(query, parameters, err))
72✔
282
                    }
283

284
                    ok(raw)
457,776✔
285
                })
286
            })
287

288
            const result = new QueryResult()
457,704✔
289

290
            if (raw?.hasOwnProperty("recordset")) {
457,704✔
291
                result.records = raw.recordset
457,704✔
292
            }
293

294
            if (raw?.hasOwnProperty("rowsAffected")) {
457,704✔
295
                result.affected = raw.rowsAffected[0]
457,704✔
296
            }
297

298
            const queryType = query.slice(0, query.indexOf(" "))
457,704✔
299
            switch (queryType) {
457,704✔
300
                case "DELETE":
301
                    // for DELETE query additionally return number of affected rows
302
                    result.raw = [raw.recordset, raw.rowsAffected[0]]
1,134✔
303
                    break
1,134✔
304
                default:
305
                    result.raw = raw.recordset
456,570✔
306
            }
307

308
            if (useStructuredResult) {
457,704✔
309
                return result
135,207✔
310
            } else {
311
                return result.raw
322,497✔
312
            }
313
        } catch (err) {
314
            this.driver.connection.logger.logQueryError(
72✔
315
                err,
316
                query,
317
                parameters,
318
                this,
319
            )
320
            this.broadcaster.broadcastAfterQueryEvent(
72✔
321
                broadcasterResult,
322
                query,
323
                parameters,
324
                false,
325
                undefined,
326
                undefined,
327
                err,
328
            )
329

330
            throw err
72✔
331
        } finally {
332
            await broadcasterResult.wait()
457,776✔
333

334
            release()
457,776✔
335
        }
336
    }
337

338
    /**
339
     * Returns raw data stream.
340
     */
341
    async stream(
342
        query: string,
343
        parameters?: any[],
344
        onEnd?: Function,
345
        onError?: Function,
346
    ): Promise<ReadStream> {
347
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
9!
348

349
        const release = await this.lock.acquire()
9✔
350

351
        this.driver.connection.logger.logQuery(query, parameters, this)
9✔
352
        const pool = await (this.mode === "slave"
9!
353
            ? this.driver.obtainSlaveConnection()
354
            : this.driver.obtainMasterConnection())
355
        const request = new this.driver.mssql.Request(
9✔
356
            this.isTransactionActive ? this.databaseConnection : pool,
9!
357
        )
358
        if (parameters && parameters.length) {
9!
359
            parameters.forEach((parameter, index) => {
×
360
                const parameterName = index.toString()
×
361
                if (InstanceChecker.isMssqlParameter(parameter)) {
×
362
                    request.input(
×
363
                        parameterName,
364
                        this.mssqlParameterToNativeParameter(parameter),
365
                        parameter.value,
366
                    )
367
                } else {
368
                    request.input(parameterName, parameter)
×
369
                }
370
            })
371
        }
372

373
        request.query(query)
9✔
374

375
        const streamRequest = request.toReadableStream()
9✔
376

377
        streamRequest.on("error", (err: any) => {
9✔
378
            release()
×
379
            this.driver.connection.logger.logQueryError(
×
380
                err,
381
                query,
382
                parameters,
383
                this,
384
            )
385
        })
386

387
        streamRequest.on("end", () => {
9✔
388
            release()
9✔
389
        })
390

391
        if (onEnd) {
9!
392
            streamRequest.on("end", onEnd)
×
393
        }
394

395
        if (onError) {
9!
396
            streamRequest.on("error", onError)
×
397
        }
398

399
        return streamRequest
9✔
400
    }
401

402
    /**
403
     * Returns all available database names including system databases.
404
     */
405
    async getDatabases(): Promise<string[]> {
406
        const results: ObjectLiteral[] = await this.query(`EXEC sp_databases`)
×
407
        return results.map((result) => result["DATABASE_NAME"])
×
408
    }
409

410
    /**
411
     * Returns all available schema names including system schemas.
412
     * If database parameter specified, returns schemas of that database.
413
     */
414
    async getSchemas(database?: string): Promise<string[]> {
415
        const query = database
×
416
            ? `SELECT * FROM "${database}"."sys"."schema"`
417
            : `SELECT * FROM "sys"."schemas"`
418
        const results: ObjectLiteral[] = await this.query(query)
×
419
        return results.map((result) => result["name"])
×
420
    }
421

422
    /**
423
     * Checks if database with the given name exist.
424
     */
425
    async hasDatabase(database: string): Promise<boolean> {
426
        const result = await this.query(
12,168✔
427
            `SELECT DB_ID('${database}') as "db_id"`,
428
        )
429
        const dbId = result[0]["db_id"]
12,168✔
430
        return !!dbId
12,168✔
431
    }
432

433
    /**
434
     * Loads currently using database
435
     */
436
    async getCurrentDatabase(): Promise<string> {
437
        const currentDBQuery = await this.query(`SELECT DB_NAME() AS "db_name"`)
15,390✔
438
        return currentDBQuery[0]["db_name"]
15,390✔
439
    }
440

441
    /**
442
     * Checks if schema with the given name exist.
443
     */
444
    async hasSchema(schema: string): Promise<boolean> {
445
        const result = await this.query(
27✔
446
            `SELECT SCHEMA_ID('${schema}') as "schema_id"`,
447
        )
448
        const schemaId = result[0]["schema_id"]
27✔
449
        return !!schemaId
27✔
450
    }
451

452
    /**
453
     * Loads currently using database schema
454
     */
455
    async getCurrentSchema(): Promise<string> {
456
        const currentSchemaQuery = await this.query(
19,260✔
457
            `SELECT SCHEMA_NAME() AS "schema_name"`,
458
        )
459
        return currentSchemaQuery[0]["schema_name"]
19,260✔
460
    }
461

462
    /**
463
     * Checks if table with the given name exist in the database.
464
     */
465
    async hasTable(tableOrName: Table | string): Promise<boolean> {
466
        const parsedTableName = this.driver.parseTableName(tableOrName)
13,140✔
467

468
        if (!parsedTableName.database) {
13,140!
469
            parsedTableName.database = await this.getCurrentDatabase()
×
470
        }
471

472
        if (!parsedTableName.schema) {
13,140!
473
            parsedTableName.schema = await this.getCurrentSchema()
×
474
        }
475

476
        const sql = `SELECT * FROM "${parsedTableName.database}"."INFORMATION_SCHEMA"."TABLES" WHERE "TABLE_NAME" = '${parsedTableName.tableName}' AND "TABLE_SCHEMA" = '${parsedTableName.schema}'`
13,140✔
477
        const result = await this.query(sql)
13,140✔
478
        return result.length ? true : false
13,140✔
479
    }
480

481
    /**
482
     * Checks if column exist in the table.
483
     */
484
    async hasColumn(
485
        tableOrName: Table | string,
486
        columnName: string,
487
    ): Promise<boolean> {
488
        const parsedTableName = this.driver.parseTableName(tableOrName)
36✔
489

490
        if (!parsedTableName.database) {
36!
491
            parsedTableName.database = await this.getCurrentDatabase()
×
492
        }
493

494
        if (!parsedTableName.schema) {
36!
495
            parsedTableName.schema = await this.getCurrentSchema()
×
496
        }
497

498
        const sql = `SELECT * FROM "${parsedTableName.database}"."INFORMATION_SCHEMA"."COLUMNS" WHERE "TABLE_NAME" = '${parsedTableName.tableName}' AND "TABLE_SCHEMA" = '${parsedTableName.schema}' AND "COLUMN_NAME" = '${columnName}'`
36✔
499
        const result = await this.query(sql)
36✔
500
        return result.length ? true : false
36✔
501
    }
502

503
    /**
504
     * Creates a new database.
505
     */
506
    async createDatabase(
507
        database: string,
508
        ifNotExist?: boolean,
509
    ): Promise<void> {
510
        const up = ifNotExist
81!
511
            ? `IF DB_ID('${database}') IS NULL CREATE DATABASE "${database}"`
512
            : `CREATE DATABASE "${database}"`
513
        const down = `DROP DATABASE "${database}"`
81✔
514
        await this.executeQueries(new Query(up), new Query(down))
81✔
515
    }
516

517
    /**
518
     * Drops database.
519
     */
520
    async dropDatabase(database: string, ifExist?: boolean): Promise<void> {
521
        const up = ifExist
18✔
522
            ? `IF DB_ID('${database}') IS NOT NULL DROP DATABASE "${database}"`
523
            : `DROP DATABASE "${database}"`
524
        const down = `CREATE DATABASE "${database}"`
18✔
525
        await this.executeQueries(new Query(up), new Query(down))
18✔
526
    }
527

528
    /**
529
     * Creates table schema.
530
     * If database name also specified (e.g. 'dbName.schemaName') schema will be created in specified database.
531
     */
532
    async createSchema(
533
        schemaPath: string,
534
        ifNotExist?: boolean,
535
    ): Promise<void> {
536
        const upQueries: Query[] = []
126✔
537
        const downQueries: Query[] = []
126✔
538

539
        if (schemaPath.indexOf(".") === -1) {
126✔
540
            const upQuery = ifNotExist
63!
541
                ? `IF SCHEMA_ID('${schemaPath}') IS NULL BEGIN EXEC ('CREATE SCHEMA "${schemaPath}"') END`
542
                : `CREATE SCHEMA "${schemaPath}"`
543
            upQueries.push(new Query(upQuery))
63✔
544
            downQueries.push(new Query(`DROP SCHEMA "${schemaPath}"`))
63✔
545
        } else {
546
            const dbName = schemaPath.split(".")[0]
63✔
547
            const schema = schemaPath.split(".")[1]
63✔
548
            const currentDB = await this.getCurrentDatabase()
63✔
549
            upQueries.push(new Query(`USE "${dbName}"`))
63✔
550
            downQueries.push(new Query(`USE "${currentDB}"`))
63✔
551

552
            const upQuery = ifNotExist
63!
553
                ? `IF SCHEMA_ID('${schema}') IS NULL BEGIN EXEC ('CREATE SCHEMA "${schema}"') END`
554
                : `CREATE SCHEMA "${schema}"`
555
            upQueries.push(new Query(upQuery))
63✔
556
            downQueries.push(new Query(`DROP SCHEMA "${schema}"`))
63✔
557

558
            upQueries.push(new Query(`USE "${currentDB}"`))
63✔
559
            downQueries.push(new Query(`USE "${dbName}"`))
63✔
560
        }
561

562
        await this.executeQueries(upQueries, downQueries)
126✔
563
    }
564

565
    /**
566
     * Drops table schema.
567
     * If database name also specified (e.g. 'dbName.schemaName') schema will be dropped in specified database.
568
     */
569
    async dropSchema(schemaPath: string, ifExist?: boolean): Promise<void> {
570
        const upQueries: Query[] = []
9✔
571
        const downQueries: Query[] = []
9✔
572

573
        if (schemaPath.indexOf(".") === -1) {
9!
574
            const upQuery = ifExist
9!
575
                ? `IF SCHEMA_ID('${schemaPath}') IS NULL BEGIN EXEC ('DROP SCHEMA "${schemaPath}"') END`
576
                : `DROP SCHEMA "${schemaPath}"`
577
            upQueries.push(new Query(upQuery))
9✔
578
            downQueries.push(new Query(`CREATE SCHEMA "${schemaPath}"`))
9✔
579
        } else {
580
            const dbName = schemaPath.split(".")[0]
×
581
            const schema = schemaPath.split(".")[1]
×
582
            const currentDB = await this.getCurrentDatabase()
×
583
            upQueries.push(new Query(`USE "${dbName}"`))
×
584
            downQueries.push(new Query(`USE "${currentDB}"`))
×
585

586
            const upQuery = ifExist
×
587
                ? `IF SCHEMA_ID('${schema}') IS NULL BEGIN EXEC ('DROP SCHEMA "${schema}"') END`
588
                : `DROP SCHEMA "${schema}"`
589
            upQueries.push(new Query(upQuery))
×
590
            downQueries.push(new Query(`CREATE SCHEMA "${schema}"`))
×
591

592
            upQueries.push(new Query(`USE "${currentDB}"`))
×
593
            downQueries.push(new Query(`USE "${dbName}"`))
×
594
        }
595

596
        await this.executeQueries(upQueries, downQueries)
9✔
597
    }
598

599
    /**
600
     * Creates a new table.
601
     */
602
    async createTable(
603
        table: Table,
604
        ifNotExist: boolean = false,
207✔
605
        createForeignKeys: boolean = true,
468✔
606
        createIndices: boolean = true,
42,363✔
607
    ): Promise<void> {
608
        if (ifNotExist) {
42,363✔
609
            const isTableExist = await this.hasTable(table)
261✔
610
            if (isTableExist) return Promise.resolve()
261✔
611
        }
612
        const upQueries: Query[] = []
42,345✔
613
        const downQueries: Query[] = []
42,345✔
614

615
        upQueries.push(this.createTableSql(table, createForeignKeys))
42,345✔
616
        downQueries.push(this.dropTableSql(table))
42,345✔
617

618
        // if createForeignKeys is true, we must drop created foreign keys in down query.
619
        // createTable does not need separate method to create foreign keys, because it create fk's in the same query with table creation.
620
        if (createForeignKeys)
42,345✔
621
            table.foreignKeys.forEach((foreignKey) =>
450✔
622
                downQueries.push(this.dropForeignKeySql(table, foreignKey)),
27✔
623
            )
624

625
        if (createIndices) {
42,345✔
626
            table.indices.forEach((index) => {
42,345✔
627
                // new index may be passed without name. In this case we generate index name manually.
628
                if (!index.name)
19,071✔
629
                    index.name = this.connection.namingStrategy.indexName(
27✔
630
                        table,
631
                        index.columnNames,
632
                        index.where,
633
                    )
634
                upQueries.push(this.createIndexSql(table, index))
19,071✔
635
                downQueries.push(this.dropIndexSql(table, index))
19,071✔
636
            })
637
        }
638

639
        // if table have column with generated type, we must add the expression to the metadata table
640
        const generatedColumns = table.columns.filter(
42,345✔
641
            (column) => column.generatedType && column.asExpression,
136,773✔
642
        )
643

644
        for (const column of generatedColumns) {
42,345✔
645
            const parsedTableName = this.driver.parseTableName(table)
252✔
646

647
            if (!parsedTableName.schema) {
252!
648
                parsedTableName.schema = await this.getCurrentSchema()
×
649
            }
650

651
            const insertQuery = this.insertTypeormMetadataSql({
252✔
652
                database: parsedTableName.database,
653
                schema: parsedTableName.schema,
654
                table: parsedTableName.tableName,
655
                type: MetadataTableType.GENERATED_COLUMN,
656
                name: column.name,
657
                value: column.asExpression,
658
            })
659

660
            const deleteQuery = this.deleteTypeormMetadataSql({
252✔
661
                database: parsedTableName.database,
662
                schema: parsedTableName.schema,
663
                table: parsedTableName.tableName,
664
                type: MetadataTableType.GENERATED_COLUMN,
665
                name: column.name,
666
            })
667

668
            upQueries.push(insertQuery)
252✔
669
            downQueries.push(deleteQuery)
252✔
670
        }
671

672
        await this.executeQueries(upQueries, downQueries)
42,345✔
673
    }
674

675
    /**
676
     * Drops the table.
677
     */
678
    async dropTable(
679
        tableOrName: Table | string,
680
        ifExist?: boolean,
681
        dropForeignKeys: boolean = true,
45✔
682
        dropIndices: boolean = true,
63✔
683
    ): Promise<void> {
684
        if (ifExist) {
63✔
685
            const isTableExist = await this.hasTable(tableOrName)
18✔
686
            if (!isTableExist) return Promise.resolve()
18!
687
        }
688

689
        // if dropTable called with dropForeignKeys = true, we must create foreign keys in down query.
690
        const createForeignKeys: boolean = dropForeignKeys
63✔
691
        const table = InstanceChecker.isTable(tableOrName)
63✔
692
            ? tableOrName
693
            : await this.getCachedTable(tableOrName)
694
        const upQueries: Query[] = []
63✔
695
        const downQueries: Query[] = []
63✔
696

697
        // It needs because if table does not exist and dropForeignKeys or dropIndices is true, we don't need
698
        // to perform drop queries for foreign keys and indices.
699

700
        if (dropIndices) {
63✔
701
            table.indices.forEach((index) => {
63✔
702
                upQueries.push(this.dropIndexSql(table, index))
9✔
703
                downQueries.push(this.createIndexSql(table, index))
9✔
704
            })
705
        }
706

707
        // if dropForeignKeys is true, we just drop the table, otherwise we also drop table foreign keys.
708
        // createTable does not need separate method to create foreign keys, because it create fk's in the same query with table creation.
709
        if (dropForeignKeys)
63✔
710
            table.foreignKeys.forEach((foreignKey) =>
45✔
711
                upQueries.push(this.dropForeignKeySql(table, foreignKey)),
18✔
712
            )
713

714
        upQueries.push(this.dropTableSql(table))
63✔
715
        downQueries.push(this.createTableSql(table, createForeignKeys))
63✔
716

717
        // if table had columns with generated type, we must remove the expression from the metadata table
718
        const generatedColumns = table.columns.filter(
63✔
719
            (column) => column.generatedType && column.asExpression,
243✔
720
        )
721

722
        for (const column of generatedColumns) {
63✔
723
            const parsedTableName = this.driver.parseTableName(table)
36✔
724

725
            if (!parsedTableName.schema) {
36!
726
                parsedTableName.schema = await this.getCurrentSchema()
×
727
            }
728

729
            const deleteQuery = this.deleteTypeormMetadataSql({
36✔
730
                database: parsedTableName.database,
731
                schema: parsedTableName.schema,
732
                table: parsedTableName.tableName,
733
                type: MetadataTableType.GENERATED_COLUMN,
734
                name: column.name,
735
            })
736

737
            const insertQuery = this.insertTypeormMetadataSql({
36✔
738
                database: parsedTableName.database,
739
                schema: parsedTableName.schema,
740
                table: parsedTableName.tableName,
741
                type: MetadataTableType.GENERATED_COLUMN,
742
                name: column.name,
743
                value: column.asExpression,
744
            })
745

746
            upQueries.push(deleteQuery)
36✔
747
            downQueries.push(insertQuery)
36✔
748
        }
749

750
        await this.executeQueries(upQueries, downQueries)
63✔
751
    }
752

753
    /**
754
     * Creates a new view.
755
     */
756
    async createView(
757
        view: View,
758
        syncWithMetadata: boolean = false,
×
759
    ): Promise<void> {
760
        const upQueries: Query[] = []
72✔
761
        const downQueries: Query[] = []
72✔
762
        upQueries.push(this.createViewSql(view))
72✔
763
        if (syncWithMetadata)
72✔
764
            upQueries.push(await this.insertViewDefinitionSql(view))
72✔
765
        downQueries.push(this.dropViewSql(view))
72✔
766
        if (syncWithMetadata)
72✔
767
            downQueries.push(await this.deleteViewDefinitionSql(view))
72✔
768
        await this.executeQueries(upQueries, downQueries)
72✔
769
    }
770

771
    /**
772
     * Drops the view.
773
     */
774
    async dropView(target: View | string): Promise<void> {
775
        const viewName = InstanceChecker.isView(target) ? target.name : target
×
776
        const view = await this.getCachedView(viewName)
×
777

778
        const upQueries: Query[] = []
×
779
        const downQueries: Query[] = []
×
780
        upQueries.push(await this.deleteViewDefinitionSql(view))
×
781
        upQueries.push(this.dropViewSql(view))
×
782
        downQueries.push(await this.insertViewDefinitionSql(view))
×
783
        downQueries.push(this.createViewSql(view))
×
784
        await this.executeQueries(upQueries, downQueries)
×
785
    }
786

787
    /**
788
     * Renames a table.
789
     */
790
    async renameTable(
791
        oldTableOrName: Table | string,
792
        newTableName: string,
793
    ): Promise<void> {
794
        const upQueries: Query[] = []
153✔
795
        const downQueries: Query[] = []
153✔
796
        const oldTable = InstanceChecker.isTable(oldTableOrName)
153✔
797
            ? oldTableOrName
798
            : await this.getCachedTable(oldTableOrName)
799
        let newTable = oldTable.clone()
153✔
800

801
        // we need database name and schema name to rename FK constraints
802
        let dbName: string | undefined = undefined
153✔
803
        let schemaName: string | undefined = undefined
153✔
804
        let oldTableName: string = oldTable.name
153✔
805
        const splittedName = oldTable.name.split(".")
153✔
806
        if (splittedName.length === 3) {
153✔
807
            dbName = splittedName[0]
18✔
808
            oldTableName = splittedName[2]
18✔
809
            if (splittedName[1] !== "") schemaName = splittedName[1]
18✔
810
        } else if (splittedName.length === 2) {
135!
811
            schemaName = splittedName[0]
×
812
            oldTableName = splittedName[1]
×
813
        }
814

815
        newTable.name = this.driver.buildTableName(
153✔
816
            newTableName,
817
            schemaName,
818
            dbName,
819
        )
820

821
        // if we have tables with database which differs from database specified in config, we must change currently used database.
822
        // This need because we can not rename objects from another database.
823
        const currentDB = await this.getCurrentDatabase()
153✔
824
        if (dbName && dbName !== currentDB) {
153✔
825
            upQueries.push(new Query(`USE "${dbName}"`))
18✔
826
            downQueries.push(new Query(`USE "${currentDB}"`))
18✔
827
        }
828

829
        // rename table
830
        upQueries.push(
153✔
831
            new Query(
832
                `EXEC sp_rename "${this.getTablePath(
833
                    oldTable,
834
                )}", "${newTableName}"`,
835
            ),
836
        )
837
        downQueries.push(
153✔
838
            new Query(
839
                `EXEC sp_rename "${this.getTablePath(
840
                    newTable,
841
                )}", "${oldTableName}"`,
842
            ),
843
        )
844

845
        // rename primary key constraint
846
        if (
153✔
847
            newTable.primaryColumns.length > 0 &&
306✔
848
            !newTable.primaryColumns[0].primaryKeyConstraintName
849
        ) {
850
            const columnNames = newTable.primaryColumns.map(
117✔
851
                (column) => column.name,
135✔
852
            )
853

854
            const oldPkName = this.connection.namingStrategy.primaryKeyName(
117✔
855
                oldTable,
856
                columnNames,
857
            )
858
            const newPkName = this.connection.namingStrategy.primaryKeyName(
117✔
859
                newTable,
860
                columnNames,
861
            )
862

863
            // rename primary constraint
864
            upQueries.push(
117✔
865
                new Query(
866
                    `EXEC sp_rename "${this.getTablePath(
867
                        newTable,
868
                    )}.${oldPkName}", "${newPkName}"`,
869
                ),
870
            )
871
            downQueries.push(
117✔
872
                new Query(
873
                    `EXEC sp_rename "${this.getTablePath(
874
                        newTable,
875
                    )}.${newPkName}", "${oldPkName}"`,
876
                ),
877
            )
878
        }
879

880
        // rename unique constraints
881
        newTable.uniques.forEach((unique) => {
153✔
882
            const oldUniqueName =
883
                this.connection.namingStrategy.uniqueConstraintName(
45✔
884
                    oldTable,
885
                    unique.columnNames,
886
                )
887

888
            // Skip renaming if Unique has user defined constraint name
889
            if (unique.name !== oldUniqueName) return
45✔
890

891
            // build new constraint name
892
            const newUniqueName =
893
                this.connection.namingStrategy.uniqueConstraintName(
27✔
894
                    newTable,
895
                    unique.columnNames,
896
                )
897

898
            // build queries
899
            upQueries.push(
27✔
900
                new Query(
901
                    `EXEC sp_rename "${this.getTablePath(newTable)}.${
902
                        unique.name
903
                    }", "${newUniqueName}"`,
904
                ),
905
            )
906
            downQueries.push(
27✔
907
                new Query(
908
                    `EXEC sp_rename "${this.getTablePath(
909
                        newTable,
910
                    )}.${newUniqueName}", "${unique.name}"`,
911
                ),
912
            )
913

914
            // replace constraint name
915
            unique.name = newUniqueName
27✔
916
        })
917

918
        // rename index constraints
919
        newTable.indices.forEach((index) => {
153✔
920
            const oldIndexName = this.connection.namingStrategy.indexName(
99✔
921
                oldTable,
922
                index.columnNames,
923
                index.where,
924
            )
925

926
            // Skip renaming if Index has user defined constraint name
927
            if (index.name !== oldIndexName) return
99✔
928

929
            // build new constraint name
930
            const newIndexName = this.connection.namingStrategy.indexName(
45✔
931
                newTable,
932
                index.columnNames,
933
                index.where,
934
            )
935

936
            // build queries
937
            upQueries.push(
45✔
938
                new Query(
939
                    `EXEC sp_rename "${this.getTablePath(newTable)}.${
940
                        index.name
941
                    }", "${newIndexName}", "INDEX"`,
942
                ),
943
            )
944
            downQueries.push(
45✔
945
                new Query(
946
                    `EXEC sp_rename "${this.getTablePath(
947
                        newTable,
948
                    )}.${newIndexName}", "${index.name}", "INDEX"`,
949
                ),
950
            )
951

952
            // replace constraint name
953
            index.name = newIndexName
45✔
954
        })
955

956
        // rename foreign key constraints
957
        newTable.foreignKeys.forEach((foreignKey) => {
153✔
958
            const oldForeignKeyName =
959
                this.connection.namingStrategy.foreignKeyName(
81✔
960
                    oldTable,
961
                    foreignKey.columnNames,
962
                    this.getTablePath(foreignKey),
963
                    foreignKey.referencedColumnNames,
964
                )
965

966
            // Skip renaming if foreign key has user defined constraint name
967
            if (foreignKey.name !== oldForeignKeyName) return
81✔
968

969
            // build new constraint name
970
            const newForeignKeyName =
971
                this.connection.namingStrategy.foreignKeyName(
9✔
972
                    newTable,
973
                    foreignKey.columnNames,
974
                    this.getTablePath(foreignKey),
975
                    foreignKey.referencedColumnNames,
976
                )
977

978
            // build queries
979
            upQueries.push(
9✔
980
                new Query(
981
                    `EXEC sp_rename "${this.buildForeignKeyName(
982
                        foreignKey.name!,
983
                        schemaName,
984
                        dbName,
985
                    )}", "${newForeignKeyName}"`,
986
                ),
987
            )
988
            downQueries.push(
9✔
989
                new Query(
990
                    `EXEC sp_rename "${this.buildForeignKeyName(
991
                        newForeignKeyName,
992
                        schemaName,
993
                        dbName,
994
                    )}", "${foreignKey.name}"`,
995
                ),
996
            )
997

998
            // replace constraint name
999
            foreignKey.name = newForeignKeyName
9✔
1000
        })
1001

1002
        // change currently used database back to default db.
1003
        if (dbName && dbName !== currentDB) {
153✔
1004
            upQueries.push(new Query(`USE "${currentDB}"`))
18✔
1005
            downQueries.push(new Query(`USE "${dbName}"`))
18✔
1006
        }
1007

1008
        await this.executeQueries(upQueries, downQueries)
153✔
1009

1010
        // rename old table and replace it in cached tabled;
1011
        oldTable.name = newTable.name
153✔
1012
        this.replaceCachedTable(oldTable, newTable)
153✔
1013
    }
1014

1015
    /**
1016
     * Creates a new column from the column in the table.
1017
     */
1018
    async addColumn(
1019
        tableOrName: Table | string,
1020
        column: TableColumn,
1021
    ): Promise<void> {
1022
        const table = InstanceChecker.isTable(tableOrName)
333✔
1023
            ? tableOrName
1024
            : await this.getCachedTable(tableOrName)
1025
        const clonedTable = table.clone()
333✔
1026
        const upQueries: Query[] = []
333✔
1027
        const downQueries: Query[] = []
333✔
1028

1029
        upQueries.push(
333✔
1030
            new Query(
1031
                `ALTER TABLE ${this.escapePath(
1032
                    table,
1033
                )} ADD ${this.buildCreateColumnSql(
1034
                    table,
1035
                    column,
1036
                    false,
1037
                    true,
1038
                )}`,
1039
            ),
1040
        )
1041
        downQueries.push(
333✔
1042
            new Query(
1043
                `ALTER TABLE ${this.escapePath(table)} DROP COLUMN "${
1044
                    column.name
1045
                }"`,
1046
            ),
1047
        )
1048

1049
        // create or update primary key constraint
1050
        if (column.isPrimary) {
333✔
1051
            const primaryColumns = clonedTable.primaryColumns
63✔
1052
            // if table already have primary key, me must drop it and recreate again
1053
            if (primaryColumns.length > 0) {
63✔
1054
                const pkName = primaryColumns[0].primaryKeyConstraintName
18!
1055
                    ? primaryColumns[0].primaryKeyConstraintName
1056
                    : this.connection.namingStrategy.primaryKeyName(
1057
                          clonedTable,
1058
                          primaryColumns.map((column) => column.name),
18✔
1059
                      )
1060

1061
                const columnNames = primaryColumns
18✔
1062
                    .map((column) => `"${column.name}"`)
18✔
1063
                    .join(", ")
1064

1065
                upQueries.push(
18✔
1066
                    new Query(
1067
                        `ALTER TABLE ${this.escapePath(
1068
                            table,
1069
                        )} DROP CONSTRAINT "${pkName}"`,
1070
                    ),
1071
                )
1072
                downQueries.push(
18✔
1073
                    new Query(
1074
                        `ALTER TABLE ${this.escapePath(
1075
                            table,
1076
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1077
                    ),
1078
                )
1079
            }
1080

1081
            primaryColumns.push(column)
63✔
1082
            const pkName = primaryColumns[0].primaryKeyConstraintName
63!
1083
                ? primaryColumns[0].primaryKeyConstraintName
1084
                : this.connection.namingStrategy.primaryKeyName(
1085
                      clonedTable,
1086
                      primaryColumns.map((column) => column.name),
81✔
1087
                  )
1088

1089
            const columnNames = primaryColumns
63✔
1090
                .map((column) => `"${column.name}"`)
81✔
1091
                .join(", ")
1092
            upQueries.push(
63✔
1093
                new Query(
1094
                    `ALTER TABLE ${this.escapePath(
1095
                        table,
1096
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1097
                ),
1098
            )
1099
            downQueries.push(
63✔
1100
                new Query(
1101
                    `ALTER TABLE ${this.escapePath(
1102
                        table,
1103
                    )} DROP CONSTRAINT "${pkName}"`,
1104
                ),
1105
            )
1106
        }
1107

1108
        // create column index
1109
        const columnIndex = clonedTable.indices.find(
333✔
1110
            (index) =>
1111
                index.columnNames.length === 1 &&
9✔
1112
                index.columnNames[0] === column.name,
1113
        )
1114
        if (columnIndex) {
333!
1115
            upQueries.push(this.createIndexSql(table, columnIndex))
×
1116
            downQueries.push(this.dropIndexSql(table, columnIndex))
×
1117
        }
1118

1119
        // create unique constraint
1120
        if (column.isUnique) {
333✔
1121
            const uniqueConstraint = new TableUnique({
27✔
1122
                name: this.connection.namingStrategy.uniqueConstraintName(
1123
                    table,
1124
                    [column.name],
1125
                ),
1126
                columnNames: [column.name],
1127
            })
1128
            clonedTable.uniques.push(uniqueConstraint)
27✔
1129
            upQueries.push(
27✔
1130
                new Query(
1131
                    `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
1132
                        uniqueConstraint.name
1133
                    }" UNIQUE ("${column.name}")`,
1134
                ),
1135
            )
1136
            downQueries.push(
27✔
1137
                new Query(
1138
                    `ALTER TABLE ${this.escapePath(table)} DROP CONSTRAINT "${
1139
                        uniqueConstraint.name
1140
                    }"`,
1141
                ),
1142
            )
1143
        }
1144

1145
        // remove default constraint
1146
        if (column.default !== null && column.default !== undefined) {
333✔
1147
            const defaultName =
1148
                this.connection.namingStrategy.defaultConstraintName(
45✔
1149
                    table,
1150
                    column.name,
1151
                )
1152
            downQueries.push(
45✔
1153
                new Query(
1154
                    `ALTER TABLE ${this.escapePath(
1155
                        table,
1156
                    )} DROP CONSTRAINT "${defaultName}"`,
1157
                ),
1158
            )
1159
        }
1160

1161
        if (column.generatedType && column.asExpression) {
333✔
1162
            const parsedTableName = this.driver.parseTableName(table)
27✔
1163

1164
            if (!parsedTableName.schema) {
27!
1165
                parsedTableName.schema = await this.getCurrentSchema()
×
1166
            }
1167

1168
            const insertQuery = this.insertTypeormMetadataSql({
27✔
1169
                database: parsedTableName.database,
1170
                schema: parsedTableName.schema,
1171
                table: parsedTableName.tableName,
1172
                type: MetadataTableType.GENERATED_COLUMN,
1173
                name: column.name,
1174
                value: column.asExpression,
1175
            })
1176

1177
            const deleteQuery = this.deleteTypeormMetadataSql({
27✔
1178
                database: parsedTableName.database,
1179
                schema: parsedTableName.schema,
1180
                table: parsedTableName.tableName,
1181
                type: MetadataTableType.GENERATED_COLUMN,
1182
                name: column.name,
1183
            })
1184

1185
            upQueries.push(insertQuery)
27✔
1186
            downQueries.push(deleteQuery)
27✔
1187
        }
1188

1189
        await this.executeQueries(upQueries, downQueries)
333✔
1190

1191
        clonedTable.addColumn(column)
315✔
1192
        this.replaceCachedTable(table, clonedTable)
315✔
1193
    }
1194

1195
    /**
1196
     * Creates a new columns from the column in the table.
1197
     */
1198
    async addColumns(
1199
        tableOrName: Table | string,
1200
        columns: TableColumn[],
1201
    ): Promise<void> {
1202
        for (const column of columns) {
54✔
1203
            await this.addColumn(tableOrName, column)
63✔
1204
        }
1205
    }
1206

1207
    /**
1208
     * Renames column in the given table.
1209
     */
1210
    async renameColumn(
1211
        tableOrName: Table | string,
1212
        oldTableColumnOrName: TableColumn | string,
1213
        newTableColumnOrName: TableColumn | string,
1214
    ): Promise<void> {
1215
        const table = InstanceChecker.isTable(tableOrName)
126✔
1216
            ? tableOrName
1217
            : await this.getCachedTable(tableOrName)
1218
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
126✔
1219
            ? oldTableColumnOrName
1220
            : table.columns.find((c) => c.name === oldTableColumnOrName)
99✔
1221
        if (!oldColumn)
126!
1222
            throw new TypeORMError(
×
1223
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
1224
            )
1225

1226
        let newColumn: TableColumn | undefined = undefined
126✔
1227
        if (InstanceChecker.isTableColumn(newTableColumnOrName)) {
126✔
1228
            newColumn = newTableColumnOrName
81✔
1229
        } else {
1230
            newColumn = oldColumn.clone()
45✔
1231
            newColumn.name = newTableColumnOrName
45✔
1232
        }
1233

1234
        await this.changeColumn(table, oldColumn, newColumn)
126✔
1235
    }
1236

1237
    /**
1238
     * Changes a column in the table.
1239
     */
1240
    async changeColumn(
1241
        tableOrName: Table | string,
1242
        oldTableColumnOrName: TableColumn | string,
1243
        newColumn: TableColumn,
1244
    ): Promise<void> {
1245
        const table = InstanceChecker.isTable(tableOrName)
459!
1246
            ? tableOrName
1247
            : await this.getCachedTable(tableOrName)
1248
        let clonedTable = table.clone()
459✔
1249
        const upQueries: Query[] = []
459✔
1250
        const downQueries: Query[] = []
459✔
1251

1252
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
459!
1253
            ? oldTableColumnOrName
1254
            : table.columns.find(
1255
                  (column) => column.name === oldTableColumnOrName,
×
1256
              )
1257
        if (!oldColumn)
459!
1258
            throw new TypeORMError(
×
1259
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
1260
            )
1261

1262
        if (
459✔
1263
            (newColumn.isGenerated !== oldColumn.isGenerated &&
1,791✔
1264
                newColumn.generationStrategy !== "uuid") ||
1265
            newColumn.type !== oldColumn.type ||
1266
            newColumn.length !== oldColumn.length ||
1267
            newColumn.asExpression !== oldColumn.asExpression ||
1268
            newColumn.generatedType !== oldColumn.generatedType
1269
        ) {
1270
            // SQL Server does not support changing of IDENTITY column, so we must drop column and recreate it again.
1271
            // Also, we recreate column if column type changed
1272
            await this.dropColumn(table, oldColumn)
225✔
1273
            await this.addColumn(table, newColumn)
225✔
1274

1275
            // update cloned table
1276
            clonedTable = table.clone()
225✔
1277
        } else {
1278
            if (newColumn.name !== oldColumn.name) {
234✔
1279
                // we need database name and schema name to rename FK constraints
1280
                let dbName: string | undefined = undefined
126✔
1281
                let schemaName: string | undefined = undefined
126✔
1282
                const splittedName = table.name.split(".")
126✔
1283
                if (splittedName.length === 3) {
126✔
1284
                    dbName = splittedName[0]
18✔
1285
                    if (splittedName[1] !== "") schemaName = splittedName[1]
18✔
1286
                } else if (splittedName.length === 2) {
108!
1287
                    schemaName = splittedName[0]
×
1288
                }
1289

1290
                // if we have tables with database which differs from database specified in config, we must change currently used database.
1291
                // This need because we can not rename objects from another database.
1292
                const currentDB = await this.getCurrentDatabase()
126✔
1293
                if (dbName && dbName !== currentDB) {
126✔
1294
                    upQueries.push(new Query(`USE "${dbName}"`))
18✔
1295
                    downQueries.push(new Query(`USE "${currentDB}"`))
18✔
1296
                }
1297

1298
                // rename the column
1299
                upQueries.push(
126✔
1300
                    new Query(
1301
                        `EXEC sp_rename "${this.getTablePath(table)}.${
1302
                            oldColumn.name
1303
                        }", "${newColumn.name}"`,
1304
                    ),
1305
                )
1306
                downQueries.push(
126✔
1307
                    new Query(
1308
                        `EXEC sp_rename "${this.getTablePath(table)}.${
1309
                            newColumn.name
1310
                        }", "${oldColumn.name}"`,
1311
                    ),
1312
                )
1313

1314
                // rename column primary key constraint
1315
                if (
126✔
1316
                    oldColumn.isPrimary === true &&
153✔
1317
                    !oldColumn.primaryKeyConstraintName
1318
                ) {
1319
                    const primaryColumns = clonedTable.primaryColumns
9✔
1320

1321
                    // build old primary constraint name
1322
                    const columnNames = primaryColumns.map(
9✔
1323
                        (column) => column.name,
9✔
1324
                    )
1325
                    const oldPkName =
1326
                        this.connection.namingStrategy.primaryKeyName(
9✔
1327
                            clonedTable,
1328
                            columnNames,
1329
                        )
1330

1331
                    // replace old column name with new column name
1332
                    columnNames.splice(columnNames.indexOf(oldColumn.name), 1)
9✔
1333
                    columnNames.push(newColumn.name)
9✔
1334

1335
                    // build new primary constraint name
1336
                    const newPkName =
1337
                        this.connection.namingStrategy.primaryKeyName(
9✔
1338
                            clonedTable,
1339
                            columnNames,
1340
                        )
1341

1342
                    // rename primary constraint
1343
                    upQueries.push(
9✔
1344
                        new Query(
1345
                            `EXEC sp_rename "${this.getTablePath(
1346
                                clonedTable,
1347
                            )}.${oldPkName}", "${newPkName}"`,
1348
                        ),
1349
                    )
1350
                    downQueries.push(
9✔
1351
                        new Query(
1352
                            `EXEC sp_rename "${this.getTablePath(
1353
                                clonedTable,
1354
                            )}.${newPkName}", "${oldPkName}"`,
1355
                        ),
1356
                    )
1357
                }
1358

1359
                // rename index constraints
1360
                clonedTable.findColumnIndices(oldColumn).forEach((index) => {
126✔
1361
                    const oldIndexName =
1362
                        this.connection.namingStrategy.indexName(
27✔
1363
                            clonedTable,
1364
                            index.columnNames,
1365
                            index.where,
1366
                        )
1367

1368
                    // Skip renaming if Index has user defined constraint name
1369
                    if (index.name !== oldIndexName) return
27✔
1370

1371
                    // build new constraint name
1372
                    index.columnNames.splice(
9✔
1373
                        index.columnNames.indexOf(oldColumn.name),
1374
                        1,
1375
                    )
1376
                    index.columnNames.push(newColumn.name)
9✔
1377
                    const newIndexName =
1378
                        this.connection.namingStrategy.indexName(
9✔
1379
                            clonedTable,
1380
                            index.columnNames,
1381
                            index.where,
1382
                        )
1383

1384
                    // build queries
1385
                    upQueries.push(
9✔
1386
                        new Query(
1387
                            `EXEC sp_rename "${this.getTablePath(
1388
                                clonedTable,
1389
                            )}.${index.name}", "${newIndexName}", "INDEX"`,
1390
                        ),
1391
                    )
1392
                    downQueries.push(
9✔
1393
                        new Query(
1394
                            `EXEC sp_rename "${this.getTablePath(
1395
                                clonedTable,
1396
                            )}.${newIndexName}", "${index.name}", "INDEX"`,
1397
                        ),
1398
                    )
1399

1400
                    // replace constraint name
1401
                    index.name = newIndexName
9✔
1402
                })
1403

1404
                // rename foreign key constraints
1405
                clonedTable
126✔
1406
                    .findColumnForeignKeys(oldColumn)
1407
                    .forEach((foreignKey) => {
1408
                        const foreignKeyName =
1409
                            this.connection.namingStrategy.foreignKeyName(
9✔
1410
                                clonedTable,
1411
                                foreignKey.columnNames,
1412
                                this.getTablePath(foreignKey),
1413
                                foreignKey.referencedColumnNames,
1414
                            )
1415

1416
                        // Skip renaming if foreign key has user defined constraint name
1417
                        if (foreignKey.name !== foreignKeyName) return
9!
1418

1419
                        // build new constraint name
1420
                        foreignKey.columnNames.splice(
9✔
1421
                            foreignKey.columnNames.indexOf(oldColumn.name),
1422
                            1,
1423
                        )
1424
                        foreignKey.columnNames.push(newColumn.name)
9✔
1425
                        const newForeignKeyName =
1426
                            this.connection.namingStrategy.foreignKeyName(
9✔
1427
                                clonedTable,
1428
                                foreignKey.columnNames,
1429
                                this.getTablePath(foreignKey),
1430
                                foreignKey.referencedColumnNames,
1431
                            )
1432

1433
                        // build queries
1434
                        upQueries.push(
9✔
1435
                            new Query(
1436
                                `EXEC sp_rename "${this.buildForeignKeyName(
1437
                                    foreignKey.name!,
1438
                                    schemaName,
1439
                                    dbName,
1440
                                )}", "${newForeignKeyName}"`,
1441
                            ),
1442
                        )
1443
                        downQueries.push(
9✔
1444
                            new Query(
1445
                                `EXEC sp_rename "${this.buildForeignKeyName(
1446
                                    newForeignKeyName,
1447
                                    schemaName,
1448
                                    dbName,
1449
                                )}", "${foreignKey.name}"`,
1450
                            ),
1451
                        )
1452

1453
                        // replace constraint name
1454
                        foreignKey.name = newForeignKeyName
9✔
1455
                    })
1456

1457
                // rename check constraints
1458
                clonedTable.findColumnChecks(oldColumn).forEach((check) => {
126✔
1459
                    // build new constraint name
1460
                    check.columnNames!.splice(
×
1461
                        check.columnNames!.indexOf(oldColumn.name),
1462
                        1,
1463
                    )
1464
                    check.columnNames!.push(newColumn.name)
×
1465
                    const newCheckName =
1466
                        this.connection.namingStrategy.checkConstraintName(
×
1467
                            clonedTable,
1468
                            check.expression!,
1469
                        )
1470

1471
                    // build queries
1472
                    upQueries.push(
×
1473
                        new Query(
1474
                            `EXEC sp_rename "${this.getTablePath(
1475
                                clonedTable,
1476
                            )}.${check.name}", "${newCheckName}"`,
1477
                        ),
1478
                    )
1479
                    downQueries.push(
×
1480
                        new Query(
1481
                            `EXEC sp_rename "${this.getTablePath(
1482
                                clonedTable,
1483
                            )}.${newCheckName}", "${check.name}"`,
1484
                        ),
1485
                    )
1486

1487
                    // replace constraint name
1488
                    check.name = newCheckName
×
1489
                })
1490

1491
                // rename unique constraints
1492
                clonedTable.findColumnUniques(oldColumn).forEach((unique) => {
126✔
1493
                    const oldUniqueName =
1494
                        this.connection.namingStrategy.uniqueConstraintName(
54✔
1495
                            clonedTable,
1496
                            unique.columnNames,
1497
                        )
1498

1499
                    // Skip renaming if Unique has user defined constraint name
1500
                    if (unique.name !== oldUniqueName) return
54✔
1501

1502
                    // build new constraint name
1503
                    unique.columnNames.splice(
36✔
1504
                        unique.columnNames.indexOf(oldColumn.name),
1505
                        1,
1506
                    )
1507
                    unique.columnNames.push(newColumn.name)
36✔
1508
                    const newUniqueName =
1509
                        this.connection.namingStrategy.uniqueConstraintName(
36✔
1510
                            clonedTable,
1511
                            unique.columnNames,
1512
                        )
1513

1514
                    // build queries
1515
                    upQueries.push(
36✔
1516
                        new Query(
1517
                            `EXEC sp_rename "${this.getTablePath(
1518
                                clonedTable,
1519
                            )}.${unique.name}", "${newUniqueName}"`,
1520
                        ),
1521
                    )
1522
                    downQueries.push(
36✔
1523
                        new Query(
1524
                            `EXEC sp_rename "${this.getTablePath(
1525
                                clonedTable,
1526
                            )}.${newUniqueName}", "${unique.name}"`,
1527
                        ),
1528
                    )
1529

1530
                    // replace constraint name
1531
                    unique.name = newUniqueName
36✔
1532
                })
1533

1534
                // rename default constraints
1535
                if (
126✔
1536
                    oldColumn.default !== null &&
252✔
1537
                    oldColumn.default !== undefined
1538
                ) {
1539
                    const oldDefaultName =
1540
                        this.connection.namingStrategy.defaultConstraintName(
18✔
1541
                            table,
1542
                            oldColumn.name,
1543
                        )
1544
                    const newDefaultName =
1545
                        this.connection.namingStrategy.defaultConstraintName(
18✔
1546
                            table,
1547
                            newColumn.name,
1548
                        )
1549

1550
                    upQueries.push(
18✔
1551
                        new Query(
1552
                            `ALTER TABLE ${this.escapePath(
1553
                                table,
1554
                            )} DROP CONSTRAINT "${oldDefaultName}"`,
1555
                        ),
1556
                    )
1557
                    downQueries.push(
18✔
1558
                        new Query(
1559
                            `ALTER TABLE ${this.escapePath(
1560
                                table,
1561
                            )} ADD CONSTRAINT "${oldDefaultName}" DEFAULT ${
1562
                                oldColumn.default
1563
                            } FOR "${newColumn.name}"`,
1564
                        ),
1565
                    )
1566

1567
                    upQueries.push(
18✔
1568
                        new Query(
1569
                            `ALTER TABLE ${this.escapePath(
1570
                                table,
1571
                            )} ADD CONSTRAINT "${newDefaultName}" DEFAULT ${
1572
                                oldColumn.default
1573
                            } FOR "${newColumn.name}"`,
1574
                        ),
1575
                    )
1576
                    downQueries.push(
18✔
1577
                        new Query(
1578
                            `ALTER TABLE ${this.escapePath(
1579
                                table,
1580
                            )} DROP CONSTRAINT "${newDefaultName}"`,
1581
                        ),
1582
                    )
1583
                }
1584

1585
                // change currently used database back to default db.
1586
                if (dbName && dbName !== currentDB) {
126✔
1587
                    upQueries.push(new Query(`USE "${currentDB}"`))
18✔
1588
                    downQueries.push(new Query(`USE "${dbName}"`))
18✔
1589
                }
1590

1591
                // rename old column in the Table object
1592
                const oldTableColumn = clonedTable.columns.find(
126✔
1593
                    (column) => column.name === oldColumn.name,
288✔
1594
                )
1595
                clonedTable.columns[
126✔
1596
                    clonedTable.columns.indexOf(oldTableColumn!)
1597
                ].name = newColumn.name
1598
                oldColumn.name = newColumn.name
126✔
1599
            }
1600

1601
            if (
234!
1602
                this.isColumnChanged(oldColumn, newColumn, false, false, false)
1603
            ) {
1604
                upQueries.push(
×
1605
                    new Query(
1606
                        `ALTER TABLE ${this.escapePath(
1607
                            table,
1608
                        )} ALTER COLUMN ${this.buildCreateColumnSql(
1609
                            table,
1610
                            newColumn,
1611
                            true,
1612
                            false,
1613
                            true,
1614
                        )}`,
1615
                    ),
1616
                )
1617
                downQueries.push(
×
1618
                    new Query(
1619
                        `ALTER TABLE ${this.escapePath(
1620
                            table,
1621
                        )} ALTER COLUMN ${this.buildCreateColumnSql(
1622
                            table,
1623
                            oldColumn,
1624
                            true,
1625
                            false,
1626
                            true,
1627
                        )}`,
1628
                    ),
1629
                )
1630
            }
1631

1632
            if (this.isEnumChanged(oldColumn, newColumn)) {
234✔
1633
                const oldExpression = this.getEnumExpression(oldColumn)
9✔
1634
                const oldCheck = new TableCheck({
9✔
1635
                    name: this.connection.namingStrategy.checkConstraintName(
1636
                        table,
1637
                        oldExpression,
1638
                        true,
1639
                    ),
1640
                    expression: oldExpression,
1641
                })
1642

1643
                const newExpression = this.getEnumExpression(newColumn)
9✔
1644
                const newCheck = new TableCheck({
9✔
1645
                    name: this.connection.namingStrategy.checkConstraintName(
1646
                        table,
1647
                        newExpression,
1648
                        true,
1649
                    ),
1650
                    expression: newExpression,
1651
                })
1652

1653
                upQueries.push(this.dropCheckConstraintSql(table, oldCheck))
9✔
1654
                upQueries.push(this.createCheckConstraintSql(table, newCheck))
9✔
1655

1656
                downQueries.push(this.dropCheckConstraintSql(table, newCheck))
9✔
1657
                downQueries.push(this.createCheckConstraintSql(table, oldCheck))
9✔
1658
            }
1659

1660
            if (newColumn.isPrimary !== oldColumn.isPrimary) {
234✔
1661
                const primaryColumns = clonedTable.primaryColumns
27✔
1662

1663
                // if primary column state changed, we must always drop existed constraint.
1664
                if (primaryColumns.length > 0) {
27✔
1665
                    const pkName = primaryColumns[0].primaryKeyConstraintName
27!
1666
                        ? primaryColumns[0].primaryKeyConstraintName
1667
                        : this.connection.namingStrategy.primaryKeyName(
1668
                              clonedTable,
1669
                              primaryColumns.map((column) => column.name),
36✔
1670
                          )
1671

1672
                    const columnNames = primaryColumns
27✔
1673
                        .map((column) => `"${column.name}"`)
36✔
1674
                        .join(", ")
1675
                    upQueries.push(
27✔
1676
                        new Query(
1677
                            `ALTER TABLE ${this.escapePath(
1678
                                table,
1679
                            )} DROP CONSTRAINT "${pkName}"`,
1680
                        ),
1681
                    )
1682
                    downQueries.push(
27✔
1683
                        new Query(
1684
                            `ALTER TABLE ${this.escapePath(
1685
                                table,
1686
                            )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1687
                        ),
1688
                    )
1689
                }
1690

1691
                if (newColumn.isPrimary === true) {
27✔
1692
                    primaryColumns.push(newColumn)
9✔
1693
                    // update column in table
1694
                    const column = clonedTable.columns.find(
9✔
1695
                        (column) => column.name === newColumn.name,
27✔
1696
                    )
1697
                    column!.isPrimary = true
9✔
1698
                    const pkName = primaryColumns[0].primaryKeyConstraintName
9!
1699
                        ? primaryColumns[0].primaryKeyConstraintName
1700
                        : this.connection.namingStrategy.primaryKeyName(
1701
                              clonedTable,
1702
                              primaryColumns.map((column) => column.name),
18✔
1703
                          )
1704

1705
                    const columnNames = primaryColumns
9✔
1706
                        .map((column) => `"${column.name}"`)
18✔
1707
                        .join(", ")
1708
                    upQueries.push(
9✔
1709
                        new Query(
1710
                            `ALTER TABLE ${this.escapePath(
1711
                                table,
1712
                            )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1713
                        ),
1714
                    )
1715
                    downQueries.push(
9✔
1716
                        new Query(
1717
                            `ALTER TABLE ${this.escapePath(
1718
                                table,
1719
                            )} DROP CONSTRAINT "${pkName}"`,
1720
                        ),
1721
                    )
1722
                } else {
1723
                    const primaryColumn = primaryColumns.find(
18✔
1724
                        (c) => c.name === newColumn.name,
18✔
1725
                    )
1726
                    primaryColumns.splice(
18✔
1727
                        primaryColumns.indexOf(primaryColumn!),
1728
                        1,
1729
                    )
1730

1731
                    // update column in table
1732
                    const column = clonedTable.columns.find(
18✔
1733
                        (column) => column.name === newColumn.name,
54✔
1734
                    )
1735
                    column!.isPrimary = false
18✔
1736

1737
                    // if we have another primary keys, we must recreate constraint.
1738
                    if (primaryColumns.length > 0) {
18✔
1739
                        const pkName = primaryColumns[0]
9!
1740
                            .primaryKeyConstraintName
1741
                            ? primaryColumns[0].primaryKeyConstraintName
1742
                            : this.connection.namingStrategy.primaryKeyName(
1743
                                  clonedTable,
1744
                                  primaryColumns.map((column) => column.name),
9✔
1745
                              )
1746

1747
                        const columnNames = primaryColumns
9✔
1748
                            .map((column) => `"${column.name}"`)
9✔
1749
                            .join(", ")
1750
                        upQueries.push(
9✔
1751
                            new Query(
1752
                                `ALTER TABLE ${this.escapePath(
1753
                                    table,
1754
                                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1755
                            ),
1756
                        )
1757
                        downQueries.push(
9✔
1758
                            new Query(
1759
                                `ALTER TABLE ${this.escapePath(
1760
                                    table,
1761
                                )} DROP CONSTRAINT "${pkName}"`,
1762
                            ),
1763
                        )
1764
                    }
1765
                }
1766
            }
1767

1768
            if (newColumn.isUnique !== oldColumn.isUnique) {
234✔
1769
                if (newColumn.isUnique === true) {
9!
1770
                    const uniqueConstraint = new TableUnique({
9✔
1771
                        name: this.connection.namingStrategy.uniqueConstraintName(
1772
                            table,
1773
                            [newColumn.name],
1774
                        ),
1775
                        columnNames: [newColumn.name],
1776
                    })
1777
                    clonedTable.uniques.push(uniqueConstraint)
9✔
1778
                    upQueries.push(
9✔
1779
                        new Query(
1780
                            `ALTER TABLE ${this.escapePath(
1781
                                table,
1782
                            )} ADD CONSTRAINT "${
1783
                                uniqueConstraint.name
1784
                            }" UNIQUE ("${newColumn.name}")`,
1785
                        ),
1786
                    )
1787
                    downQueries.push(
9✔
1788
                        new Query(
1789
                            `ALTER TABLE ${this.escapePath(
1790
                                table,
1791
                            )} DROP CONSTRAINT "${uniqueConstraint.name}"`,
1792
                        ),
1793
                    )
1794
                } else {
1795
                    const uniqueConstraint = clonedTable.uniques.find(
×
1796
                        (unique) => {
1797
                            return (
×
1798
                                unique.columnNames.length === 1 &&
×
1799
                                !!unique.columnNames.find(
1800
                                    (columnName) =>
1801
                                        columnName === newColumn.name,
×
1802
                                )
1803
                            )
1804
                        },
1805
                    )
1806
                    clonedTable.uniques.splice(
×
1807
                        clonedTable.uniques.indexOf(uniqueConstraint!),
1808
                        1,
1809
                    )
1810
                    upQueries.push(
×
1811
                        new Query(
1812
                            `ALTER TABLE ${this.escapePath(
1813
                                table,
1814
                            )} DROP CONSTRAINT "${uniqueConstraint!.name}"`,
1815
                        ),
1816
                    )
1817
                    downQueries.push(
×
1818
                        new Query(
1819
                            `ALTER TABLE ${this.escapePath(
1820
                                table,
1821
                            )} ADD CONSTRAINT "${
1822
                                uniqueConstraint!.name
1823
                            }" UNIQUE ("${newColumn.name}")`,
1824
                        ),
1825
                    )
1826
                }
1827
            }
1828

1829
            if (newColumn.default !== oldColumn.default) {
234✔
1830
                // (note) if there is a previous default, we need to drop its constraint first
1831
                if (
18✔
1832
                    oldColumn.default !== null &&
36✔
1833
                    oldColumn.default !== undefined
1834
                ) {
1835
                    const defaultName =
1836
                        this.connection.namingStrategy.defaultConstraintName(
9✔
1837
                            table,
1838
                            oldColumn.name,
1839
                        )
1840
                    upQueries.push(
9✔
1841
                        new Query(
1842
                            `ALTER TABLE ${this.escapePath(
1843
                                table,
1844
                            )} DROP CONSTRAINT "${defaultName}"`,
1845
                        ),
1846
                    )
1847
                    downQueries.push(
9✔
1848
                        new Query(
1849
                            `ALTER TABLE ${this.escapePath(
1850
                                table,
1851
                            )} ADD CONSTRAINT "${defaultName}" DEFAULT ${
1852
                                oldColumn.default
1853
                            } FOR "${oldColumn.name}"`,
1854
                        ),
1855
                    )
1856
                }
1857

1858
                if (
18✔
1859
                    newColumn.default !== null &&
36✔
1860
                    newColumn.default !== undefined
1861
                ) {
1862
                    const defaultName =
1863
                        this.connection.namingStrategy.defaultConstraintName(
18✔
1864
                            table,
1865
                            newColumn.name,
1866
                        )
1867
                    upQueries.push(
18✔
1868
                        new Query(
1869
                            `ALTER TABLE ${this.escapePath(
1870
                                table,
1871
                            )} ADD CONSTRAINT "${defaultName}" DEFAULT ${
1872
                                newColumn.default
1873
                            } FOR "${newColumn.name}"`,
1874
                        ),
1875
                    )
1876
                    downQueries.push(
18✔
1877
                        new Query(
1878
                            `ALTER TABLE ${this.escapePath(
1879
                                table,
1880
                            )} DROP CONSTRAINT "${defaultName}"`,
1881
                        ),
1882
                    )
1883
                }
1884
            }
1885

1886
            await this.executeQueries(upQueries, downQueries)
234✔
1887
            this.replaceCachedTable(table, clonedTable)
234✔
1888
        }
1889
    }
1890

1891
    /**
1892
     * Changes a column in the table.
1893
     */
1894
    async changeColumns(
1895
        tableOrName: Table | string,
1896
        changedColumns: { newColumn: TableColumn; oldColumn: TableColumn }[],
1897
    ): Promise<void> {
1898
        for (const { oldColumn, newColumn } of changedColumns) {
198✔
1899
            await this.changeColumn(tableOrName, oldColumn, newColumn)
279✔
1900
        }
1901
    }
1902

1903
    /**
1904
     * Drops column in the table.
1905
     */
1906
    async dropColumn(
1907
        tableOrName: Table | string,
1908
        columnOrName: TableColumn | string,
1909
    ): Promise<void> {
1910
        const table = InstanceChecker.isTable(tableOrName)
351✔
1911
            ? tableOrName
1912
            : await this.getCachedTable(tableOrName)
1913
        const column = InstanceChecker.isTableColumn(columnOrName)
351✔
1914
            ? columnOrName
1915
            : table.findColumnByName(columnOrName)
1916
        if (!column)
351✔
1917
            throw new TypeORMError(
9✔
1918
                `Column "${columnOrName}" was not found in table "${table.name}"`,
1919
            )
1920

1921
        const clonedTable = table.clone()
342✔
1922
        const upQueries: Query[] = []
342✔
1923
        const downQueries: Query[] = []
342✔
1924

1925
        // drop primary key constraint
1926
        if (column.isPrimary) {
342✔
1927
            const pkName = column.primaryKeyConstraintName
63!
1928
                ? column.primaryKeyConstraintName
1929
                : this.connection.namingStrategy.primaryKeyName(
1930
                      clonedTable,
1931
                      clonedTable.primaryColumns.map((column) => column.name),
72✔
1932
                  )
1933

1934
            const columnNames = clonedTable.primaryColumns
63✔
1935
                .map((primaryColumn) => `"${primaryColumn.name}"`)
72✔
1936
                .join(", ")
1937

1938
            upQueries.push(
63✔
1939
                new Query(
1940
                    `ALTER TABLE ${this.escapePath(
1941
                        clonedTable,
1942
                    )} DROP CONSTRAINT "${pkName}"`,
1943
                ),
1944
            )
1945
            downQueries.push(
63✔
1946
                new Query(
1947
                    `ALTER TABLE ${this.escapePath(
1948
                        clonedTable,
1949
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1950
                ),
1951
            )
1952

1953
            // update column in table
1954
            const tableColumn = clonedTable.findColumnByName(column.name)
63✔
1955
            tableColumn!.isPrimary = false
63✔
1956

1957
            // if primary key have multiple columns, we must recreate it without dropped column
1958
            if (clonedTable.primaryColumns.length > 0) {
63✔
1959
                const pkName = clonedTable.primaryColumns[0]
9!
1960
                    .primaryKeyConstraintName
1961
                    ? clonedTable.primaryColumns[0].primaryKeyConstraintName
1962
                    : this.connection.namingStrategy.primaryKeyName(
1963
                          clonedTable,
1964
                          clonedTable.primaryColumns.map(
1965
                              (column) => column.name,
9✔
1966
                          ),
1967
                      )
1968

1969
                const columnNames = clonedTable.primaryColumns
9✔
1970
                    .map((primaryColumn) => `"${primaryColumn.name}"`)
9✔
1971
                    .join(", ")
1972
                upQueries.push(
9✔
1973
                    new Query(
1974
                        `ALTER TABLE ${this.escapePath(
1975
                            clonedTable,
1976
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1977
                    ),
1978
                )
1979
                downQueries.push(
9✔
1980
                    new Query(
1981
                        `ALTER TABLE ${this.escapePath(
1982
                            clonedTable,
1983
                        )} DROP CONSTRAINT "${pkName}"`,
1984
                    ),
1985
                )
1986
            }
1987
        }
1988

1989
        // drop column index
1990
        const columnIndex = clonedTable.indices.find(
342✔
1991
            (index) =>
1992
                index.columnNames.length === 1 &&
9✔
1993
                index.columnNames[0] === column.name,
1994
        )
1995
        if (columnIndex) {
342!
1996
            clonedTable.indices.splice(
×
1997
                clonedTable.indices.indexOf(columnIndex),
1998
                1,
1999
            )
2000
            upQueries.push(this.dropIndexSql(table, columnIndex))
×
2001
            downQueries.push(this.createIndexSql(table, columnIndex))
×
2002
        }
2003

2004
        // drop column check
2005
        const columnCheck = clonedTable.checks.find(
342✔
2006
            (check) =>
2007
                !!check.columnNames &&
144✔
2008
                check.columnNames.length === 1 &&
2009
                check.columnNames[0] === column.name,
2010
        )
2011
        if (columnCheck) {
342✔
2012
            clonedTable.checks.splice(
18✔
2013
                clonedTable.checks.indexOf(columnCheck),
2014
                1,
2015
            )
2016
            upQueries.push(this.dropCheckConstraintSql(table, columnCheck))
18✔
2017
            downQueries.push(this.createCheckConstraintSql(table, columnCheck))
18✔
2018
        }
2019

2020
        // drop column unique
2021
        const columnUnique = clonedTable.uniques.find(
342✔
2022
            (unique) =>
2023
                unique.columnNames.length === 1 &&
270✔
2024
                unique.columnNames[0] === column.name,
2025
        )
2026
        if (columnUnique) {
342✔
2027
            clonedTable.uniques.splice(
27✔
2028
                clonedTable.uniques.indexOf(columnUnique),
2029
                1,
2030
            )
2031
            upQueries.push(this.dropUniqueConstraintSql(table, columnUnique))
27✔
2032
            downQueries.push(
27✔
2033
                this.createUniqueConstraintSql(table, columnUnique),
2034
            )
2035
        }
2036

2037
        // drop default constraint
2038
        if (column.default !== null && column.default !== undefined) {
342✔
2039
            const defaultName =
2040
                this.connection.namingStrategy.defaultConstraintName(
54✔
2041
                    table,
2042
                    column.name,
2043
                )
2044
            upQueries.push(
54✔
2045
                new Query(
2046
                    `ALTER TABLE ${this.escapePath(
2047
                        table,
2048
                    )} DROP CONSTRAINT "${defaultName}"`,
2049
                ),
2050
            )
2051
            downQueries.push(
54✔
2052
                new Query(
2053
                    `ALTER TABLE ${this.escapePath(
2054
                        table,
2055
                    )} ADD CONSTRAINT "${defaultName}" DEFAULT ${
2056
                        column.default
2057
                    } FOR "${column.name}"`,
2058
                ),
2059
            )
2060
        }
2061

2062
        if (column.generatedType && column.asExpression) {
342✔
2063
            const parsedTableName = this.driver.parseTableName(table)
36✔
2064

2065
            if (!parsedTableName.schema) {
36!
2066
                parsedTableName.schema = await this.getCurrentSchema()
×
2067
            }
2068

2069
            const deleteQuery = this.deleteTypeormMetadataSql({
36✔
2070
                database: parsedTableName.database,
2071
                schema: parsedTableName.schema,
2072
                table: parsedTableName.tableName,
2073
                type: MetadataTableType.GENERATED_COLUMN,
2074
                name: column.name,
2075
            })
2076
            const insertQuery = this.insertTypeormMetadataSql({
36✔
2077
                database: parsedTableName.database,
2078
                schema: parsedTableName.schema,
2079
                table: parsedTableName.tableName,
2080
                type: MetadataTableType.GENERATED_COLUMN,
2081
                name: column.name,
2082
                value: column.asExpression,
2083
            })
2084

2085
            upQueries.push(deleteQuery)
36✔
2086
            downQueries.push(insertQuery)
36✔
2087
        }
2088

2089
        upQueries.push(
342✔
2090
            new Query(
2091
                `ALTER TABLE ${this.escapePath(table)} DROP COLUMN "${
2092
                    column.name
2093
                }"`,
2094
            ),
2095
        )
2096
        downQueries.push(
342✔
2097
            new Query(
2098
                `ALTER TABLE ${this.escapePath(
2099
                    table,
2100
                )} ADD ${this.buildCreateColumnSql(
2101
                    table,
2102
                    column,
2103
                    false,
2104
                    false,
2105
                )}`,
2106
            ),
2107
        )
2108

2109
        await this.executeQueries(upQueries, downQueries)
342✔
2110

2111
        clonedTable.removeColumn(column)
342✔
2112
        this.replaceCachedTable(table, clonedTable)
342✔
2113
    }
2114

2115
    /**
2116
     * Drops the columns in the table.
2117
     */
2118
    async dropColumns(
2119
        tableOrName: Table | string,
2120
        columns: TableColumn[] | string[],
2121
    ): Promise<void> {
2122
        for (const column of columns) {
36✔
2123
            await this.dropColumn(tableOrName, column)
81✔
2124
        }
2125
    }
2126

2127
    /**
2128
     * Creates a new primary key.
2129
     */
2130
    async createPrimaryKey(
2131
        tableOrName: Table | string,
2132
        columnNames: string[],
2133
        constraintName?: string,
2134
    ): Promise<void> {
2135
        const table = InstanceChecker.isTable(tableOrName)
18!
2136
            ? tableOrName
2137
            : await this.getCachedTable(tableOrName)
2138
        const clonedTable = table.clone()
18✔
2139

2140
        const up = this.createPrimaryKeySql(table, columnNames, constraintName)
18✔
2141

2142
        // mark columns as primary, because dropPrimaryKeySql build constraint name from table primary column names.
2143
        clonedTable.columns.forEach((column) => {
18✔
2144
            if (columnNames.find((columnName) => columnName === column.name))
63✔
2145
                column.isPrimary = true
27✔
2146
        })
2147
        const down = this.dropPrimaryKeySql(clonedTable)
18✔
2148

2149
        await this.executeQueries(up, down)
18✔
2150
        this.replaceCachedTable(table, clonedTable)
18✔
2151
    }
2152

2153
    /**
2154
     * Updates composite primary keys.
2155
     */
2156
    async updatePrimaryKeys(
2157
        tableOrName: Table | string,
2158
        columns: TableColumn[],
2159
    ): Promise<void> {
2160
        const table = InstanceChecker.isTable(tableOrName)
27!
2161
            ? tableOrName
2162
            : await this.getCachedTable(tableOrName)
2163
        const clonedTable = table.clone()
27✔
2164
        const columnNames = columns.map((column) => column.name)
54✔
2165
        const upQueries: Query[] = []
27✔
2166
        const downQueries: Query[] = []
27✔
2167

2168
        // if table already have primary columns, we must drop them.
2169
        const primaryColumns = clonedTable.primaryColumns
27✔
2170
        if (primaryColumns.length > 0) {
27✔
2171
            const pkName = primaryColumns[0].primaryKeyConstraintName
27!
2172
                ? primaryColumns[0].primaryKeyConstraintName
2173
                : this.connection.namingStrategy.primaryKeyName(
2174
                      clonedTable,
2175
                      primaryColumns.map((column) => column.name),
27✔
2176
                  )
2177

2178
            const columnNamesString = primaryColumns
27✔
2179
                .map((column) => `"${column.name}"`)
27✔
2180
                .join(", ")
2181

2182
            upQueries.push(
27✔
2183
                new Query(
2184
                    `ALTER TABLE ${this.escapePath(
2185
                        table,
2186
                    )} DROP CONSTRAINT "${pkName}"`,
2187
                ),
2188
            )
2189
            downQueries.push(
27✔
2190
                new Query(
2191
                    `ALTER TABLE ${this.escapePath(
2192
                        table,
2193
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
2194
                ),
2195
            )
2196
        }
2197

2198
        // update columns in table.
2199
        clonedTable.columns
27✔
2200
            .filter((column) => columnNames.indexOf(column.name) !== -1)
108✔
2201
            .forEach((column) => (column.isPrimary = true))
54✔
2202

2203
        const pkName = primaryColumns[0].primaryKeyConstraintName
27!
2204
            ? primaryColumns[0].primaryKeyConstraintName
2205
            : this.connection.namingStrategy.primaryKeyName(
2206
                  clonedTable,
2207
                  columnNames,
2208
              )
2209

2210
        const columnNamesString = columnNames
27✔
2211
            .map((columnName) => `"${columnName}"`)
54✔
2212
            .join(", ")
2213

2214
        upQueries.push(
27✔
2215
            new Query(
2216
                `ALTER TABLE ${this.escapePath(
2217
                    table,
2218
                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
2219
            ),
2220
        )
2221
        downQueries.push(
27✔
2222
            new Query(
2223
                `ALTER TABLE ${this.escapePath(
2224
                    table,
2225
                )} DROP CONSTRAINT "${pkName}"`,
2226
            ),
2227
        )
2228

2229
        await this.executeQueries(upQueries, downQueries)
27✔
2230
        this.replaceCachedTable(table, clonedTable)
27✔
2231
    }
2232

2233
    /**
2234
     * Drops a primary key.
2235
     */
2236
    async dropPrimaryKey(
2237
        tableOrName: Table | string,
2238
        constraintName?: string,
2239
    ): Promise<void> {
2240
        const table = InstanceChecker.isTable(tableOrName)
27!
2241
            ? tableOrName
2242
            : await this.getCachedTable(tableOrName)
2243
        const up = this.dropPrimaryKeySql(table)
27✔
2244
        const down = this.createPrimaryKeySql(
27✔
2245
            table,
2246
            table.primaryColumns.map((column) => column.name),
27✔
2247
            constraintName,
2248
        )
2249
        await this.executeQueries(up, down)
27✔
2250
        table.primaryColumns.forEach((column) => {
27✔
2251
            column.isPrimary = false
27✔
2252
        })
2253
    }
2254

2255
    /**
2256
     * Creates a new unique constraint.
2257
     */
2258
    async createUniqueConstraint(
2259
        tableOrName: Table | string,
2260
        uniqueConstraint: TableUnique,
2261
    ): Promise<void> {
2262
        const table = InstanceChecker.isTable(tableOrName)
36✔
2263
            ? tableOrName
2264
            : await this.getCachedTable(tableOrName)
2265

2266
        // new unique constraint may be passed without name. In this case we generate unique name manually.
2267
        if (!uniqueConstraint.name)
36✔
2268
            uniqueConstraint.name =
18✔
2269
                this.connection.namingStrategy.uniqueConstraintName(
2270
                    table,
2271
                    uniqueConstraint.columnNames,
2272
                )
2273

2274
        const up = this.createUniqueConstraintSql(table, uniqueConstraint)
36✔
2275
        const down = this.dropUniqueConstraintSql(table, uniqueConstraint)
36✔
2276
        await this.executeQueries(up, down)
36✔
2277
        table.addUniqueConstraint(uniqueConstraint)
36✔
2278
    }
2279

2280
    /**
2281
     * Creates a new unique constraints.
2282
     */
2283
    async createUniqueConstraints(
2284
        tableOrName: Table | string,
2285
        uniqueConstraints: TableUnique[],
2286
    ): Promise<void> {
2287
        const promises = uniqueConstraints.map((uniqueConstraint) =>
18✔
2288
            this.createUniqueConstraint(tableOrName, uniqueConstraint),
18✔
2289
        )
2290
        await Promise.all(promises)
18✔
2291
    }
2292

2293
    /**
2294
     * Drops unique constraint.
2295
     */
2296
    async dropUniqueConstraint(
2297
        tableOrName: Table | string,
2298
        uniqueOrName: TableUnique | string,
2299
    ): Promise<void> {
2300
        const table = InstanceChecker.isTable(tableOrName)
36!
2301
            ? tableOrName
2302
            : await this.getCachedTable(tableOrName)
2303
        const uniqueConstraint = InstanceChecker.isTableUnique(uniqueOrName)
36!
2304
            ? uniqueOrName
2305
            : table.uniques.find((u) => u.name === uniqueOrName)
×
2306
        if (!uniqueConstraint)
36!
2307
            throw new TypeORMError(
×
2308
                `Supplied unique constraint was not found in table ${table.name}`,
2309
            )
2310

2311
        const up = this.dropUniqueConstraintSql(table, uniqueConstraint)
36✔
2312
        const down = this.createUniqueConstraintSql(table, uniqueConstraint)
36✔
2313
        await this.executeQueries(up, down)
36✔
2314
        table.removeUniqueConstraint(uniqueConstraint)
36✔
2315
    }
2316

2317
    /**
2318
     * Drops an unique constraints.
2319
     */
2320
    async dropUniqueConstraints(
2321
        tableOrName: Table | string,
2322
        uniqueConstraints: TableUnique[],
2323
    ): Promise<void> {
2324
        const promises = uniqueConstraints.map((uniqueConstraint) =>
27✔
2325
            this.dropUniqueConstraint(tableOrName, uniqueConstraint),
27✔
2326
        )
2327
        await Promise.all(promises)
27✔
2328
    }
2329

2330
    /**
2331
     * Creates a new check constraint.
2332
     */
2333
    async createCheckConstraint(
2334
        tableOrName: Table | string,
2335
        checkConstraint: TableCheck,
2336
    ): Promise<void> {
2337
        const table = InstanceChecker.isTable(tableOrName)
45✔
2338
            ? tableOrName
2339
            : await this.getCachedTable(tableOrName)
2340

2341
        // new unique constraint may be passed without name. In this case we generate unique name manually.
2342
        if (!checkConstraint.name)
45✔
2343
            checkConstraint.name =
27✔
2344
                this.connection.namingStrategy.checkConstraintName(
2345
                    table,
2346
                    checkConstraint.expression!,
2347
                )
2348

2349
        const up = this.createCheckConstraintSql(table, checkConstraint)
45✔
2350
        const down = this.dropCheckConstraintSql(table, checkConstraint)
45✔
2351
        await this.executeQueries(up, down)
45✔
2352
        table.addCheckConstraint(checkConstraint)
45✔
2353
    }
2354

2355
    /**
2356
     * Creates a new check constraints.
2357
     */
2358
    async createCheckConstraints(
2359
        tableOrName: Table | string,
2360
        checkConstraints: TableCheck[],
2361
    ): Promise<void> {
2362
        const promises = checkConstraints.map((checkConstraint) =>
18✔
2363
            this.createCheckConstraint(tableOrName, checkConstraint),
18✔
2364
        )
2365
        await Promise.all(promises)
18✔
2366
    }
2367

2368
    /**
2369
     * Drops check constraint.
2370
     */
2371
    async dropCheckConstraint(
2372
        tableOrName: Table | string,
2373
        checkOrName: TableCheck | string,
2374
    ): Promise<void> {
2375
        const table = InstanceChecker.isTable(tableOrName)
27!
2376
            ? tableOrName
2377
            : await this.getCachedTable(tableOrName)
2378
        const checkConstraint = InstanceChecker.isTableCheck(checkOrName)
27!
2379
            ? checkOrName
2380
            : table.checks.find((c) => c.name === checkOrName)
×
2381
        if (!checkConstraint)
27!
2382
            throw new TypeORMError(
×
2383
                `Supplied check constraint was not found in table ${table.name}`,
2384
            )
2385

2386
        const up = this.dropCheckConstraintSql(table, checkConstraint)
27✔
2387
        const down = this.createCheckConstraintSql(table, checkConstraint)
27✔
2388
        await this.executeQueries(up, down)
27✔
2389
        table.removeCheckConstraint(checkConstraint)
27✔
2390
    }
2391

2392
    /**
2393
     * Drops check constraints.
2394
     */
2395
    async dropCheckConstraints(
2396
        tableOrName: Table | string,
2397
        checkConstraints: TableCheck[],
2398
    ): Promise<void> {
2399
        const promises = checkConstraints.map((checkConstraint) =>
18✔
2400
            this.dropCheckConstraint(tableOrName, checkConstraint),
18✔
2401
        )
2402
        await Promise.all(promises)
18✔
2403
    }
2404

2405
    /**
2406
     * Creates a new exclusion constraint.
2407
     */
2408
    async createExclusionConstraint(
2409
        tableOrName: Table | string,
2410
        exclusionConstraint: TableExclusion,
2411
    ): Promise<void> {
2412
        throw new TypeORMError(
×
2413
            `SqlServer does not support exclusion constraints.`,
2414
        )
2415
    }
2416

2417
    /**
2418
     * Creates a new exclusion constraints.
2419
     */
2420
    async createExclusionConstraints(
2421
        tableOrName: Table | string,
2422
        exclusionConstraints: TableExclusion[],
2423
    ): Promise<void> {
2424
        throw new TypeORMError(
×
2425
            `SqlServer does not support exclusion constraints.`,
2426
        )
2427
    }
2428

2429
    /**
2430
     * Drops exclusion constraint.
2431
     */
2432
    async dropExclusionConstraint(
2433
        tableOrName: Table | string,
2434
        exclusionOrName: TableExclusion | string,
2435
    ): Promise<void> {
2436
        throw new TypeORMError(
×
2437
            `SqlServer does not support exclusion constraints.`,
2438
        )
2439
    }
2440

2441
    /**
2442
     * Drops exclusion constraints.
2443
     */
2444
    async dropExclusionConstraints(
2445
        tableOrName: Table | string,
2446
        exclusionConstraints: TableExclusion[],
2447
    ): Promise<void> {
2448
        throw new TypeORMError(
×
2449
            `SqlServer does not support exclusion constraints.`,
2450
        )
2451
    }
2452

2453
    /**
2454
     * Creates a new foreign key.
2455
     */
2456
    async createForeignKey(
2457
        tableOrName: Table | string,
2458
        foreignKey: TableForeignKey,
2459
    ): Promise<void> {
2460
        const table = InstanceChecker.isTable(tableOrName)
28,179✔
2461
            ? tableOrName
2462
            : await this.getCachedTable(tableOrName)
2463
        const metadata = this.connection.hasMetadata(table.name)
28,179✔
2464
            ? this.connection.getMetadata(table.name)
2465
            : undefined
2466

2467
        if (
28,179!
2468
            metadata &&
63,369✔
2469
            metadata.treeParentRelation &&
2470
            metadata.treeParentRelation!.isTreeParent &&
2471
            metadata.foreignKeys.find(
2472
                (foreignKey) => foreignKey.onDelete !== "NO ACTION",
4,176✔
2473
            )
2474
        )
2475
            throw new TypeORMError(
×
2476
                "SqlServer does not support options in TreeParent.",
2477
            )
2478

2479
        // new FK may be passed without name. In this case we generate FK name manually.
2480
        if (!foreignKey.name)
28,179✔
2481
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
9✔
2482
                table,
2483
                foreignKey.columnNames,
2484
                this.getTablePath(foreignKey),
2485
                foreignKey.referencedColumnNames,
2486
            )
2487

2488
        const up = this.createForeignKeySql(table, foreignKey)
28,179✔
2489
        const down = this.dropForeignKeySql(table, foreignKey)
28,179✔
2490
        await this.executeQueries(up, down)
28,179✔
2491
        table.addForeignKey(foreignKey)
28,179✔
2492
    }
2493

2494
    /**
2495
     * Creates a new foreign keys.
2496
     */
2497
    async createForeignKeys(
2498
        tableOrName: Table | string,
2499
        foreignKeys: TableForeignKey[],
2500
    ): Promise<void> {
2501
        const promises = foreignKeys.map((foreignKey) =>
17,109✔
2502
            this.createForeignKey(tableOrName, foreignKey),
28,170✔
2503
        )
2504
        await Promise.all(promises)
17,109✔
2505
    }
2506

2507
    /**
2508
     * Drops a foreign key from the table.
2509
     */
2510
    async dropForeignKey(
2511
        tableOrName: Table | string,
2512
        foreignKeyOrName: TableForeignKey | string,
2513
    ): Promise<void> {
2514
        const table = InstanceChecker.isTable(tableOrName)
63!
2515
            ? tableOrName
2516
            : await this.getCachedTable(tableOrName)
2517
        const foreignKey = InstanceChecker.isTableForeignKey(foreignKeyOrName)
63!
2518
            ? foreignKeyOrName
2519
            : table.foreignKeys.find((fk) => fk.name === foreignKeyOrName)
×
2520
        if (!foreignKey)
63!
2521
            throw new TypeORMError(
×
2522
                `Supplied foreign key was not found in table ${table.name}`,
2523
            )
2524

2525
        const up = this.dropForeignKeySql(table, foreignKey)
63✔
2526
        const down = this.createForeignKeySql(table, foreignKey)
63✔
2527
        await this.executeQueries(up, down)
63✔
2528
        table.removeForeignKey(foreignKey)
63✔
2529
    }
2530

2531
    /**
2532
     * Drops a foreign keys from the table.
2533
     */
2534
    async dropForeignKeys(
2535
        tableOrName: Table | string,
2536
        foreignKeys: TableForeignKey[],
2537
    ): Promise<void> {
2538
        const promises = foreignKeys.map((foreignKey) =>
54✔
2539
            this.dropForeignKey(tableOrName, foreignKey),
54✔
2540
        )
2541
        await Promise.all(promises)
54✔
2542
    }
2543

2544
    /**
2545
     * Creates a new index.
2546
     */
2547
    async createIndex(
2548
        tableOrName: Table | string,
2549
        index: TableIndex,
2550
    ): Promise<void> {
2551
        const table = InstanceChecker.isTable(tableOrName)
90✔
2552
            ? tableOrName
2553
            : await this.getCachedTable(tableOrName)
2554

2555
        // new index may be passed without name. In this case we generate index name manually.
2556
        if (!index.name) index.name = this.generateIndexName(table, index)
90✔
2557

2558
        const up = this.createIndexSql(table, index)
90✔
2559
        const down = this.dropIndexSql(table, index)
90✔
2560
        await this.executeQueries(up, down)
90✔
2561
        table.addIndex(index)
90✔
2562
    }
2563

2564
    /**
2565
     * Creates a new indices
2566
     */
2567
    async createIndices(
2568
        tableOrName: Table | string,
2569
        indices: TableIndex[],
2570
    ): Promise<void> {
2571
        const promises = indices.map((index) =>
54✔
2572
            this.createIndex(tableOrName, index),
54✔
2573
        )
2574
        await Promise.all(promises)
54✔
2575
    }
2576

2577
    /**
2578
     * Drops an index.
2579
     */
2580
    async dropIndex(
2581
        tableOrName: Table | string,
2582
        indexOrName: TableIndex | string,
2583
    ): Promise<void> {
2584
        const table = InstanceChecker.isTable(tableOrName)
108✔
2585
            ? tableOrName
2586
            : await this.getCachedTable(tableOrName)
2587
        const index = InstanceChecker.isTableIndex(indexOrName)
108!
2588
            ? indexOrName
2589
            : table.indices.find((i) => i.name === indexOrName)
×
2590
        if (!index)
108!
2591
            throw new TypeORMError(
×
2592
                `Supplied index was not found in table ${table.name}`,
2593
            )
2594

2595
        // old index may be passed without name. In this case we generate index name manually.
2596
        if (!index.name) index.name = this.generateIndexName(table, index)
108✔
2597

2598
        const up = this.dropIndexSql(table, index)
108✔
2599
        const down = this.createIndexSql(table, index)
108✔
2600
        await this.executeQueries(up, down)
108✔
2601
        table.removeIndex(index)
108✔
2602
    }
2603

2604
    /**
2605
     * Drops an indices from the table.
2606
     */
2607
    async dropIndices(
2608
        tableOrName: Table | string,
2609
        indices: TableIndex[],
2610
    ): Promise<void> {
2611
        const promises = indices.map((index) =>
9✔
2612
            this.dropIndex(tableOrName, index),
18✔
2613
        )
2614
        await Promise.all(promises)
9✔
2615
    }
2616

2617
    /**
2618
     * Clears all table contents.
2619
     * Note: this operation uses SQL's TRUNCATE query which cannot be reverted in transactions.
2620
     */
2621
    async clearTable(tablePath: string): Promise<void> {
2622
        await this.query(`TRUNCATE TABLE ${this.escapePath(tablePath)}`)
18✔
2623
    }
2624

2625
    /**
2626
     * Removes all tables from the currently connected database.
2627
     */
2628
    async clearDatabase(database?: string): Promise<void> {
2629
        if (database) {
12,141✔
2630
            const isDatabaseExist = await this.hasDatabase(database)
12,141✔
2631
            if (!isDatabaseExist) return Promise.resolve()
12,141!
2632
        }
2633

2634
        const isAnotherTransactionActive = this.isTransactionActive
12,141✔
2635
        if (!isAnotherTransactionActive) await this.startTransaction()
12,141✔
2636
        try {
12,141✔
2637
            let allViewsSql = database
12,141!
2638
                ? `SELECT * FROM "${database}"."INFORMATION_SCHEMA"."VIEWS"`
2639
                : `SELECT * FROM "INFORMATION_SCHEMA"."VIEWS"`
2640
            const allViewsResults: ObjectLiteral[] = await this.query(
12,141✔
2641
                allViewsSql,
2642
            )
2643

2644
            await Promise.all(
12,141✔
2645
                allViewsResults.map((viewResult) => {
2646
                    // 'DROP VIEW' does not allow specifying the database name as a prefix to the object name.
2647
                    const dropTableSql = `DROP VIEW "${viewResult["TABLE_SCHEMA"]}"."${viewResult["TABLE_NAME"]}"`
72✔
2648
                    return this.query(dropTableSql)
72✔
2649
                }),
2650
            )
2651

2652
            let allTablesSql = database
12,141!
2653
                ? `SELECT * FROM "${database}"."INFORMATION_SCHEMA"."TABLES" WHERE "TABLE_TYPE" = 'BASE TABLE'`
2654
                : `SELECT * FROM "INFORMATION_SCHEMA"."TABLES" WHERE "TABLE_TYPE" = 'BASE TABLE'`
2655
            const allTablesResults: ObjectLiteral[] = await this.query(
12,141✔
2656
                allTablesSql,
2657
            )
2658

2659
            if (allTablesResults.length > 0) {
12,141✔
2660
                const tablesByCatalog: {
2661
                    [key: string]: {
2662
                        TABLE_NAME: string
2663
                        TABLE_SCHEMA: string
2664
                    }[]
2665
                } = allTablesResults.reduce(
11,979✔
2666
                    (c, { TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME }) => {
2667
                        c[TABLE_CATALOG] = c[TABLE_CATALOG] || []
42,147✔
2668
                        c[TABLE_CATALOG].push({ TABLE_SCHEMA, TABLE_NAME })
42,147✔
2669
                        return c
42,147✔
2670
                    },
2671
                    {},
2672
                )
2673

2674
                const foreignKeysSql = Object.entries(tablesByCatalog)
11,979✔
2675
                    .map(([TABLE_CATALOG, tables]) => {
2676
                        const conditions = tables
11,979✔
2677
                            .map(({ TABLE_SCHEMA, TABLE_NAME }) => {
2678
                                return `("fk"."referenced_object_id" = OBJECT_ID('"${TABLE_CATALOG}"."${TABLE_SCHEMA}"."${TABLE_NAME}"'))`
42,147✔
2679
                            })
2680
                            .join(" OR ")
2681

2682
                        return `
11,979✔
2683
                        SELECT DISTINCT '${TABLE_CATALOG}' AS                                              "TABLE_CATALOG",
2684
                                        OBJECT_SCHEMA_NAME("fk"."parent_object_id",
2685
                                                           DB_ID('${TABLE_CATALOG}')) AS                   "TABLE_SCHEMA",
2686
                                        OBJECT_NAME("fk"."parent_object_id", DB_ID('${TABLE_CATALOG}')) AS "TABLE_NAME",
2687
                                        "fk"."name" AS                                                     "CONSTRAINT_NAME"
2688
                        FROM "${TABLE_CATALOG}"."sys"."foreign_keys" AS "fk"
2689
                        WHERE (${conditions})
2690
                    `
2691
                    })
2692
                    .join(" UNION ALL ")
2693

2694
                const foreignKeys: {
2695
                    TABLE_CATALOG: string
2696
                    TABLE_SCHEMA: string
2697
                    TABLE_NAME: string
2698
                    CONSTRAINT_NAME: string
2699
                }[] = await this.query(foreignKeysSql)
11,979✔
2700

2701
                await Promise.all(
11,979✔
2702
                    foreignKeys.map(
2703
                        async ({
2704
                            TABLE_CATALOG,
2705
                            TABLE_SCHEMA,
2706
                            TABLE_NAME,
2707
                            CONSTRAINT_NAME,
2708
                        }) => {
2709
                            // Disable the constraint first.
2710
                            await this.query(
28,080✔
2711
                                `ALTER TABLE "${TABLE_CATALOG}"."${TABLE_SCHEMA}"."${TABLE_NAME}" ` +
2712
                                    `NOCHECK CONSTRAINT "${CONSTRAINT_NAME}"`,
2713
                            )
2714

2715
                            await this.query(
28,080✔
2716
                                `ALTER TABLE "${TABLE_CATALOG}"."${TABLE_SCHEMA}"."${TABLE_NAME}" ` +
2717
                                    `DROP CONSTRAINT "${CONSTRAINT_NAME}" -- FROM CLEAR`,
2718
                            )
2719
                        },
2720
                    ),
2721
                )
2722

2723
                await Promise.all(
11,979✔
2724
                    allTablesResults.map((tablesResult) => {
2725
                        if (tablesResult["TABLE_NAME"].startsWith("#")) {
42,147!
2726
                            // don't try to drop temporary tables
UNCOV
2727
                            return
×
2728
                        }
2729

2730
                        const dropTableSql = `DROP TABLE "${tablesResult["TABLE_CATALOG"]}"."${tablesResult["TABLE_SCHEMA"]}"."${tablesResult["TABLE_NAME"]}"`
42,147✔
2731
                        return this.query(dropTableSql)
42,147✔
2732
                    }),
2733
                )
2734
            }
2735

2736
            if (!isAnotherTransactionActive) await this.commitTransaction()
12,141✔
2737
        } catch (error) {
2738
            try {
×
2739
                // we throw original error even if rollback thrown an error
2740
                if (!isAnotherTransactionActive)
×
2741
                    await this.rollbackTransaction()
×
2742
            } catch (rollbackError) {}
2743
            throw error
×
2744
        }
2745
    }
2746

2747
    // -------------------------------------------------------------------------
2748
    // Protected Methods
2749
    // -------------------------------------------------------------------------
2750

2751
    protected async loadViews(viewPaths?: string[]): Promise<View[]> {
2752
        const hasTable = await this.hasTable(this.getTypeormMetadataTableName())
12,636✔
2753
        if (!hasTable) {
12,636✔
2754
            return []
12,501✔
2755
        }
2756

2757
        if (!viewPaths) {
135!
2758
            viewPaths = []
×
2759
        }
2760

2761
        const currentSchema = await this.getCurrentSchema()
135✔
2762
        const currentDatabase = await this.getCurrentDatabase()
135✔
2763

2764
        const dbNames = viewPaths
135✔
2765
            .map((viewPath) => this.driver.parseTableName(viewPath).database)
99✔
2766
            .filter((database) => database)
99✔
2767

2768
        if (
135✔
2769
            this.driver.database &&
270✔
2770
            !dbNames.find((dbName) => dbName === this.driver.database)
63✔
2771
        )
2772
            dbNames.push(this.driver.database)
72✔
2773

2774
        const viewsCondition = viewPaths
135✔
2775
            .map((viewPath) => {
2776
                let { schema, tableName: name } =
2777
                    this.driver.parseTableName(viewPath)
99✔
2778

2779
                if (!schema) {
99!
2780
                    schema = currentSchema
×
2781
                }
2782
                return `("T"."SCHEMA" = '${schema}' AND "T"."NAME" = '${name}')`
99✔
2783
            })
2784
            .join(" OR ")
2785

2786
        const query = dbNames
135✔
2787
            .map((dbName) => {
2788
                return (
171✔
2789
                    `SELECT "T".*, "V"."CHECK_OPTION" FROM ${this.escapePath(
2790
                        this.getTypeormMetadataTableName(),
2791
                    )} "t" ` +
2792
                    `INNER JOIN "${dbName}"."INFORMATION_SCHEMA"."VIEWS" "V" ON "V"."TABLE_SCHEMA" = "T"."SCHEMA" AND "v"."TABLE_NAME" = "T"."NAME" WHERE "T"."TYPE" = '${
2793
                        MetadataTableType.VIEW
2794
                    }' ${viewsCondition ? `AND (${viewsCondition})` : ""}`
171✔
2795
                )
2796
            })
2797
            .join(" UNION ALL ")
2798

2799
        const dbViews = await this.query(query)
135✔
2800
        return dbViews.map((dbView: any) => {
135✔
2801
            const view = new View()
27✔
2802
            const db =
2803
                dbView["TABLE_CATALOG"] === currentDatabase
27!
2804
                    ? undefined
2805
                    : dbView["TABLE_CATALOG"]
2806
            const schema =
2807
                dbView["schema"] === currentSchema &&
27!
2808
                !this.driver.options.schema
2809
                    ? undefined
2810
                    : dbView["schema"]
2811
            view.database = dbView["TABLE_CATALOG"]
27✔
2812
            view.schema = dbView["schema"]
27✔
2813
            view.name = this.driver.buildTableName(dbView["name"], schema, db)
27✔
2814
            view.expression = dbView["value"]
27✔
2815
            return view
27✔
2816
        })
2817
    }
2818

2819
    /**
2820
     * Loads all tables (with given names) from the database and creates a Table from them.
2821
     */
2822
    protected async loadTables(tableNames?: string[]): Promise<Table[]> {
2823
        // if no tables given then no need to proceed
2824
        if (tableNames && tableNames.length === 0) {
14,976✔
2825
            return []
63✔
2826
        }
2827

2828
        const currentSchema = await this.getCurrentSchema()
14,913✔
2829
        const currentDatabase = await this.getCurrentDatabase()
14,913✔
2830

2831
        const dbTables: {
2832
            TABLE_CATALOG: string
2833
            TABLE_SCHEMA: string
2834
            TABLE_NAME: string
2835
        }[] = []
14,913✔
2836

2837
        if (!tableNames) {
14,913!
2838
            const databasesSql =
2839
                `SELECT DISTINCT "name" ` +
×
2840
                `FROM "master"."dbo"."sysdatabases" ` +
2841
                `WHERE "name" NOT IN ('master', 'model', 'msdb')`
2842
            const dbDatabases: { name: string }[] = await this.query(
×
2843
                databasesSql,
2844
            )
2845

2846
            const tablesSql = dbDatabases
×
2847
                .map(({ name }) => {
2848
                    return `
×
2849
                    SELECT DISTINCT
2850
                        "TABLE_CATALOG", "TABLE_SCHEMA", "TABLE_NAME"
2851
                    FROM "${name}"."INFORMATION_SCHEMA"."TABLES"
2852
                    WHERE
2853
                      "TABLE_TYPE" = 'BASE TABLE'
2854
                      AND
2855
                      "TABLE_CATALOG" = '${name}'
2856
                      AND
2857
                      ISNULL(Objectproperty(Object_id("TABLE_CATALOG" + '.' + "TABLE_SCHEMA" + '.' + "TABLE_NAME"), 'IsMSShipped'), 0) = 0
2858
                `
2859
                })
2860
                .join(" UNION ALL ")
2861

2862
            dbTables.push(...(await this.query(tablesSql)))
×
2863
        } else {
2864
            const tableNamesByCatalog = tableNames
14,913✔
2865
                .map((tableName) => this.driver.parseTableName(tableName))
46,296✔
2866
                .reduce((c, { database, ...other }) => {
2867
                    database = database || currentDatabase
46,296!
2868
                    c[database] = c[database] || []
46,296✔
2869
                    c[database].push({
46,296✔
2870
                        schema: other.schema || currentSchema,
46,296!
2871
                        tableName: other.tableName,
2872
                    })
2873
                    return c
46,296✔
2874
                }, {} as { [key: string]: { schema: string; tableName: string }[] })
2875

2876
            const tablesSql = Object.entries(tableNamesByCatalog)
14,913✔
2877
                .map(([database, tables]) => {
2878
                    const tablesCondition = tables
14,949✔
2879
                        .map(({ schema, tableName }) => {
2880
                            return `("TABLE_SCHEMA" = '${schema}' AND "TABLE_NAME" = '${tableName}')`
46,296✔
2881
                        })
2882
                        .join(" OR ")
2883

2884
                    return `
14,949✔
2885
                    SELECT DISTINCT
2886
                        "TABLE_CATALOG", "TABLE_SCHEMA", "TABLE_NAME"
2887
                    FROM "${database}"."INFORMATION_SCHEMA"."TABLES"
2888
                    WHERE
2889
                          "TABLE_TYPE" = 'BASE TABLE' AND
2890
                          "TABLE_CATALOG" = '${database}' AND
2891
                          ${tablesCondition}
2892
                `
2893
                })
2894
                .join(" UNION ALL ")
2895

2896
            dbTables.push(...(await this.query(tablesSql)))
14,913✔
2897
        }
2898

2899
        // if tables were not found in the db, no need to proceed
2900
        if (dbTables.length === 0) {
14,913✔
2901
            return []
12,168✔
2902
        }
2903

2904
        const dbTablesByCatalog = dbTables.reduce(
2,745✔
2905
            (c, { TABLE_CATALOG, ...other }) => {
2906
                c[TABLE_CATALOG] = c[TABLE_CATALOG] || []
4,176✔
2907
                c[TABLE_CATALOG].push(other)
4,176✔
2908
                return c
4,176✔
2909
            },
2910
            {} as {
2911
                [key: string]: { TABLE_NAME: string; TABLE_SCHEMA: string }[]
2912
            },
2913
        )
2914

2915
        const columnsSql = Object.entries(dbTablesByCatalog)
2,745✔
2916
            .map(([TABLE_CATALOG, tables]) => {
2917
                const condition = tables
2,745✔
2918
                    .map(
2919
                        ({ TABLE_SCHEMA, TABLE_NAME }) =>
2920
                            `("TABLE_SCHEMA" = '${TABLE_SCHEMA}' AND "TABLE_NAME" = '${TABLE_NAME}')`,
4,176✔
2921
                    )
2922
                    .join("OR")
2923

2924
                return (
2,745✔
2925
                    `SELECT "COLUMNS".*, "cc"."is_persisted", "cc"."definition" ` +
2926
                    `FROM "${TABLE_CATALOG}"."INFORMATION_SCHEMA"."COLUMNS" ` +
2927
                    `LEFT JOIN "sys"."computed_columns" "cc" ON COL_NAME("cc"."object_id", "cc"."column_id") = "column_name" ` +
2928
                    `WHERE (${condition})`
2929
                )
2930
            })
2931
            .join(" UNION ALL ")
2932

2933
        const constraintsSql = Object.entries(dbTablesByCatalog)
2,745✔
2934
            .map(([TABLE_CATALOG, tables]) => {
2935
                const conditions = tables
2,745✔
2936
                    .map(
2937
                        ({ TABLE_NAME, TABLE_SCHEMA }) =>
2938
                            `("columnUsages"."TABLE_SCHEMA" = '${TABLE_SCHEMA}' AND "columnUsages"."TABLE_NAME" = '${TABLE_NAME}')`,
4,176✔
2939
                    )
2940
                    .join(" OR ")
2941

2942
                return (
2,745✔
2943
                    `SELECT "columnUsages".*, "tableConstraints"."CONSTRAINT_TYPE", "chk"."definition" ` +
2944
                    `FROM "${TABLE_CATALOG}"."INFORMATION_SCHEMA"."CONSTRAINT_COLUMN_USAGE" "columnUsages" ` +
2945
                    `INNER JOIN "${TABLE_CATALOG}"."INFORMATION_SCHEMA"."TABLE_CONSTRAINTS" "tableConstraints" ` +
2946
                    `ON ` +
2947
                    `"tableConstraints"."CONSTRAINT_NAME" = "columnUsages"."CONSTRAINT_NAME" AND ` +
2948
                    `"tableConstraints"."TABLE_SCHEMA" = "columnUsages"."TABLE_SCHEMA" AND ` +
2949
                    `"tableConstraints"."TABLE_NAME" = "columnUsages"."TABLE_NAME" ` +
2950
                    `LEFT JOIN "${TABLE_CATALOG}"."sys"."check_constraints" "chk" ` +
2951
                    `ON ` +
2952
                    `"chk"."object_id" = OBJECT_ID("columnUsages"."TABLE_CATALOG" + '.' + "columnUsages"."TABLE_SCHEMA" + '.' + "columnUsages"."CONSTRAINT_NAME") ` +
2953
                    `WHERE ` +
2954
                    `(${conditions}) AND ` +
2955
                    `"tableConstraints"."CONSTRAINT_TYPE" IN ('PRIMARY KEY', 'UNIQUE', 'CHECK')`
2956
                )
2957
            })
2958
            .join(" UNION ALL ")
2959

2960
        const foreignKeysSql = Object.entries(dbTablesByCatalog)
2,745✔
2961
            .map(([TABLE_CATALOG, tables]) => {
2962
                const conditions = tables
2,745✔
2963
                    .map(
2964
                        ({ TABLE_NAME, TABLE_SCHEMA }) =>
2965
                            `("s1"."name" = '${TABLE_SCHEMA}' AND "t1"."name" = '${TABLE_NAME}')`,
4,176✔
2966
                    )
2967
                    .join(" OR ")
2968

2969
                return (
2,745✔
2970
                    `SELECT "fk"."name" AS "FK_NAME", '${TABLE_CATALOG}' AS "TABLE_CATALOG", "s1"."name" AS "TABLE_SCHEMA", "t1"."name" AS "TABLE_NAME", ` +
2971
                    `"col1"."name" AS "COLUMN_NAME", "s2"."name" AS "REF_SCHEMA", "t2"."name" AS "REF_TABLE", "col2"."name" AS "REF_COLUMN", ` +
2972
                    `"fk"."delete_referential_action_desc" AS "ON_DELETE", "fk"."update_referential_action_desc" AS "ON_UPDATE" ` +
2973
                    `FROM "${TABLE_CATALOG}"."sys"."foreign_keys" "fk" ` +
2974
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."foreign_key_columns" "fkc" ON "fkc"."constraint_object_id" = "fk"."object_id" ` +
2975
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."tables" "t1" ON "t1"."object_id" = "fk"."parent_object_id" ` +
2976
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."schemas" "s1" ON "s1"."schema_id" = "t1"."schema_id" ` +
2977
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."tables" "t2" ON "t2"."object_id" = "fk"."referenced_object_id" ` +
2978
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."schemas" "s2" ON "s2"."schema_id" = "t2"."schema_id" ` +
2979
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."columns" "col1" ON "col1"."column_id" = "fkc"."parent_column_id" AND "col1"."object_id" = "fk"."parent_object_id" ` +
2980
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."columns" "col2" ON "col2"."column_id" = "fkc"."referenced_column_id" AND "col2"."object_id" = "fk"."referenced_object_id" ` +
2981
                    `WHERE (${conditions})`
2982
                )
2983
            })
2984
            .join(" UNION ALL ")
2985

2986
        const identityColumnsSql = Object.entries(dbTablesByCatalog)
2,745✔
2987
            .map(([TABLE_CATALOG, tables]) => {
2988
                const conditions = tables
2,745✔
2989
                    .map(
2990
                        ({ TABLE_NAME, TABLE_SCHEMA }) =>
2991
                            `("TABLE_SCHEMA" = '${TABLE_SCHEMA}' AND "TABLE_NAME" = '${TABLE_NAME}')`,
4,176✔
2992
                    )
2993
                    .join(" OR ")
2994

2995
                return (
2,745✔
2996
                    `SELECT "TABLE_CATALOG", "TABLE_SCHEMA", "COLUMN_NAME", "TABLE_NAME" ` +
2997
                    `FROM "${TABLE_CATALOG}"."INFORMATION_SCHEMA"."COLUMNS" ` +
2998
                    `WHERE ` +
2999
                    `EXISTS(SELECT 1 FROM "${TABLE_CATALOG}"."sys"."columns" "S" WHERE OBJECT_ID("TABLE_CATALOG" + '.' + "TABLE_SCHEMA" + '.' + "TABLE_NAME") = "S"."OBJECT_ID" AND "COLUMN_NAME" = "S"."NAME" AND "S"."is_identity" = 1) AND ` +
3000
                    `(${conditions})`
3001
                )
3002
            })
3003
            .join(" UNION ALL ")
3004

3005
        const dbCollationsSql = `SELECT "NAME", "COLLATION_NAME" FROM "sys"."databases"`
2,745✔
3006

3007
        const indicesSql = Object.entries(dbTablesByCatalog)
2,745✔
3008
            .map(([TABLE_CATALOG, tables]) => {
3009
                const conditions = tables
2,745✔
3010
                    .map(
3011
                        ({ TABLE_NAME, TABLE_SCHEMA }) =>
3012
                            `("s"."name" = '${TABLE_SCHEMA}' AND "t"."name" = '${TABLE_NAME}')`,
4,176✔
3013
                    )
3014
                    .join(" OR ")
3015

3016
                return (
2,745✔
3017
                    `SELECT '${TABLE_CATALOG}' AS "TABLE_CATALOG", "s"."name" AS "TABLE_SCHEMA", "t"."name" AS "TABLE_NAME", ` +
3018
                    `"ind"."name" AS "INDEX_NAME", "col"."name" AS "COLUMN_NAME", "ind"."is_unique" AS "IS_UNIQUE", "ind"."filter_definition" as "CONDITION" ` +
3019
                    `FROM "${TABLE_CATALOG}"."sys"."indexes" "ind" ` +
3020
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."index_columns" "ic" ON "ic"."object_id" = "ind"."object_id" AND "ic"."index_id" = "ind"."index_id" ` +
3021
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."columns" "col" ON "col"."object_id" = "ic"."object_id" AND "col"."column_id" = "ic"."column_id" ` +
3022
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."tables" "t" ON "t"."object_id" = "ind"."object_id" ` +
3023
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."schemas" "s" ON "s"."schema_id" = "t"."schema_id" ` +
3024
                    `WHERE ` +
3025
                    `"ind"."is_primary_key" = 0 AND "ind"."is_unique_constraint" = 0 AND "t"."is_ms_shipped" = 0 AND ` +
3026
                    `(${conditions})`
3027
                )
3028
            })
3029
            .join(" UNION ALL ")
3030

3031
        const [
3032
            dbColumns,
3033
            dbConstraints,
3034
            dbForeignKeys,
3035
            dbIdentityColumns,
3036
            dbCollations,
3037
            dbIndices,
3038
        ]: ObjectLiteral[][] = await Promise.all([
2,745✔
3039
            this.query(columnsSql),
3040
            this.query(constraintsSql),
3041
            this.query(foreignKeysSql),
3042
            this.query(identityColumnsSql),
3043
            this.query(dbCollationsSql),
3044
            this.query(indicesSql),
3045
        ])
3046

3047
        // create table schemas for loaded tables
3048
        return await Promise.all(
2,745✔
3049
            dbTables.map(async (dbTable) => {
3050
                const table = new Table()
4,176✔
3051

3052
                const getSchemaFromKey = (dbObject: any, key: string) => {
4,176✔
3053
                    return dbObject[key] === currentSchema &&
5,526✔
3054
                        (!this.driver.options.schema ||
3055
                            this.driver.options.schema === currentSchema)
3056
                        ? undefined
3057
                        : dbObject[key]
3058
                }
3059

3060
                // We do not need to join schema and database names, when db or schema is by default.
3061
                const db =
3062
                    dbTable["TABLE_CATALOG"] === currentDatabase
4,176✔
3063
                        ? undefined
3064
                        : dbTable["TABLE_CATALOG"]
3065
                const schema = getSchemaFromKey(dbTable, "TABLE_SCHEMA")
4,176✔
3066
                table.database = dbTable["TABLE_CATALOG"]
4,176✔
3067
                table.schema = dbTable["TABLE_SCHEMA"]
4,176✔
3068
                table.name = this.driver.buildTableName(
4,176✔
3069
                    dbTable["TABLE_NAME"],
3070
                    schema,
3071
                    db,
3072
                )
3073

3074
                const defaultCollation = dbCollations.find(
4,176✔
3075
                    (dbCollation) =>
3076
                        dbCollation["NAME"] === dbTable["TABLE_CATALOG"],
9,423✔
3077
                )!
3078

3079
                // create columns from the loaded columns
3080
                table.columns = await Promise.all(
4,176✔
3081
                    dbColumns
3082
                        .filter(
3083
                            (dbColumn) =>
3084
                                dbColumn["TABLE_NAME"] ===
46,224✔
3085
                                    dbTable["TABLE_NAME"] &&
3086
                                dbColumn["TABLE_SCHEMA"] ===
3087
                                    dbTable["TABLE_SCHEMA"] &&
3088
                                dbColumn["TABLE_CATALOG"] ===
3089
                                    dbTable["TABLE_CATALOG"],
3090
                        )
3091
                        .map(async (dbColumn) => {
3092
                            const columnConstraints = dbConstraints.filter(
14,787✔
3093
                                (dbConstraint) =>
3094
                                    dbConstraint["TABLE_NAME"] ===
81,729✔
3095
                                        dbColumn["TABLE_NAME"] &&
3096
                                    dbConstraint["TABLE_SCHEMA"] ===
3097
                                        dbColumn["TABLE_SCHEMA"] &&
3098
                                    dbConstraint["TABLE_CATALOG"] ===
3099
                                        dbColumn["TABLE_CATALOG"] &&
3100
                                    dbConstraint["COLUMN_NAME"] ===
3101
                                        dbColumn["COLUMN_NAME"],
3102
                            )
3103

3104
                            const uniqueConstraints = columnConstraints.filter(
14,787✔
3105
                                (constraint) =>
3106
                                    constraint["CONSTRAINT_TYPE"] === "UNIQUE",
7,803✔
3107
                            )
3108
                            const isConstraintComposite =
3109
                                uniqueConstraints.every((uniqueConstraint) => {
14,787✔
3110
                                    return dbConstraints.some(
2,565✔
3111
                                        (dbConstraint) =>
3112
                                            dbConstraint["CONSTRAINT_TYPE"] ===
11,100✔
3113
                                                "UNIQUE" &&
3114
                                            dbConstraint["CONSTRAINT_NAME"] ===
3115
                                                uniqueConstraint[
3116
                                                    "CONSTRAINT_NAME"
3117
                                                ] &&
3118
                                            dbConstraint["TABLE_SCHEMA"] ===
3119
                                                dbColumn["TABLE_SCHEMA"] &&
3120
                                            dbConstraint["TABLE_CATALOG"] ===
3121
                                                dbColumn["TABLE_CATALOG"] &&
3122
                                            dbConstraint["COLUMN_NAME"] !==
3123
                                                dbColumn["COLUMN_NAME"],
3124
                                    )
3125
                                })
3126

3127
                            const isGenerated = !!dbIdentityColumns.find(
14,787✔
3128
                                (column) =>
3129
                                    column["TABLE_NAME"] ===
24,120✔
3130
                                        dbColumn["TABLE_NAME"] &&
3131
                                    column["TABLE_SCHEMA"] ===
3132
                                        dbColumn["TABLE_SCHEMA"] &&
3133
                                    column["TABLE_CATALOG"] ===
3134
                                        dbColumn["TABLE_CATALOG"] &&
3135
                                    column["COLUMN_NAME"] ===
3136
                                        dbColumn["COLUMN_NAME"],
3137
                            )
3138

3139
                            const tableColumn = new TableColumn()
14,787✔
3140
                            tableColumn.name = dbColumn["COLUMN_NAME"]
14,787✔
3141
                            tableColumn.type =
14,787✔
3142
                                dbColumn["DATA_TYPE"].toLowerCase()
3143

3144
                            // check only columns that have length property
3145
                            if (
14,787✔
3146
                                this.driver.withLengthColumnTypes.indexOf(
22,851✔
3147
                                    tableColumn.type as ColumnType,
3148
                                ) !== -1 &&
3149
                                dbColumn["CHARACTER_MAXIMUM_LENGTH"]
3150
                            ) {
3151
                                const length =
3152
                                    dbColumn[
8,064✔
3153
                                        "CHARACTER_MAXIMUM_LENGTH"
3154
                                    ].toString()
3155
                                if (length === "-1") {
8,064✔
3156
                                    tableColumn.length = "MAX"
36✔
3157
                                } else {
3158
                                    tableColumn.length =
8,028✔
3159
                                        !this.isDefaultColumnLength(
8,028✔
3160
                                            table,
3161
                                            tableColumn,
3162
                                            length,
3163
                                        )
3164
                                            ? length
3165
                                            : ""
3166
                                }
3167
                            }
3168

3169
                            if (
14,787✔
3170
                                tableColumn.type === "decimal" ||
29,529✔
3171
                                tableColumn.type === "numeric"
3172
                            ) {
3173
                                if (
63✔
3174
                                    dbColumn["NUMERIC_PRECISION"] !== null &&
126✔
3175
                                    !this.isDefaultColumnPrecision(
3176
                                        table,
3177
                                        tableColumn,
3178
                                        dbColumn["NUMERIC_PRECISION"],
3179
                                    )
3180
                                )
3181
                                    tableColumn.precision =
36✔
3182
                                        dbColumn["NUMERIC_PRECISION"]
3183
                                if (
63✔
3184
                                    dbColumn["NUMERIC_SCALE"] !== null &&
126✔
3185
                                    !this.isDefaultColumnScale(
3186
                                        table,
3187
                                        tableColumn,
3188
                                        dbColumn["NUMERIC_SCALE"],
3189
                                    )
3190
                                )
3191
                                    tableColumn.scale =
36✔
3192
                                        dbColumn["NUMERIC_SCALE"]
3193
                            }
3194

3195
                            if (tableColumn.type === "nvarchar") {
14,787✔
3196
                                // Check if this is an enum
3197
                                const columnCheckConstraints =
3198
                                    columnConstraints.filter(
7,065✔
3199
                                        (constraint) =>
3200
                                            constraint["CONSTRAINT_TYPE"] ===
2,295✔
3201
                                            "CHECK",
3202
                                    )
3203
                                if (columnCheckConstraints.length) {
7,065✔
3204
                                    // const isEnumRegexp = new RegExp("^\\(\\[" + tableColumn.name + "\\]='[^']+'(?: OR \\[" + tableColumn.name + "\\]='[^']+')*\\)$");
3205
                                    for (const checkConstraint of columnCheckConstraints) {
99✔
3206
                                        if (
99✔
3207
                                            this.isEnumCheckConstraint(
3208
                                                checkConstraint[
3209
                                                    "CONSTRAINT_NAME"
3210
                                                ],
3211
                                            )
3212
                                        ) {
3213
                                            // This is an enum constraint, make column into an enum
3214
                                            tableColumn.enum = []
45✔
3215
                                            const enumValueRegexp = new RegExp(
45✔
3216
                                                "\\[" +
3217
                                                    tableColumn.name +
3218
                                                    "\\]='([^']+)'",
3219
                                                "g",
3220
                                            )
3221
                                            let result
3222
                                            while (
45✔
3223
                                                (result = enumValueRegexp.exec(
3224
                                                    checkConstraint[
3225
                                                        "definition"
3226
                                                    ],
3227
                                                )) !== null
3228
                                            ) {
3229
                                                tableColumn.enum.unshift(
117✔
3230
                                                    result[1],
3231
                                                )
3232
                                            }
3233
                                            // Skip other column constraints
3234
                                            break
45✔
3235
                                        }
3236
                                    }
3237
                                }
3238
                            }
3239

3240
                            const primaryConstraint = columnConstraints.find(
14,787✔
3241
                                (constraint) =>
3242
                                    constraint["CONSTRAINT_TYPE"] ===
7,731✔
3243
                                    "PRIMARY KEY",
3244
                            )
3245
                            if (primaryConstraint) {
14,787✔
3246
                                tableColumn.isPrimary = true
4,347✔
3247
                                // find another columns involved in primary key constraint
3248
                                const anotherPrimaryConstraints =
3249
                                    dbConstraints.filter(
4,347✔
3250
                                        (constraint) =>
3251
                                            constraint["TABLE_NAME"] ===
23,823✔
3252
                                                dbColumn["TABLE_NAME"] &&
3253
                                            constraint["TABLE_SCHEMA"] ===
3254
                                                dbColumn["TABLE_SCHEMA"] &&
3255
                                            constraint["TABLE_CATALOG"] ===
3256
                                                dbColumn["TABLE_CATALOG"] &&
3257
                                            constraint["COLUMN_NAME"] !==
3258
                                                dbColumn["COLUMN_NAME"] &&
3259
                                            constraint["CONSTRAINT_TYPE"] ===
3260
                                                "PRIMARY KEY",
3261
                                    )
3262

3263
                                // collect all column names
3264
                                const columnNames =
3265
                                    anotherPrimaryConstraints.map(
4,347✔
3266
                                        (constraint) =>
3267
                                            constraint["COLUMN_NAME"],
522✔
3268
                                    )
3269
                                columnNames.push(dbColumn["COLUMN_NAME"])
4,347✔
3270

3271
                                // build default primary key constraint name
3272
                                const pkName =
3273
                                    this.connection.namingStrategy.primaryKeyName(
4,347✔
3274
                                        table,
3275
                                        columnNames,
3276
                                    )
3277

3278
                                // if primary key has user-defined constraint name, write it in table column
3279
                                if (
4,347✔
3280
                                    primaryConstraint["CONSTRAINT_NAME"] !==
3281
                                    pkName
3282
                                ) {
3283
                                    tableColumn.primaryKeyConstraintName =
234✔
3284
                                        primaryConstraint["CONSTRAINT_NAME"]
3285
                                }
3286
                            }
3287

3288
                            tableColumn.default =
14,787✔
3289
                                dbColumn["COLUMN_DEFAULT"] !== null &&
30,528✔
3290
                                dbColumn["COLUMN_DEFAULT"] !== undefined
3291
                                    ? this.removeParenthesisFromDefault(
3292
                                          dbColumn["COLUMN_DEFAULT"],
3293
                                      )
3294
                                    : undefined
3295
                            tableColumn.isNullable =
14,787✔
3296
                                dbColumn["IS_NULLABLE"] === "YES"
3297
                            tableColumn.isUnique =
14,787✔
3298
                                uniqueConstraints.length > 0 &&
17,352✔
3299
                                !isConstraintComposite
3300
                            tableColumn.isGenerated = isGenerated
14,787✔
3301
                            if (isGenerated)
14,787✔
3302
                                tableColumn.generationStrategy = "increment"
2,286✔
3303
                            if (tableColumn.default === "newsequentialid()") {
14,787✔
3304
                                tableColumn.isGenerated = true
63✔
3305
                                tableColumn.generationStrategy = "uuid"
63✔
3306
                                tableColumn.default = undefined
63✔
3307
                            }
3308

3309
                            // todo: unable to get default charset
3310
                            // tableColumn.charset = dbColumn["CHARACTER_SET_NAME"];
3311
                            if (dbColumn["COLLATION_NAME"])
14,787✔
3312
                                tableColumn.collation =
7,857✔
3313
                                    dbColumn["COLLATION_NAME"] ===
7,857✔
3314
                                    defaultCollation["COLLATION_NAME"]
3315
                                        ? undefined
3316
                                        : dbColumn["COLLATION_NAME"]
3317

3318
                            if (
14,787✔
3319
                                tableColumn.type === "datetime2" ||
44,136✔
3320
                                tableColumn.type === "time" ||
3321
                                tableColumn.type === "datetimeoffset"
3322
                            ) {
3323
                                tableColumn.precision =
144✔
3324
                                    !this.isDefaultColumnPrecision(
144✔
3325
                                        table,
3326
                                        tableColumn,
3327
                                        dbColumn["DATETIME_PRECISION"],
3328
                                    )
3329
                                        ? dbColumn["DATETIME_PRECISION"]
3330
                                        : undefined
3331
                            }
3332

3333
                            if (
14,787✔
3334
                                dbColumn["is_persisted"] !== null &&
15,633✔
3335
                                dbColumn["is_persisted"] !== undefined &&
3336
                                dbColumn["definition"]
3337
                            ) {
3338
                                tableColumn.generatedType =
423✔
3339
                                    dbColumn["is_persisted"] === true
423✔
3340
                                        ? "STORED"
3341
                                        : "VIRTUAL"
3342
                                // We cannot relay on information_schema.columns.generation_expression, because it is formatted different.
3343
                                const asExpressionQuery =
3344
                                    this.selectTypeormMetadataSql({
423✔
3345
                                        database: dbTable["TABLE_CATALOG"],
3346
                                        schema: dbTable["TABLE_SCHEMA"],
3347
                                        table: dbTable["TABLE_NAME"],
3348
                                        type: MetadataTableType.GENERATED_COLUMN,
3349
                                        name: tableColumn.name,
3350
                                    })
3351

3352
                                const results = await this.query(
423✔
3353
                                    asExpressionQuery.query,
3354
                                    asExpressionQuery.parameters,
3355
                                )
3356
                                if (results[0] && results[0].value) {
423!
3357
                                    tableColumn.asExpression = results[0].value
423✔
3358
                                } else {
3359
                                    tableColumn.asExpression = ""
×
3360
                                }
3361
                            }
3362

3363
                            return tableColumn
14,787✔
3364
                        }),
3365
                )
3366

3367
                // find unique constraints of table, group them by constraint name and build TableUnique.
3368
                const tableUniqueConstraints = OrmUtils.uniq(
4,176✔
3369
                    dbConstraints.filter(
3370
                        (dbConstraint) =>
3371
                            dbConstraint["TABLE_NAME"] ===
23,202✔
3372
                                dbTable["TABLE_NAME"] &&
3373
                            dbConstraint["TABLE_SCHEMA"] ===
3374
                                dbTable["TABLE_SCHEMA"] &&
3375
                            dbConstraint["TABLE_CATALOG"] ===
3376
                                dbTable["TABLE_CATALOG"] &&
3377
                            dbConstraint["CONSTRAINT_TYPE"] === "UNIQUE",
3378
                    ),
3379
                    (dbConstraint) => dbConstraint["CONSTRAINT_NAME"],
4,347✔
3380
                )
3381

3382
                table.uniques = tableUniqueConstraints.map((constraint) => {
4,176✔
3383
                    const uniques = dbConstraints.filter(
1,791✔
3384
                        (dbC) =>
3385
                            dbC["CONSTRAINT_NAME"] ===
11,331✔
3386
                            constraint["CONSTRAINT_NAME"],
3387
                    )
3388
                    return new TableUnique({
1,791✔
3389
                        name: constraint["CONSTRAINT_NAME"],
3390
                        columnNames: uniques.map((u) => u["COLUMN_NAME"]),
2,592✔
3391
                    })
3392
                })
3393

3394
                // find check constraints of table, group them by constraint name and build TableCheck.
3395
                const tableCheckConstraints = OrmUtils.uniq(
4,176✔
3396
                    dbConstraints.filter(
3397
                        (dbConstraint) =>
3398
                            dbConstraint["TABLE_NAME"] ===
23,202✔
3399
                                dbTable["TABLE_NAME"] &&
3400
                            dbConstraint["TABLE_SCHEMA"] ===
3401
                                dbTable["TABLE_SCHEMA"] &&
3402
                            dbConstraint["TABLE_CATALOG"] ===
3403
                                dbTable["TABLE_CATALOG"] &&
3404
                            dbConstraint["CONSTRAINT_TYPE"] === "CHECK",
3405
                    ),
3406
                    (dbConstraint) => dbConstraint["CONSTRAINT_NAME"],
981✔
3407
                )
3408

3409
                table.checks = tableCheckConstraints
4,176✔
3410
                    .filter(
3411
                        (constraint) =>
3412
                            !this.isEnumCheckConstraint(
828✔
3413
                                constraint["CONSTRAINT_NAME"],
3414
                            ),
3415
                    )
3416
                    .map((constraint) => {
3417
                        const checks = dbConstraints.filter(
783✔
3418
                            (dbC) =>
3419
                                dbC["CONSTRAINT_NAME"] ===
5,472✔
3420
                                constraint["CONSTRAINT_NAME"],
3421
                        )
3422
                        return new TableCheck({
783✔
3423
                            name: constraint["CONSTRAINT_NAME"],
3424
                            columnNames: checks.map((c) => c["COLUMN_NAME"]),
819✔
3425
                            expression: constraint["definition"],
3426
                        })
3427
                    })
3428

3429
                // find foreign key constraints of table, group them by constraint name and build TableForeignKey.
3430
                const tableForeignKeyConstraints = OrmUtils.uniq(
4,176✔
3431
                    dbForeignKeys.filter(
3432
                        (dbForeignKey) =>
3433
                            dbForeignKey["TABLE_NAME"] ===
5,463✔
3434
                                dbTable["TABLE_NAME"] &&
3435
                            dbForeignKey["TABLE_SCHEMA"] ===
3436
                                dbTable["TABLE_SCHEMA"] &&
3437
                            dbForeignKey["TABLE_CATALOG"] ===
3438
                                dbTable["TABLE_CATALOG"],
3439
                    ),
3440
                    (dbForeignKey) => dbForeignKey["FK_NAME"],
1,836✔
3441
                )
3442

3443
                table.foreignKeys = tableForeignKeyConstraints.map(
4,176✔
3444
                    (dbForeignKey) => {
3445
                        const foreignKeys = dbForeignKeys.filter(
1,350✔
3446
                            (dbFk) =>
3447
                                dbFk["FK_NAME"] === dbForeignKey["FK_NAME"],
3,150✔
3448
                        )
3449

3450
                        // if referenced table located in currently used db and schema, we don't need to concat db and schema names to table name.
3451
                        const db =
3452
                            dbForeignKey["TABLE_CATALOG"] === currentDatabase
1,350✔
3453
                                ? undefined
3454
                                : dbForeignKey["TABLE_CATALOG"]
3455
                        const schema = getSchemaFromKey(
1,350✔
3456
                            dbForeignKey,
3457
                            "REF_SCHEMA",
3458
                        )
3459
                        const referencedTableName = this.driver.buildTableName(
1,350✔
3460
                            dbForeignKey["REF_TABLE"],
3461
                            schema,
3462
                            db,
3463
                        )
3464

3465
                        return new TableForeignKey({
1,350✔
3466
                            name: dbForeignKey["FK_NAME"],
3467
                            columnNames: foreignKeys.map(
3468
                                (dbFk) => dbFk["COLUMN_NAME"],
1,368✔
3469
                            ),
3470
                            referencedDatabase: dbForeignKey["TABLE_CATALOG"],
3471
                            referencedSchema: dbForeignKey["REF_SCHEMA"],
3472
                            referencedTableName: referencedTableName,
3473
                            referencedColumnNames: foreignKeys.map(
3474
                                (dbFk) => dbFk["REF_COLUMN"],
1,368✔
3475
                            ),
3476
                            onDelete: dbForeignKey["ON_DELETE"].replace(
3477
                                "_",
3478
                                " ",
3479
                            ), // SqlServer returns NO_ACTION, instead of NO ACTION
3480
                            onUpdate: dbForeignKey["ON_UPDATE"].replace(
3481
                                "_",
3482
                                " ",
3483
                            ), // SqlServer returns NO_ACTION, instead of NO ACTION
3484
                        })
3485
                    },
3486
                )
3487

3488
                // find index constraints of table, group them by constraint name and build TableIndex.
3489
                const tableIndexConstraints = OrmUtils.uniq(
4,176✔
3490
                    dbIndices.filter(
3491
                        (dbIndex) =>
3492
                            dbIndex["TABLE_NAME"] === dbTable["TABLE_NAME"] &&
3,159✔
3493
                            dbIndex["TABLE_SCHEMA"] ===
3494
                                dbTable["TABLE_SCHEMA"] &&
3495
                            dbIndex["TABLE_CATALOG"] ===
3496
                                dbTable["TABLE_CATALOG"],
3497
                    ),
3498
                    (dbIndex) => dbIndex["INDEX_NAME"],
2,592✔
3499
                )
3500

3501
                table.indices = tableIndexConstraints.map((constraint) => {
4,176✔
3502
                    const indices = dbIndices.filter((index) => {
1,260✔
3503
                        return (
4,194✔
3504
                            index["TABLE_CATALOG"] ===
15,561✔
3505
                                constraint["TABLE_CATALOG"] &&
3506
                            index["TABLE_SCHEMA"] ===
3507
                                constraint["TABLE_SCHEMA"] &&
3508
                            index["TABLE_NAME"] === constraint["TABLE_NAME"] &&
3509
                            index["INDEX_NAME"] === constraint["INDEX_NAME"]
3510
                        )
3511
                    })
3512
                    return new TableIndex(<TableIndexOptions>{
1,260✔
3513
                        table: table,
3514
                        name: constraint["INDEX_NAME"],
3515
                        columnNames: indices.map((i) => i["COLUMN_NAME"]),
1,458✔
3516
                        isUnique: constraint["IS_UNIQUE"],
3517
                        where: constraint["CONDITION"],
3518
                    })
3519
                })
3520

3521
                return table
4,176✔
3522
            }),
3523
        )
3524
    }
3525

3526
    /**
3527
     * Builds and returns SQL for create table.
3528
     */
3529
    protected createTableSql(table: Table, createForeignKeys?: boolean): Query {
3530
        const columnDefinitions = table.columns
42,408✔
3531
            .map((column) =>
3532
                this.buildCreateColumnSql(table, column, false, true),
137,016✔
3533
            )
3534
            .join(", ")
3535
        let sql = `CREATE TABLE ${this.escapePath(table)} (${columnDefinitions}`
42,408✔
3536

3537
        table.columns
42,408✔
3538
            .filter((column) => column.isUnique)
137,016✔
3539
            .forEach((column) => {
3540
                const isUniqueExist = table.uniques.some(
2,718✔
3541
                    (unique) =>
3542
                        unique.columnNames.length === 1 &&
3,294✔
3543
                        unique.columnNames[0] === column.name,
3544
                )
3545
                if (!isUniqueExist)
2,718✔
3546
                    table.uniques.push(
36✔
3547
                        new TableUnique({
3548
                            name: this.connection.namingStrategy.uniqueConstraintName(
3549
                                table,
3550
                                [column.name],
3551
                            ),
3552
                            columnNames: [column.name],
3553
                        }),
3554
                    )
3555
            })
3556

3557
        if (table.uniques.length > 0) {
42,408✔
3558
            const uniquesSql = table.uniques
2,484✔
3559
                .map((unique) => {
3560
                    const uniqueName = unique.name
3,654✔
3561
                        ? unique.name
3562
                        : this.connection.namingStrategy.uniqueConstraintName(
3563
                              table,
3564
                              unique.columnNames,
3565
                          )
3566
                    const columnNames = unique.columnNames
3,654✔
3567
                        .map((columnName) => `"${columnName}"`)
4,905✔
3568
                        .join(", ")
3569
                    return `CONSTRAINT "${uniqueName}" UNIQUE (${columnNames})`
3,654✔
3570
                })
3571
                .join(", ")
3572

3573
            sql += `, ${uniquesSql}`
2,484✔
3574
        }
3575

3576
        if (table.checks.length > 0) {
42,408✔
3577
            const checksSql = table.checks
630✔
3578
                .map((check) => {
3579
                    const checkName = check.name
639✔
3580
                        ? check.name
3581
                        : this.connection.namingStrategy.checkConstraintName(
3582
                              table,
3583
                              check.expression!,
3584
                          )
3585
                    return `CONSTRAINT "${checkName}" CHECK (${check.expression})`
639✔
3586
                })
3587
                .join(", ")
3588

3589
            sql += `, ${checksSql}`
630✔
3590
        }
3591

3592
        if (table.foreignKeys.length > 0 && createForeignKeys) {
42,408✔
3593
            const foreignKeysSql = table.foreignKeys
36✔
3594
                .map((fk) => {
3595
                    const columnNames = fk.columnNames
45✔
3596
                        .map((columnName) => `"${columnName}"`)
54✔
3597
                        .join(", ")
3598
                    if (!fk.name)
45✔
3599
                        fk.name = this.connection.namingStrategy.foreignKeyName(
27✔
3600
                            table,
3601
                            fk.columnNames,
3602
                            this.getTablePath(fk),
3603
                            fk.referencedColumnNames,
3604
                        )
3605
                    const referencedColumnNames = fk.referencedColumnNames
45✔
3606
                        .map((columnName) => `"${columnName}"`)
54✔
3607
                        .join(", ")
3608

3609
                    let constraint = `CONSTRAINT "${
45✔
3610
                        fk.name
3611
                    }" FOREIGN KEY (${columnNames}) REFERENCES ${this.escapePath(
3612
                        this.getTablePath(fk),
3613
                    )} (${referencedColumnNames})`
3614
                    if (fk.onDelete) constraint += ` ON DELETE ${fk.onDelete}`
45✔
3615
                    if (fk.onUpdate) constraint += ` ON UPDATE ${fk.onUpdate}`
45✔
3616

3617
                    return constraint
45✔
3618
                })
3619
                .join(", ")
3620

3621
            sql += `, ${foreignKeysSql}`
36✔
3622
        }
3623

3624
        const primaryColumns = table.columns.filter(
42,408✔
3625
            (column) => column.isPrimary,
137,016✔
3626
        )
3627
        if (primaryColumns.length > 0) {
42,408✔
3628
            const primaryKeyName = primaryColumns[0].primaryKeyConstraintName
42,291✔
3629
                ? primaryColumns[0].primaryKeyConstraintName
3630
                : this.connection.namingStrategy.primaryKeyName(
3631
                      table,
3632
                      primaryColumns.map((column) => column.name),
53,253✔
3633
                  )
3634

3635
            const columnNames = primaryColumns
42,291✔
3636
                .map((column) => `"${column.name}"`)
53,469✔
3637
                .join(", ")
3638
            sql += `, CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNames})`
42,291✔
3639
        }
3640

3641
        sql += `)`
42,408✔
3642

3643
        return new Query(sql)
42,408✔
3644
    }
3645

3646
    /**
3647
     * Builds drop table sql.
3648
     */
3649
    protected dropTableSql(
3650
        tableOrName: Table | string,
3651
        ifExist?: boolean,
3652
    ): Query {
3653
        const query = ifExist
42,408!
3654
            ? `DROP TABLE IF EXISTS ${this.escapePath(tableOrName)}`
3655
            : `DROP TABLE ${this.escapePath(tableOrName)}`
3656
        return new Query(query)
42,408✔
3657
    }
3658

3659
    protected createViewSql(view: View): Query {
3660
        const parsedName = this.driver.parseTableName(view)
72✔
3661

3662
        // Can't use `escapePath` here because `CREATE VIEW` does not accept database names.
3663
        const viewIdentifier = parsedName.schema
72!
3664
            ? `"${parsedName.schema}"."${parsedName.tableName}"`
3665
            : `"${parsedName.tableName}"`
3666

3667
        if (typeof view.expression === "string") {
72✔
3668
            return new Query(
18✔
3669
                `CREATE VIEW ${viewIdentifier} AS ${view.expression}`,
3670
            )
3671
        } else {
3672
            return new Query(
54✔
3673
                `CREATE VIEW ${viewIdentifier} AS ${view
3674
                    .expression(this.connection)
3675
                    .getQuery()}`,
3676
            )
3677
        }
3678
    }
3679

3680
    protected async insertViewDefinitionSql(view: View): Promise<Query> {
3681
        const parsedTableName = this.driver.parseTableName(view)
72✔
3682

3683
        if (!parsedTableName.schema) {
72!
3684
            parsedTableName.schema = await this.getCurrentSchema()
×
3685
        }
3686

3687
        const expression =
3688
            typeof view.expression === "string"
72✔
3689
                ? view.expression.trim()
3690
                : view.expression(this.connection).getQuery()
3691
        return this.insertTypeormMetadataSql({
72✔
3692
            type: MetadataTableType.VIEW,
3693
            database: parsedTableName.database,
3694
            schema: parsedTableName.schema,
3695
            name: parsedTableName.tableName,
3696
            value: expression,
3697
        })
3698
    }
3699

3700
    /**
3701
     * Builds drop view sql.
3702
     */
3703
    protected dropViewSql(viewOrPath: View | string): Query {
3704
        return new Query(`DROP VIEW ${this.escapePath(viewOrPath)}`)
72✔
3705
    }
3706

3707
    /**
3708
     * Builds remove view sql.
3709
     */
3710
    protected async deleteViewDefinitionSql(
3711
        viewOrPath: View | string,
3712
    ): Promise<Query> {
3713
        const parsedTableName = this.driver.parseTableName(viewOrPath)
72✔
3714

3715
        if (!parsedTableName.schema) {
72!
3716
            parsedTableName.schema = await this.getCurrentSchema()
×
3717
        }
3718

3719
        return this.deleteTypeormMetadataSql({
72✔
3720
            type: MetadataTableType.VIEW,
3721
            database: parsedTableName.database,
3722
            schema: parsedTableName.schema,
3723
            name: parsedTableName.tableName,
3724
        })
3725
    }
3726

3727
    /**
3728
     * Builds create index sql.
3729
     */
3730
    protected createIndexSql(table: Table, index: TableIndex): Query {
3731
        const columns = index.columnNames
19,278✔
3732
            .map((columnName) => `"${columnName}"`)
22,482✔
3733
            .join(", ")
3734
        return new Query(
19,278✔
3735
            `CREATE ${index.isUnique ? "UNIQUE " : ""}INDEX "${
19,278✔
3736
                index.name
3737
            }" ON ${this.escapePath(table)} (${columns}) ${
3738
                index.where ? "WHERE " + index.where : ""
19,278✔
3739
            }`,
3740
        )
3741
    }
3742

3743
    /**
3744
     * Builds drop index sql.
3745
     */
3746
    protected dropIndexSql(
3747
        table: Table,
3748
        indexOrName: TableIndex | string,
3749
    ): Query {
3750
        let indexName = InstanceChecker.isTableIndex(indexOrName)
19,278!
3751
            ? indexOrName.name
3752
            : indexOrName
3753
        return new Query(
19,278✔
3754
            `DROP INDEX "${indexName}" ON ${this.escapePath(table)}`,
3755
        )
3756
    }
3757

3758
    /**
3759
     * Builds create primary key sql.
3760
     */
3761
    protected createPrimaryKeySql(
3762
        table: Table,
3763
        columnNames: string[],
3764
        constraintName?: string,
3765
    ): Query {
3766
        const primaryKeyName = constraintName
45!
3767
            ? constraintName
3768
            : this.connection.namingStrategy.primaryKeyName(table, columnNames)
3769

3770
        const columnNamesString = columnNames
45✔
3771
            .map((columnName) => `"${columnName}"`)
54✔
3772
            .join(", ")
3773
        return new Query(
45✔
3774
            `ALTER TABLE ${this.escapePath(
3775
                table,
3776
            )} ADD CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNamesString})`,
3777
        )
3778
    }
3779

3780
    /**
3781
     * Builds drop primary key sql.
3782
     */
3783
    protected dropPrimaryKeySql(table: Table): Query {
3784
        const columnNames = table.primaryColumns.map((column) => column.name)
54✔
3785
        const constraintName = table.primaryColumns[0].primaryKeyConstraintName
45✔
3786
        const primaryKeyName = constraintName
45!
3787
            ? constraintName
3788
            : this.connection.namingStrategy.primaryKeyName(table, columnNames)
3789

3790
        return new Query(
45✔
3791
            `ALTER TABLE ${this.escapePath(
3792
                table,
3793
            )} DROP CONSTRAINT "${primaryKeyName}"`,
3794
        )
3795
    }
3796

3797
    /**
3798
     * Builds create unique constraint sql.
3799
     */
3800
    protected createUniqueConstraintSql(
3801
        table: Table,
3802
        uniqueConstraint: TableUnique,
3803
    ): Query {
3804
        const columnNames = uniqueConstraint.columnNames
99✔
3805
            .map((column) => `"` + column + `"`)
162✔
3806
            .join(", ")
3807
        return new Query(
99✔
3808
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
3809
                uniqueConstraint.name
3810
            }" UNIQUE (${columnNames})`,
3811
        )
3812
    }
3813

3814
    /**
3815
     * Builds drop unique constraint sql.
3816
     */
3817
    protected dropUniqueConstraintSql(
3818
        table: Table,
3819
        uniqueOrName: TableUnique | string,
3820
    ): Query {
3821
        const uniqueName = InstanceChecker.isTableUnique(uniqueOrName)
99!
3822
            ? uniqueOrName.name
3823
            : uniqueOrName
3824
        return new Query(
99✔
3825
            `ALTER TABLE ${this.escapePath(
3826
                table,
3827
            )} DROP CONSTRAINT "${uniqueName}"`,
3828
        )
3829
    }
3830

3831
    /**
3832
     * Builds create check constraint sql.
3833
     */
3834
    protected createCheckConstraintSql(
3835
        table: Table,
3836
        checkConstraint: TableCheck,
3837
    ): Query {
3838
        return new Query(
108✔
3839
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
3840
                checkConstraint.name
3841
            }" CHECK (${checkConstraint.expression})`,
3842
        )
3843
    }
3844

3845
    /**
3846
     * Builds drop check constraint sql.
3847
     */
3848
    protected dropCheckConstraintSql(
3849
        table: Table,
3850
        checkOrName: TableCheck | string,
3851
    ): Query {
3852
        const checkName = InstanceChecker.isTableCheck(checkOrName)
108!
3853
            ? checkOrName.name
3854
            : checkOrName
3855
        return new Query(
108✔
3856
            `ALTER TABLE ${this.escapePath(
3857
                table,
3858
            )} DROP CONSTRAINT "${checkName}"`,
3859
        )
3860
    }
3861

3862
    /**
3863
     * Builds create foreign key sql.
3864
     */
3865
    protected createForeignKeySql(
3866
        table: Table,
3867
        foreignKey: TableForeignKey,
3868
    ): Query {
3869
        const columnNames = foreignKey.columnNames
28,242✔
3870
            .map((column) => `"` + column + `"`)
32,634✔
3871
            .join(", ")
3872
        const referencedColumnNames = foreignKey.referencedColumnNames
28,242✔
3873
            .map((column) => `"` + column + `"`)
32,634✔
3874
            .join(",")
3875
        let sql =
3876
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
28,242✔
3877
                foreignKey.name
3878
            }" FOREIGN KEY (${columnNames}) ` +
3879
            `REFERENCES ${this.escapePath(
3880
                this.getTablePath(foreignKey),
3881
            )}(${referencedColumnNames})`
3882
        if (foreignKey.onDelete) sql += ` ON DELETE ${foreignKey.onDelete}`
28,242✔
3883
        if (foreignKey.onUpdate) sql += ` ON UPDATE ${foreignKey.onUpdate}`
28,242✔
3884

3885
        return new Query(sql)
28,242✔
3886
    }
3887

3888
    /**
3889
     * Builds drop foreign key sql.
3890
     */
3891
    protected dropForeignKeySql(
3892
        table: Table,
3893
        foreignKeyOrName: TableForeignKey | string,
3894
    ): Query {
3895
        const foreignKeyName = InstanceChecker.isTableForeignKey(
28,287!
3896
            foreignKeyOrName,
3897
        )
3898
            ? foreignKeyOrName.name
3899
            : foreignKeyOrName
3900
        return new Query(
28,287✔
3901
            `ALTER TABLE ${this.escapePath(
3902
                table,
3903
            )} DROP CONSTRAINT "${foreignKeyName}"`,
3904
        )
3905
    }
3906

3907
    /**
3908
     * Escapes given table or View path.
3909
     */
3910
    protected escapePath(target: Table | View | string): string {
3911
        const { database, schema, tableName } =
3912
            this.driver.parseTableName(target)
211,158✔
3913

3914
        if (database && database !== this.driver.database) {
211,158✔
3915
            if (schema && schema !== this.driver.searchSchema) {
432✔
3916
                return `"${database}"."${schema}"."${tableName}"`
378✔
3917
            }
3918

3919
            return `"${database}".."${tableName}"`
54✔
3920
        }
3921

3922
        if (schema && schema !== this.driver.searchSchema) {
210,726✔
3923
            return `"${schema}"."${tableName}"`
630✔
3924
        }
3925

3926
        return `"${tableName}"`
210,096✔
3927
    }
3928

3929
    /**
3930
     * Concat database name and schema name to the foreign key name.
3931
     * Needs because FK name is relevant to the schema and database.
3932
     */
3933
    protected buildForeignKeyName(
3934
        fkName: string,
3935
        schemaName: string | undefined,
3936
        dbName: string | undefined,
3937
    ): string {
3938
        let joinedFkName = fkName
36✔
3939
        if (schemaName && schemaName !== this.driver.searchSchema)
36✔
3940
            joinedFkName = schemaName + "." + joinedFkName
36✔
3941
        if (dbName && dbName !== this.driver.database)
36✔
3942
            joinedFkName = dbName + "." + joinedFkName
36✔
3943

3944
        return joinedFkName
36✔
3945
    }
3946

3947
    /**
3948
     * Removes parenthesis around default value.
3949
     * Sql server returns default value with parenthesis around, e.g.
3950
     *  ('My text') - for string
3951
     *  ((1)) - for number
3952
     *  (newsequentialId()) - for function
3953
     */
3954
    protected removeParenthesisFromDefault(defaultValue: string): any {
3955
        if (defaultValue.substr(0, 1) !== "(") return defaultValue
1,926✔
3956
        const normalizedDefault = defaultValue.substr(
972✔
3957
            1,
3958
            defaultValue.lastIndexOf(")") - 1,
3959
        )
3960
        return this.removeParenthesisFromDefault(normalizedDefault)
972✔
3961
    }
3962

3963
    /**
3964
     * Builds a query for create column.
3965
     */
3966
    protected buildCreateColumnSql(
3967
        table: Table,
3968
        column: TableColumn,
3969
        skipIdentity: boolean,
3970
        createDefault: boolean,
3971
        skipEnum?: boolean,
3972
    ) {
3973
        let c = `"${column.name}" ${this.connection.driver.createFullType(
137,691✔
3974
            column,
3975
        )}`
3976

3977
        if (!skipEnum && column.enum) {
137,691✔
3978
            const expression = this.getEnumExpression(column)
216✔
3979
            const checkName =
3980
                this.connection.namingStrategy.checkConstraintName(
216✔
3981
                    table,
3982
                    expression,
3983
                    true,
3984
                )
3985
            c += ` CONSTRAINT ${checkName} CHECK(${expression})`
216✔
3986
        }
3987

3988
        if (column.collation) c += " COLLATE " + column.collation
137,691✔
3989

3990
        if (column.asExpression) {
137,691✔
3991
            c += ` AS (${column.asExpression})`
351✔
3992
            if (column.generatedType === "STORED") {
351✔
3993
                c += ` PERSISTED`
189✔
3994

3995
                // NOT NULL can be specified for computed columns only if PERSISTED is also specified
3996
                if (column.isNullable !== true) c += " NOT NULL"
189✔
3997
            }
3998
        } else {
3999
            if (column.isNullable !== true) c += " NOT NULL"
137,340✔
4000
        }
4001

4002
        if (
137,691✔
4003
            column.isGenerated === true &&
188,091✔
4004
            column.generationStrategy === "increment" &&
4005
            !skipIdentity
4006
        )
4007
            // don't use skipPrimary here since updates can update already exist primary without auto inc.
4008
            c += " IDENTITY(1,1)"
24,624✔
4009

4010
        if (
137,691✔
4011
            column.default !== undefined &&
155,367✔
4012
            column.default !== null &&
4013
            createDefault
4014
        ) {
4015
            // we create named constraint to be able to delete this constraint when column been dropped
4016
            const defaultName =
4017
                this.connection.namingStrategy.defaultConstraintName(
8,784✔
4018
                    table,
4019
                    column.name,
4020
                )
4021
            c += ` CONSTRAINT "${defaultName}" DEFAULT ${column.default}`
8,784✔
4022
        }
4023

4024
        if (
137,691✔
4025
            column.isGenerated &&
164,619✔
4026
            column.generationStrategy === "uuid" &&
4027
            !column.default
4028
        ) {
4029
            // we create named constraint to be able to delete this constraint when column been dropped
4030
            const defaultName =
4031
                this.connection.namingStrategy.defaultConstraintName(
1,152✔
4032
                    table,
4033
                    column.name,
4034
                )
4035
            c += ` CONSTRAINT "${defaultName}" DEFAULT NEWSEQUENTIALID()`
1,152✔
4036
        }
4037
        return c
137,691✔
4038
    }
4039

4040
    private getEnumExpression(column: TableColumn) {
4041
        if (!column.enum) {
234!
4042
            throw new Error(`Enum is not defined in column ${column.name}`)
×
4043
        }
4044
        return (
234✔
4045
            column.name +
4046
            " IN (" +
4047
            column.enum.map((val) => "'" + val + "'").join(",") +
729✔
4048
            ")"
4049
        )
4050
    }
4051

4052
    protected isEnumCheckConstraint(name: string): boolean {
4053
        return name.indexOf("CHK_") !== -1 && name.indexOf("_ENUM") !== -1
927✔
4054
    }
4055

4056
    /**
4057
     * Converts MssqlParameter into real mssql parameter type.
4058
     */
4059
    protected mssqlParameterToNativeParameter(parameter: MssqlParameter): any {
4060
        switch (this.driver.normalizeType({ type: parameter.type as any })) {
221,607!
4061
            case "bit":
4062
                return this.driver.mssql.Bit
18,036✔
4063
            case "bigint":
4064
                return this.driver.mssql.BigInt
297✔
4065
            case "decimal":
4066
                return this.driver.mssql.Decimal(...parameter.params)
36✔
4067
            case "float":
4068
                return this.driver.mssql.Float
270✔
4069
            case "int":
4070
                return this.driver.mssql.Int
98,307✔
4071
            case "money":
4072
                return this.driver.mssql.Money
9✔
4073
            case "numeric":
4074
                return this.driver.mssql.Numeric(...parameter.params)
18✔
4075
            case "smallint":
4076
                return this.driver.mssql.SmallInt
9✔
4077
            case "smallmoney":
4078
                return this.driver.mssql.SmallMoney
9✔
4079
            case "real":
4080
                return this.driver.mssql.Real
9✔
4081
            case "tinyint":
4082
                return this.driver.mssql.TinyInt
9✔
4083
            case "char":
4084
                if (
45✔
4085
                    this.driver.options.options
4086
                        ?.disableAsciiToUnicodeParamConversion
4087
                ) {
4088
                    return this.driver.mssql.Char(...parameter.params)
9✔
4089
                }
4090
                return this.driver.mssql.NChar(...parameter.params)
36✔
4091
            case "nchar":
4092
                return this.driver.mssql.NChar(...parameter.params)
18✔
4093
            case "text":
4094
                if (
45!
4095
                    this.driver.options.options
4096
                        ?.disableAsciiToUnicodeParamConversion
4097
                ) {
4098
                    return this.driver.mssql.Text
×
4099
                }
4100
                return this.driver.mssql.Ntext
45✔
4101
            case "ntext":
4102
                return this.driver.mssql.Ntext
27✔
4103
            case "varchar":
4104
                if (
288✔
4105
                    this.driver.options.options
4106
                        ?.disableAsciiToUnicodeParamConversion
4107
                ) {
4108
                    return this.driver.mssql.VarChar(...parameter.params)
9✔
4109
                }
4110
                return this.driver.mssql.NVarChar(...parameter.params)
279✔
4111
            case "nvarchar":
4112
                return this.driver.mssql.NVarChar(...parameter.params)
101,466✔
4113
            case "xml":
4114
                return this.driver.mssql.Xml
×
4115
            case "time":
4116
                return this.driver.mssql.Time(...parameter.params)
54✔
4117
            case "date":
4118
                return this.driver.mssql.Date
27✔
4119
            case "datetime":
4120
                return this.driver.mssql.DateTime
153✔
4121
            case "datetime2":
4122
                return this.driver.mssql.DateTime2(...parameter.params)
108✔
4123
            case "datetimeoffset":
4124
                return this.driver.mssql.DateTimeOffset(...parameter.params)
36✔
4125
            case "smalldatetime":
4126
                return this.driver.mssql.SmallDateTime
9✔
4127
            case "uniqueidentifier":
4128
                return this.driver.mssql.UniqueIdentifier
2,097✔
4129
            case "variant":
4130
                return this.driver.mssql.Variant
×
4131
            case "binary":
4132
                return this.driver.mssql.Binary
117✔
4133
            case "varbinary":
4134
                return this.driver.mssql.VarBinary(...parameter.params)
18✔
4135
            case "image":
4136
                return this.driver.mssql.Image
9✔
4137
            case "udt":
4138
                return this.driver.mssql.UDT
×
4139
            case "rowversion":
4140
                return this.driver.mssql.RowVersion
×
4141
        }
4142
    }
4143

4144
    /**
4145
     * Converts string literal of isolation level to enum.
4146
     * The underlying mssql driver requires an enum for the isolation level.
4147
     */
4148
    convertIsolationLevel(isolation: IsolationLevel) {
4149
        const ISOLATION_LEVEL = this.driver.mssql.ISOLATION_LEVEL
54✔
4150
        switch (isolation) {
54✔
4151
            case "READ UNCOMMITTED":
4152
                return ISOLATION_LEVEL.READ_UNCOMMITTED
18✔
4153
            case "REPEATABLE READ":
4154
                return ISOLATION_LEVEL.REPEATABLE_READ
9✔
4155
            case "SERIALIZABLE":
4156
                return ISOLATION_LEVEL.SERIALIZABLE
18✔
4157

4158
            case "READ COMMITTED":
4159
            default:
4160
                return ISOLATION_LEVEL.READ_COMMITTED
9✔
4161
        }
4162
    }
4163

4164
    /**
4165
     * Change table comment.
4166
     */
4167
    changeTableComment(
4168
        tableOrName: Table | string,
4169
        comment?: string,
4170
    ): Promise<void> {
4171
        throw new TypeORMError(
×
4172
            `sqlserver driver does not support change table comment.`,
4173
        )
4174
    }
4175
}
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

© 2026 Coveralls, Inc