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

typeorm / typeorm / 22831820825

08 Mar 2026 10:57PM UTC coverage: 74.734% (+0.07%) from 74.664%
22831820825

Pull #12121

github

web-flow
Merge 1a6f8d65b into cd72d2a03
Pull Request #12121: feat(QueryRunner): add ifExists parameter to all drop methods

26613 of 32154 branches covered (82.77%)

Branch coverage included in aggregate %.

631 of 907 new or added lines in 11 files covered. (69.57%)

5 existing lines in 4 files now uncovered.

84313 of 116273 relevant lines covered (72.51%)

65684.32 hits per line

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

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

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

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

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

23✔
52
    private lock: QueryLock = new QueryLock()
23✔
53

23✔
54
    // -------------------------------------------------------------------------
23✔
55
    // Constructor
23✔
56
    // -------------------------------------------------------------------------
23✔
57

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

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

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

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

23✔
87
    /**
23✔
88
     * Starts transaction.
23✔
89
     * @param isolationLevel
23✔
90
     */
23✔
91
    async startTransaction(isolationLevel?: IsolationLevel): Promise<void> {
23✔
92
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
19,220!
93

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

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

19,220✔
136
        await this.broadcaster.broadcast("AfterTransactionStart")
19,220✔
137
    }
19,220✔
138

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

19,170✔
146
        if (!this.isTransactionActive) throw new TransactionNotStartedError()
19,170!
147

19,170✔
148
        await this.broadcaster.broadcast("BeforeTransactionCommit")
19,170✔
149

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

19,166✔
157
                    await this.broadcaster.broadcast("AfterTransactionCommit")
19,166✔
158

19,166✔
159
                    ok()
19,166✔
160
                    this.connection.logger.logQuery("COMMIT")
19,166✔
161
                    this.transactionDepth -= 1
19,166✔
162
                })
19,166✔
163
            })
19,166✔
164
        }
19,166✔
165
        this.transactionDepth -= 1
4✔
166
    }
4✔
167

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

50✔
175
        if (!this.isTransactionActive) throw new TransactionNotStartedError()
50!
176

50✔
177
        await this.broadcaster.broadcast("BeforeTransactionRollback")
50✔
178

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

42✔
191
                    await this.broadcaster.broadcast("AfterTransactionRollback")
42✔
192

42✔
193
                    ok()
42✔
194
                    this.connection.logger.logQuery("ROLLBACK")
42✔
195
                    this.transactionDepth -= 1
42✔
196
                })
44✔
197
            })
44✔
198
        }
44✔
199
    }
50✔
200

23✔
201
    /**
23✔
202
     * Executes a given SQL query.
23✔
203
     * @param query
23✔
204
     * @param parameters
23✔
205
     * @param useStructuredResult
23✔
206
     */
23✔
207
    async query(
23✔
208
        query: string,
110,958✔
209
        parameters?: any[],
110,958✔
210
        useStructuredResult = false,
110,958✔
211
    ): Promise<any> {
110,958✔
212
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
110,958!
213

110,958✔
214
        const release = await this.lock.acquire()
110,958✔
215

110,958✔
216
        this.driver.connection.logger.logQuery(query, parameters, this)
110,958✔
217
        await this.broadcaster.broadcast("BeforeQuery", query, parameters)
110,958✔
218

110,958✔
219
        const broadcasterResult = new BroadcasterResult()
110,958✔
220

110,958✔
221
        try {
110,958✔
222
            const pool = await (this.mode === "slave"
110,958✔
223
                ? this.driver.obtainSlaveConnection()
110,958✔
224
                : this.driver.obtainMasterConnection())
110,958✔
225
            const request = new this.driver.mssql.Request(
110,958✔
226
                this.isTransactionActive ? this.databaseConnection : pool,
110,958✔
227
            )
110,958✔
228
            if (parameters && parameters.length) {
110,958✔
229
                parameters.forEach((parameter, index) => {
30,562✔
230
                    const parameterName = index.toString()
72,022✔
231
                    if (InstanceChecker.isMssqlParameter(parameter)) {
72,022✔
232
                        const mssqlParameter =
54,214✔
233
                            this.mssqlParameterToNativeParameter(parameter)
54,214✔
234
                        if (mssqlParameter) {
54,214✔
235
                            request.input(
54,150✔
236
                                parameterName,
54,150✔
237
                                mssqlParameter,
54,150✔
238
                                parameter.value,
54,150✔
239
                            )
54,150✔
240
                        } else {
54,214✔
241
                            request.input(parameterName, parameter.value)
64✔
242
                        }
64✔
243
                    } else {
72,022✔
244
                        request.input(parameterName, parameter)
17,808✔
245
                    }
17,808✔
246
                })
30,562✔
247
            }
30,562✔
248
            const queryStartTime = Date.now()
110,958✔
249

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

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

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

110,958✔
280
                    if (err) {
110,958✔
281
                        fail(new QueryFailedError(query, parameters, err))
18✔
282
                    }
18✔
283

110,958✔
284
                    ok(raw)
110,958✔
285
                })
110,958✔
286
            })
110,958✔
287

110,940✔
288
            const result = new QueryResult()
110,940✔
289

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

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

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

110,940✔
308
            if (useStructuredResult) {
110,958✔
309
                return result
31,658✔
310
            } else {
110,958✔
311
                return result.raw
79,282✔
312
            }
79,282✔
313
        } catch (err) {
110,958✔
314
            this.driver.connection.logger.logQueryError(
18✔
315
                err,
18✔
316
                query,
18✔
317
                parameters,
18✔
318
                this,
18✔
319
            )
18✔
320
            this.broadcaster.broadcastAfterQueryEvent(
18✔
321
                broadcasterResult,
18✔
322
                query,
18✔
323
                parameters,
18✔
324
                false,
18✔
325
                undefined,
18✔
326
                undefined,
18✔
327
                err,
18✔
328
            )
18✔
329

18✔
330
            throw err
18✔
331
        } finally {
110,958!
332
            await broadcasterResult.wait()
110,958✔
333

110,958✔
334
            release()
110,958✔
335
        }
110,958✔
336
    }
110,958✔
337

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

2✔
353
        const release = await this.lock.acquire()
2✔
354

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

2✔
377
        request.query(query)
2✔
378

2✔
379
        const streamRequest = request.toReadableStream()
2✔
380

2✔
381
        streamRequest.on("error", (err: any) => {
2✔
382
            release()
×
383
            this.driver.connection.logger.logQueryError(
×
384
                err,
×
385
                query,
×
386
                parameters,
×
387
                this,
×
388
            )
×
389
        })
2✔
390

2✔
391
        streamRequest.on("end", () => {
2✔
392
            release()
2✔
393
        })
2✔
394

2✔
395
        if (onEnd) {
2!
396
            streamRequest.on("end", onEnd)
×
397
        }
×
398

2✔
399
        if (onError) {
2!
400
            streamRequest.on("error", onError)
×
401
        }
×
402

2✔
403
        return streamRequest
2✔
404
    }
2✔
405

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

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

23✔
427
    /**
23✔
428
     * Checks if database with the given name exist.
23✔
429
     * @param database
23✔
430
     */
23✔
431
    async hasDatabase(database: string): Promise<boolean> {
23✔
432
        const result = await this.query(
3,014✔
433
            `SELECT DB_ID('${database}') as "db_id"`,
3,014✔
434
        )
3,014✔
435
        const dbId = result[0]["db_id"]
3,014✔
436
        return !!dbId
3,014✔
437
    }
3,014✔
438

23✔
439
    /**
23✔
440
     * Loads currently using database
23✔
441
     */
23✔
442
    async getCurrentDatabase(): Promise<string> {
23✔
443
        const currentDBQuery = await this.query(`SELECT DB_NAME() AS "db_name"`)
3,796✔
444
        return currentDBQuery[0]["db_name"]
3,796✔
445
    }
3,796✔
446

23✔
447
    /**
23✔
448
     * Checks if schema with the given name exist.
23✔
449
     * @param schema
23✔
450
     */
23✔
451
    async hasSchema(schema: string): Promise<boolean> {
23✔
452
        const result = await this.query(
6✔
453
            `SELECT SCHEMA_ID('${schema}') as "schema_id"`,
6✔
454
        )
6✔
455
        const schemaId = result[0]["schema_id"]
6✔
456
        return !!schemaId
6✔
457
    }
6✔
458

23✔
459
    /**
23✔
460
     * Loads currently using database schema
23✔
461
     */
23✔
462
    async getCurrentSchema(): Promise<string> {
23✔
463
        const currentSchemaQuery = await this.query(
4,708✔
464
            `SELECT SCHEMA_NAME() AS "schema_name"`,
4,708✔
465
        )
4,708✔
466
        return currentSchemaQuery[0]["schema_name"]
4,708✔
467
    }
4,708✔
468

23✔
469
    /**
23✔
470
     * Checks if table with the given name exist in the database.
23✔
471
     * @param tableOrName
23✔
472
     */
23✔
473
    async hasTable(tableOrName: Table | string): Promise<boolean> {
23✔
474
        const parsedTableName = this.driver.parseTableName(tableOrName)
3,240✔
475

3,240✔
476
        if (!parsedTableName.database) {
3,240!
477
            parsedTableName.database = await this.getCurrentDatabase()
×
478
        }
×
479

3,240✔
480
        if (!parsedTableName.schema) {
3,240!
481
            parsedTableName.schema = await this.getCurrentSchema()
×
482
        }
×
483

3,240✔
484
        const sql = `SELECT * FROM "${parsedTableName.database}"."INFORMATION_SCHEMA"."TABLES" WHERE "TABLE_NAME" = '${parsedTableName.tableName}' AND "TABLE_SCHEMA" = '${parsedTableName.schema}'`
3,240✔
485
        const result = await this.query(sql)
3,240✔
486
        return result.length ? true : false
3,240✔
487
    }
3,240✔
488

23✔
489
    /**
23✔
490
     * Checks if column exist in the table.
23✔
491
     * @param tableOrName
23✔
492
     * @param columnName
23✔
493
     */
23✔
494
    async hasColumn(
23✔
495
        tableOrName: Table | string,
8✔
496
        columnName: string,
8✔
497
    ): Promise<boolean> {
8✔
498
        const parsedTableName = this.driver.parseTableName(tableOrName)
8✔
499

8✔
500
        if (!parsedTableName.database) {
8!
501
            parsedTableName.database = await this.getCurrentDatabase()
×
502
        }
×
503

8✔
504
        if (!parsedTableName.schema) {
8!
505
            parsedTableName.schema = await this.getCurrentSchema()
×
506
        }
×
507

8✔
508
        const sql = `SELECT * FROM "${parsedTableName.database}"."INFORMATION_SCHEMA"."COLUMNS" WHERE "TABLE_NAME" = '${parsedTableName.tableName}' AND "TABLE_SCHEMA" = '${parsedTableName.schema}' AND "COLUMN_NAME" = '${columnName}'`
8✔
509
        const result = await this.query(sql)
8✔
510
        return result.length ? true : false
8✔
511
    }
8✔
512

23✔
513
    /**
23✔
514
     * Creates a new database.
23✔
515
     * @param database
23✔
516
     * @param ifNotExists
23✔
517
     */
23✔
518
    async createDatabase(
23✔
519
        database: string,
18✔
520
        ifNotExists?: boolean,
18✔
521
    ): Promise<void> {
18✔
522
        const up = ifNotExists
18✔
523
            ? `IF DB_ID('${database}') IS NULL CREATE DATABASE "${database}"`
18✔
524
            : `CREATE DATABASE "${database}"`
18!
525
        const down = `DROP DATABASE "${database}"`
18✔
526
        await this.executeQueries(new Query(up), new Query(down))
18✔
527
    }
18✔
528

23✔
529
    /**
23✔
530
     * Drops database.
23✔
531
     * @param database
23✔
532
     * @param ifExists
23✔
533
     */
23✔
534
    async dropDatabase(database: string, ifExists?: boolean): Promise<void> {
23✔
535
        const up = ifExists
6✔
536
            ? `IF DB_ID('${database}') IS NOT NULL DROP DATABASE "${database}"`
6✔
537
            : `DROP DATABASE "${database}"`
6✔
538
        const down = `CREATE DATABASE "${database}"`
6✔
539
        await this.executeQueries(new Query(up), new Query(down))
6✔
540
    }
6✔
541

23✔
542
    /**
23✔
543
     * Creates table schema.
23✔
544
     * If database name also specified (e.g. 'dbName.schemaName') schema will be created in specified database.
23✔
545
     * @param schemaPath
23✔
546
     * @param ifNotExists
23✔
547
     */
23✔
548
    async createSchema(
23✔
549
        schemaPath: string,
30✔
550
        ifNotExists?: boolean,
30✔
551
    ): Promise<void> {
30✔
552
        const upQueries: Query[] = []
30✔
553
        const downQueries: Query[] = []
30✔
554

30✔
555
        if (schemaPath.indexOf(".") === -1) {
30✔
556
            const upQuery = ifNotExists
16✔
557
                ? `IF SCHEMA_ID('${schemaPath}') IS NULL BEGIN EXEC ('CREATE SCHEMA "${schemaPath}"') END`
16✔
558
                : `CREATE SCHEMA "${schemaPath}"`
16!
559
            upQueries.push(new Query(upQuery))
16✔
560
            downQueries.push(new Query(`DROP SCHEMA "${schemaPath}"`))
16✔
561
        } else {
30✔
562
            const dbName = schemaPath.split(".")[0]
14✔
563
            const schema = schemaPath.split(".")[1]
14✔
564
            const currentDB = await this.getCurrentDatabase()
14✔
565
            upQueries.push(new Query(`USE "${dbName}"`))
14✔
566
            downQueries.push(new Query(`USE "${currentDB}"`))
14✔
567

14✔
568
            const upQuery = ifNotExists
14✔
569
                ? `IF SCHEMA_ID('${schema}') IS NULL BEGIN EXEC ('CREATE SCHEMA "${schema}"') END`
14✔
570
                : `CREATE SCHEMA "${schema}"`
14!
571
            upQueries.push(new Query(upQuery))
14✔
572
            downQueries.push(new Query(`DROP SCHEMA "${schema}"`))
14✔
573

14✔
574
            upQueries.push(new Query(`USE "${currentDB}"`))
14✔
575
            downQueries.push(new Query(`USE "${dbName}"`))
14✔
576
        }
14✔
577

30✔
578
        await this.executeQueries(upQueries, downQueries)
30✔
579
    }
30✔
580

23✔
581
    /**
23✔
582
     * Drops table schema.
23✔
583
     * If database name also specified (e.g. 'dbName.schemaName') schema will be dropped in specified database.
23✔
584
     * @param schemaPath
23✔
585
     * @param ifExists
23✔
586
     */
23✔
587
    async dropSchema(schemaPath: string, ifExists?: boolean): Promise<void> {
23✔
588
        const upQueries: Query[] = []
4✔
589
        const downQueries: Query[] = []
4✔
590

4✔
591
        if (schemaPath.indexOf(".") === -1) {
4✔
592
            const upQuery = ifExists
4✔
593
                ? `IF SCHEMA_ID('${schemaPath}') IS NOT NULL BEGIN EXEC ('DROP SCHEMA "${schemaPath}"') END`
4✔
594
                : `DROP SCHEMA "${schemaPath}"`
4✔
595
            upQueries.push(new Query(upQuery))
4✔
596
            downQueries.push(new Query(`CREATE SCHEMA "${schemaPath}"`))
4✔
597
        } else {
4!
598
            const dbName = schemaPath.split(".")[0]
×
599
            const schema = schemaPath.split(".")[1]
×
600
            const currentDB = await this.getCurrentDatabase()
×
601
            upQueries.push(new Query(`USE "${dbName}"`))
×
602
            downQueries.push(new Query(`USE "${currentDB}"`))
×
603

×
NEW
604
            const upQuery = ifExists
×
NEW
605
                ? `IF SCHEMA_ID('${schema}') IS NOT NULL BEGIN EXEC ('DROP SCHEMA "${schema}"') END`
×
606
                : `DROP SCHEMA "${schema}"`
×
607
            upQueries.push(new Query(upQuery))
×
608
            downQueries.push(new Query(`CREATE SCHEMA "${schema}"`))
×
609

×
610
            upQueries.push(new Query(`USE "${currentDB}"`))
×
611
            downQueries.push(new Query(`USE "${dbName}"`))
×
612
        }
×
613

4✔
614
        await this.executeQueries(upQueries, downQueries)
4✔
615
    }
4✔
616

23✔
617
    /**
23✔
618
     * Creates a new table.
23✔
619
     * @param table
23✔
620
     * @param ifNotExists
23✔
621
     * @param createForeignKeys
23✔
622
     * @param createIndices
23✔
623
     */
23✔
624
    async createTable(
23✔
625
        table: Table,
10,534✔
626
        ifNotExists: boolean = false,
10,534✔
627
        createForeignKeys: boolean = true,
10,534✔
628
        createIndices: boolean = true,
10,534✔
629
    ): Promise<void> {
10,534✔
630
        if (ifNotExists) {
10,534✔
631
            const isTableExist = await this.hasTable(table)
66✔
632
            if (isTableExist) return Promise.resolve()
66✔
633
        }
66✔
634
        const upQueries: Query[] = []
10,530✔
635
        const downQueries: Query[] = []
10,530✔
636

10,530✔
637
        upQueries.push(this.createTableSql(table, createForeignKeys))
10,530✔
638
        downQueries.push(this.dropTableSql(table))
10,530✔
639

10,530✔
640
        // if createForeignKeys is true, we must drop created foreign keys in down query.
10,530✔
641
        // createTable does not need separate method to create foreign keys, because it create fk's in the same query with table creation.
10,530✔
642
        if (createForeignKeys)
10,530✔
643
            table.foreignKeys.forEach((foreignKey) =>
10,534✔
644
                downQueries.push(this.dropForeignKeySql(table, foreignKey)),
110✔
645
            )
110✔
646

10,530✔
647
        if (createIndices) {
10,530✔
648
            table.indices.forEach((index) => {
10,530✔
649
                // new index may be passed without name. In this case we generate index name manually.
4,472✔
650
                if (!index.name)
4,472✔
651
                    index.name = this.connection.namingStrategy.indexName(
4,472✔
652
                        table,
6✔
653
                        index.columnNames,
6✔
654
                        index.where,
6✔
655
                    )
6✔
656
                upQueries.push(this.createIndexSql(table, index))
4,472✔
657
                downQueries.push(this.dropIndexSql(table, index))
4,472✔
658
            })
10,530✔
659
        }
10,530✔
660

10,530✔
661
        // if table have column with generated type, we must add the expression to the metadata table
10,530✔
662
        const generatedColumns = table.columns.filter(
10,530✔
663
            (column) => column.generatedType && column.asExpression,
10,530✔
664
        )
10,530✔
665

10,530✔
666
        for (const column of generatedColumns) {
10,534✔
667
            const parsedTableName = this.driver.parseTableName(table)
56✔
668

56✔
669
            if (!parsedTableName.schema) {
56!
670
                parsedTableName.schema = await this.getCurrentSchema()
×
671
            }
×
672

56✔
673
            const insertQuery = this.insertTypeormMetadataSql({
56✔
674
                database: parsedTableName.database,
56✔
675
                schema: parsedTableName.schema,
56✔
676
                table: parsedTableName.tableName,
56✔
677
                type: MetadataTableType.GENERATED_COLUMN,
56✔
678
                name: column.name,
56✔
679
                value: column.asExpression,
56✔
680
            })
56✔
681

56✔
682
            const deleteQuery = this.deleteTypeormMetadataSql({
56✔
683
                database: parsedTableName.database,
56✔
684
                schema: parsedTableName.schema,
56✔
685
                table: parsedTableName.tableName,
56✔
686
                type: MetadataTableType.GENERATED_COLUMN,
56✔
687
                name: column.name,
56✔
688
            })
56✔
689

56✔
690
            upQueries.push(insertQuery)
56✔
691
            downQueries.push(deleteQuery)
56✔
692
        }
56✔
693

10,530✔
694
        await this.executeQueries(upQueries, downQueries)
10,530✔
695
    }
10,530✔
696

23✔
697
    /**
23✔
698
     * Drops the table.
23✔
699
     * @param tableOrName
23✔
700
     * @param ifExists
23✔
701
     * @param dropForeignKeys
23✔
702
     * @param dropIndices
23✔
703
     */
23✔
704
    async dropTable(
23✔
705
        tableOrName: Table | string,
26✔
706
        ifExists?: boolean,
26✔
707
        dropForeignKeys: boolean = true,
26✔
708
        dropIndices: boolean = true,
26✔
709
    ): Promise<void> {
26✔
710
        if (ifExists) {
26✔
711
            const isTableExist = await this.hasTable(tableOrName)
6✔
712
            if (!isTableExist) return Promise.resolve()
6✔
713
        }
6✔
714

24✔
715
        // if dropTable called with dropForeignKeys = true, we must create foreign keys in down query.
24✔
716
        const createForeignKeys: boolean = dropForeignKeys
24✔
717
        const table = InstanceChecker.isTable(tableOrName)
24✔
718
            ? tableOrName
26✔
719
            : await this.getCachedTable(tableOrName)
26✔
720
        const upQueries: Query[] = []
16✔
721
        const downQueries: Query[] = []
16✔
722

16✔
723
        // It needs because if table does not exist and dropForeignKeys or dropIndices is true, we don't need
16✔
724
        // to perform drop queries for foreign keys and indices.
16✔
725

16✔
726
        if (dropIndices) {
26✔
727
            table.indices.forEach((index) => {
24✔
728
                upQueries.push(this.dropIndexSql(table, index))
2✔
729
                downQueries.push(this.createIndexSql(table, index))
2✔
730
            })
24✔
731
        }
24✔
732

24✔
733
        // if dropForeignKeys is true, we just drop the table, otherwise we also drop table foreign keys.
24✔
734
        // createTable does not need separate method to create foreign keys, because it create fk's in the same query with table creation.
24✔
735
        if (dropForeignKeys)
24✔
736
            table.foreignKeys.forEach((foreignKey) =>
26✔
737
                upQueries.push(this.dropForeignKeySql(table, foreignKey)),
20✔
738
            )
20✔
739

24✔
740
        upQueries.push(this.dropTableSql(table))
24✔
741
        downQueries.push(this.createTableSql(table, createForeignKeys))
24✔
742

24✔
743
        // if table had columns with generated type, we must remove the expression from the metadata table
24✔
744
        const generatedColumns = table.columns.filter(
24✔
745
            (column) => column.generatedType && column.asExpression,
24✔
746
        )
24✔
747

24✔
748
        for (const column of generatedColumns) {
26✔
749
            const parsedTableName = this.driver.parseTableName(table)
8✔
750

8✔
751
            if (!parsedTableName.schema) {
8!
752
                parsedTableName.schema = await this.getCurrentSchema()
×
753
            }
×
754

8✔
755
            const deleteQuery = this.deleteTypeormMetadataSql({
8✔
756
                database: parsedTableName.database,
8✔
757
                schema: parsedTableName.schema,
8✔
758
                table: parsedTableName.tableName,
8✔
759
                type: MetadataTableType.GENERATED_COLUMN,
8✔
760
                name: column.name,
8✔
761
            })
8✔
762

8✔
763
            const insertQuery = this.insertTypeormMetadataSql({
8✔
764
                database: parsedTableName.database,
8✔
765
                schema: parsedTableName.schema,
8✔
766
                table: parsedTableName.tableName,
8✔
767
                type: MetadataTableType.GENERATED_COLUMN,
8✔
768
                name: column.name,
8✔
769
                value: column.asExpression,
8✔
770
            })
8✔
771

8✔
772
            upQueries.push(deleteQuery)
8✔
773
            downQueries.push(insertQuery)
8✔
774
        }
8✔
775

24✔
776
        await this.executeQueries(upQueries, downQueries)
24✔
777
    }
24✔
778

23✔
779
    /**
23✔
780
     * Creates a new view.
23✔
781
     * @param view
23✔
782
     * @param syncWithMetadata
23✔
783
     */
23✔
784
    async createView(
23✔
785
        view: View,
16✔
786
        syncWithMetadata: boolean = false,
16✔
787
    ): Promise<void> {
16✔
788
        const upQueries: Query[] = []
16✔
789
        const downQueries: Query[] = []
16✔
790
        upQueries.push(this.createViewSql(view))
16✔
791
        if (syncWithMetadata)
16✔
792
            upQueries.push(await this.insertViewDefinitionSql(view))
16✔
793
        downQueries.push(this.dropViewSql(view))
16✔
794
        if (syncWithMetadata)
16✔
795
            downQueries.push(await this.deleteViewDefinitionSql(view))
16✔
796
        await this.executeQueries(upQueries, downQueries)
16✔
797
    }
16✔
798

23✔
799
    /**
23✔
800
     * Drops the view.
23✔
801
     * @param target
23✔
802
     * @param ifExists
23✔
803
     */
23✔
804
    async dropView(target: View | string, ifExists?: boolean): Promise<void> {
23✔
805
        const viewName = InstanceChecker.isView(target) ? target.name : target
×
NEW
806
        let view: View
×
NEW
807
        try {
×
NEW
808
            view = await this.getCachedView(viewName)
×
NEW
809
        } catch {
×
NEW
810
            if (ifExists) return
×
NEW
811
            throw new TypeORMError(`View "${viewName}" does not exist.`)
×
NEW
812
        }
×
813

×
NEW
814
        await this.executeQueries(
×
NEW
815
            [
×
NEW
816
                await this.deleteViewDefinitionSql(view),
×
NEW
817
                this.dropViewSql(view, ifExists),
×
NEW
818
            ],
×
NEW
819
            [
×
NEW
820
                await this.insertViewDefinitionSql(view),
×
NEW
821
                this.createViewSql(view),
×
NEW
822
            ],
×
NEW
823
        )
×
UNCOV
824
    }
×
825

23✔
826
    /**
23✔
827
     * Renames a table.
23✔
828
     * @param oldTableOrName
23✔
829
     * @param newTableName
23✔
830
     */
23✔
831
    async renameTable(
23✔
832
        oldTableOrName: Table | string,
34✔
833
        newTableName: string,
34✔
834
    ): Promise<void> {
34✔
835
        const upQueries: Query[] = []
34✔
836
        const downQueries: Query[] = []
34✔
837
        const oldTable = InstanceChecker.isTable(oldTableOrName)
34✔
838
            ? oldTableOrName
34✔
839
            : await this.getCachedTable(oldTableOrName)
34✔
840
        const newTable = oldTable.clone()
30✔
841

30✔
842
        // we need database name and schema name to rename FK constraints
30✔
843
        let dbName: string | undefined = undefined
30✔
844
        let schemaName: string | undefined = undefined
30✔
845
        let oldTableName: string = oldTable.name
30✔
846
        const splittedName = oldTable.name.split(".")
30✔
847
        if (splittedName.length === 3) {
34✔
848
            dbName = splittedName[0]
4✔
849
            oldTableName = splittedName[2]
4✔
850
            if (splittedName[1] !== "") schemaName = splittedName[1]
4✔
851
        } else if (splittedName.length === 2) {
34!
852
            schemaName = splittedName[0]
×
853
            oldTableName = splittedName[1]
×
854
        }
×
855

34✔
856
        newTable.name = this.driver.buildTableName(
34✔
857
            newTableName,
34✔
858
            schemaName,
34✔
859
            dbName,
34✔
860
        )
34✔
861

34✔
862
        // if we have tables with database which differs from database specified in config, we must change currently used database.
34✔
863
        // This need because we can not rename objects from another database.
34✔
864
        const currentDB = await this.getCurrentDatabase()
34✔
865
        if (dbName && dbName !== currentDB) {
34✔
866
            upQueries.push(new Query(`USE "${dbName}"`))
4✔
867
            downQueries.push(new Query(`USE "${currentDB}"`))
4✔
868
        }
4✔
869

34✔
870
        // rename table
34✔
871
        upQueries.push(
34✔
872
            new Query(
34✔
873
                `EXEC sp_rename "${this.getTablePath(
34✔
874
                    oldTable,
34✔
875
                )}", "${newTableName}"`,
34✔
876
            ),
34✔
877
        )
34✔
878
        downQueries.push(
34✔
879
            new Query(
34✔
880
                `EXEC sp_rename "${this.getTablePath(
34✔
881
                    newTable,
34✔
882
                )}", "${oldTableName}"`,
34✔
883
            ),
34✔
884
        )
34✔
885

34✔
886
        // rename primary key constraint
34✔
887
        if (
34✔
888
            newTable.primaryColumns.length > 0 &&
34✔
889
            !newTable.primaryColumns[0].primaryKeyConstraintName
34✔
890
        ) {
34✔
891
            const columnNames = newTable.primaryColumns.map(
26✔
892
                (column) => column.name,
26✔
893
            )
26✔
894

26✔
895
            const oldPkName = this.connection.namingStrategy.primaryKeyName(
26✔
896
                oldTable,
26✔
897
                columnNames,
26✔
898
            )
26✔
899
            const newPkName = this.connection.namingStrategy.primaryKeyName(
26✔
900
                newTable,
26✔
901
                columnNames,
26✔
902
            )
26✔
903

26✔
904
            // rename primary constraint
26✔
905
            upQueries.push(
26✔
906
                new Query(
26✔
907
                    `EXEC sp_rename "${this.getTablePath(
26✔
908
                        newTable,
26✔
909
                    )}.${oldPkName}", "${newPkName}"`,
26✔
910
                ),
26✔
911
            )
26✔
912
            downQueries.push(
26✔
913
                new Query(
26✔
914
                    `EXEC sp_rename "${this.getTablePath(
26✔
915
                        newTable,
26✔
916
                    )}.${newPkName}", "${oldPkName}"`,
26✔
917
                ),
26✔
918
            )
26✔
919
        }
26✔
920

34✔
921
        // rename unique constraints
34✔
922
        newTable.uniques.forEach((unique) => {
34✔
923
            const oldUniqueName =
10✔
924
                this.connection.namingStrategy.uniqueConstraintName(
10✔
925
                    oldTable,
10✔
926
                    unique.columnNames,
10✔
927
                )
10✔
928

10✔
929
            // Skip renaming if Unique has user defined constraint name
10✔
930
            if (unique.name !== oldUniqueName) return
10✔
931

6✔
932
            // build new constraint name
6✔
933
            const newUniqueName =
6✔
934
                this.connection.namingStrategy.uniqueConstraintName(
6✔
935
                    newTable,
6✔
936
                    unique.columnNames,
6✔
937
                )
6✔
938

6✔
939
            // build queries
6✔
940
            upQueries.push(
6✔
941
                new Query(
6✔
942
                    `EXEC sp_rename "${this.getTablePath(newTable)}.${
6✔
943
                        unique.name
6✔
944
                    }", "${newUniqueName}"`,
6✔
945
                ),
6✔
946
            )
6✔
947
            downQueries.push(
6✔
948
                new Query(
6✔
949
                    `EXEC sp_rename "${this.getTablePath(
6✔
950
                        newTable,
6✔
951
                    )}.${newUniqueName}", "${unique.name}"`,
6✔
952
                ),
6✔
953
            )
6✔
954

6✔
955
            // replace constraint name
6✔
956
            unique.name = newUniqueName
6✔
957
        })
34✔
958

34✔
959
        // rename index constraints
34✔
960
        newTable.indices.forEach((index) => {
34✔
961
            const oldIndexName = this.connection.namingStrategy.indexName(
22✔
962
                oldTable,
22✔
963
                index.columnNames,
22✔
964
                index.where,
22✔
965
            )
22✔
966

22✔
967
            // Skip renaming if Index has user defined constraint name
22✔
968
            if (index.name !== oldIndexName) return
22✔
969

10✔
970
            // build new constraint name
10✔
971
            const newIndexName = this.connection.namingStrategy.indexName(
10✔
972
                newTable,
10✔
973
                index.columnNames,
10✔
974
                index.where,
10✔
975
            )
10✔
976

10✔
977
            // build queries
10✔
978
            upQueries.push(
10✔
979
                new Query(
10✔
980
                    `EXEC sp_rename "${this.getTablePath(newTable)}.${
10✔
981
                        index.name
10✔
982
                    }", "${newIndexName}", "INDEX"`,
10✔
983
                ),
10✔
984
            )
10✔
985
            downQueries.push(
10✔
986
                new Query(
10✔
987
                    `EXEC sp_rename "${this.getTablePath(
10✔
988
                        newTable,
10✔
989
                    )}.${newIndexName}", "${index.name}", "INDEX"`,
10✔
990
                ),
10✔
991
            )
10✔
992

10✔
993
            // replace constraint name
10✔
994
            index.name = newIndexName
10✔
995
        })
34✔
996

34✔
997
        // rename foreign key constraints
34✔
998
        newTable.foreignKeys.forEach((foreignKey) => {
34✔
999
            const oldForeignKeyName =
18✔
1000
                this.connection.namingStrategy.foreignKeyName(
18✔
1001
                    oldTable,
18✔
1002
                    foreignKey.columnNames,
18✔
1003
                    this.getTablePath(foreignKey),
18✔
1004
                    foreignKey.referencedColumnNames,
18✔
1005
                )
18✔
1006

18✔
1007
            // Skip renaming if foreign key has user defined constraint name
18✔
1008
            if (foreignKey.name !== oldForeignKeyName) return
18✔
1009

2✔
1010
            // build new constraint name
2✔
1011
            const newForeignKeyName =
2✔
1012
                this.connection.namingStrategy.foreignKeyName(
2✔
1013
                    newTable,
2✔
1014
                    foreignKey.columnNames,
2✔
1015
                    this.getTablePath(foreignKey),
2✔
1016
                    foreignKey.referencedColumnNames,
2✔
1017
                )
2✔
1018

2✔
1019
            // build queries
2✔
1020
            upQueries.push(
2✔
1021
                new Query(
2✔
1022
                    `EXEC sp_rename "${this.buildForeignKeyName(
2✔
1023
                        foreignKey.name!,
2✔
1024
                        schemaName,
2✔
1025
                        dbName,
2✔
1026
                    )}", "${newForeignKeyName}"`,
2✔
1027
                ),
2✔
1028
            )
2✔
1029
            downQueries.push(
2✔
1030
                new Query(
2✔
1031
                    `EXEC sp_rename "${this.buildForeignKeyName(
2✔
1032
                        newForeignKeyName,
2✔
1033
                        schemaName,
2✔
1034
                        dbName,
2✔
1035
                    )}", "${foreignKey.name}"`,
2✔
1036
                ),
2✔
1037
            )
2✔
1038

2✔
1039
            // replace constraint name
2✔
1040
            foreignKey.name = newForeignKeyName
2✔
1041
        })
34✔
1042

34✔
1043
        // change currently used database back to default db.
34✔
1044
        if (dbName && dbName !== currentDB) {
34✔
1045
            upQueries.push(new Query(`USE "${currentDB}"`))
4✔
1046
            downQueries.push(new Query(`USE "${dbName}"`))
4✔
1047
        }
4✔
1048

34✔
1049
        await this.executeQueries(upQueries, downQueries)
34✔
1050

34✔
1051
        // rename old table and replace it in cached tabled;
34✔
1052
        oldTable.name = newTable.name
34✔
1053
        this.replaceCachedTable(oldTable, newTable)
34✔
1054
    }
34✔
1055

23✔
1056
    /**
23✔
1057
     * Creates a new column from the column in the table.
23✔
1058
     * @param tableOrName
23✔
1059
     * @param column
23✔
1060
     */
23✔
1061
    async addColumn(
23✔
1062
        tableOrName: Table | string,
74✔
1063
        column: TableColumn,
74✔
1064
    ): Promise<void> {
74✔
1065
        const table = InstanceChecker.isTable(tableOrName)
74✔
1066
            ? tableOrName
74✔
1067
            : await this.getCachedTable(tableOrName)
74✔
1068
        const clonedTable = table.clone()
4✔
1069
        const upQueries: Query[] = []
4✔
1070
        const downQueries: Query[] = []
4✔
1071

4✔
1072
        upQueries.push(
4✔
1073
            new Query(
4✔
1074
                `ALTER TABLE ${this.escapePath(
4✔
1075
                    table,
4✔
1076
                )} ADD ${this.buildCreateColumnSql(
4✔
1077
                    table,
4✔
1078
                    column,
4✔
1079
                    false,
4✔
1080
                    true,
4✔
1081
                )}`,
4✔
1082
            ),
4✔
1083
        )
4✔
1084
        downQueries.push(
4✔
1085
            new Query(
4✔
1086
                `ALTER TABLE ${this.escapePath(table)} DROP COLUMN "${
4✔
1087
                    column.name
4✔
1088
                }"`,
4✔
1089
            ),
4✔
1090
        )
4✔
1091

4✔
1092
        // create or update primary key constraint
4✔
1093
        if (column.isPrimary) {
74✔
1094
            const primaryColumns = clonedTable.primaryColumns
14✔
1095
            // if table already have primary key, me must drop it and recreate again
14✔
1096
            if (primaryColumns.length > 0) {
14✔
1097
                const pkName = primaryColumns[0].primaryKeyConstraintName
4✔
1098
                    ? primaryColumns[0].primaryKeyConstraintName
4!
1099
                    : this.connection.namingStrategy.primaryKeyName(
4✔
1100
                          clonedTable,
4✔
1101
                          primaryColumns.map((column) => column.name),
4✔
1102
                      )
4✔
1103

4✔
1104
                const columnNames = primaryColumns
4✔
1105
                    .map((column) => `"${column.name}"`)
4✔
1106
                    .join(", ")
4✔
1107

4✔
1108
                upQueries.push(
4✔
1109
                    new Query(
4✔
1110
                        `ALTER TABLE ${this.escapePath(
4✔
1111
                            table,
4✔
1112
                        )} DROP CONSTRAINT "${pkName}"`,
4✔
1113
                    ),
4✔
1114
                )
4✔
1115
                downQueries.push(
4✔
1116
                    new Query(
4✔
1117
                        `ALTER TABLE ${this.escapePath(
4✔
1118
                            table,
4✔
1119
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
4✔
1120
                    ),
4✔
1121
                )
4✔
1122
            }
4✔
1123

14✔
1124
            primaryColumns.push(column)
14✔
1125
            const pkName = primaryColumns[0].primaryKeyConstraintName
14✔
1126
                ? primaryColumns[0].primaryKeyConstraintName
14!
1127
                : this.connection.namingStrategy.primaryKeyName(
14✔
1128
                      clonedTable,
14✔
1129
                      primaryColumns.map((column) => column.name),
14✔
1130
                  )
14✔
1131

14✔
1132
            const columnNames = primaryColumns
14✔
1133
                .map((column) => `"${column.name}"`)
14✔
1134
                .join(", ")
14✔
1135
            upQueries.push(
14✔
1136
                new Query(
14✔
1137
                    `ALTER TABLE ${this.escapePath(
14✔
1138
                        table,
14✔
1139
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
14✔
1140
                ),
14✔
1141
            )
14✔
1142
            downQueries.push(
14✔
1143
                new Query(
14✔
1144
                    `ALTER TABLE ${this.escapePath(
14✔
1145
                        table,
14✔
1146
                    )} DROP CONSTRAINT "${pkName}"`,
14✔
1147
                ),
14✔
1148
            )
14✔
1149
        }
14✔
1150

74✔
1151
        // create column index
74✔
1152
        const columnIndex = clonedTable.indices.find(
74✔
1153
            (index) =>
74✔
1154
                index.columnNames.length === 1 &&
2✔
1155
                index.columnNames[0] === column.name,
74✔
1156
        )
74✔
1157
        if (columnIndex) {
74!
1158
            upQueries.push(this.createIndexSql(table, columnIndex))
×
1159
            downQueries.push(this.dropIndexSql(table, columnIndex))
×
1160
        }
×
1161

74✔
1162
        // create unique constraint
74✔
1163
        if (column.isUnique) {
74✔
1164
            const uniqueConstraint = new TableUnique({
6✔
1165
                name: this.connection.namingStrategy.uniqueConstraintName(
6✔
1166
                    table,
6✔
1167
                    [column.name],
6✔
1168
                ),
6✔
1169
                columnNames: [column.name],
6✔
1170
            })
6✔
1171
            clonedTable.uniques.push(uniqueConstraint)
6✔
1172
            upQueries.push(
6✔
1173
                new Query(
6✔
1174
                    `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
6✔
1175
                        uniqueConstraint.name
6✔
1176
                    }" UNIQUE ("${column.name}")`,
6✔
1177
                ),
6✔
1178
            )
6✔
1179
            downQueries.push(
6✔
1180
                new Query(
6✔
1181
                    `ALTER TABLE ${this.escapePath(table)} DROP CONSTRAINT "${
6✔
1182
                        uniqueConstraint.name
6✔
1183
                    }"`,
6✔
1184
                ),
6✔
1185
            )
6✔
1186
        }
6✔
1187

74✔
1188
        // remove default constraint
74✔
1189
        if (column.default !== null && column.default !== undefined) {
74✔
1190
            const defaultName =
10✔
1191
                this.connection.namingStrategy.defaultConstraintName(
10✔
1192
                    table,
10✔
1193
                    column.name,
10✔
1194
                )
10✔
1195
            downQueries.push(
10✔
1196
                new Query(
10✔
1197
                    `ALTER TABLE ${this.escapePath(
10✔
1198
                        table,
10✔
1199
                    )} DROP CONSTRAINT "${defaultName}"`,
10✔
1200
                ),
10✔
1201
            )
10✔
1202
        }
10✔
1203

74✔
1204
        if (column.generatedType && column.asExpression) {
74✔
1205
            const parsedTableName = this.driver.parseTableName(table)
6✔
1206

6✔
1207
            if (!parsedTableName.schema) {
6!
1208
                parsedTableName.schema = await this.getCurrentSchema()
×
1209
            }
×
1210

6✔
1211
            const insertQuery = this.insertTypeormMetadataSql({
6✔
1212
                database: parsedTableName.database,
6✔
1213
                schema: parsedTableName.schema,
6✔
1214
                table: parsedTableName.tableName,
6✔
1215
                type: MetadataTableType.GENERATED_COLUMN,
6✔
1216
                name: column.name,
6✔
1217
                value: column.asExpression,
6✔
1218
            })
6✔
1219

6✔
1220
            const deleteQuery = this.deleteTypeormMetadataSql({
6✔
1221
                database: parsedTableName.database,
6✔
1222
                schema: parsedTableName.schema,
6✔
1223
                table: parsedTableName.tableName,
6✔
1224
                type: MetadataTableType.GENERATED_COLUMN,
6✔
1225
                name: column.name,
6✔
1226
            })
6✔
1227

6✔
1228
            upQueries.push(insertQuery)
6✔
1229
            downQueries.push(deleteQuery)
6✔
1230
        }
6✔
1231

74✔
1232
        await this.executeQueries(upQueries, downQueries)
74✔
1233

70✔
1234
        clonedTable.addColumn(column)
70✔
1235
        this.replaceCachedTable(table, clonedTable)
70✔
1236
    }
70✔
1237

23✔
1238
    /**
23✔
1239
     * Creates a new columns from the column in the table.
23✔
1240
     * @param tableOrName
23✔
1241
     * @param columns
23✔
1242
     */
23✔
1243
    async addColumns(
23✔
1244
        tableOrName: Table | string,
12✔
1245
        columns: TableColumn[],
12✔
1246
    ): Promise<void> {
12✔
1247
        for (const column of columns) {
12✔
1248
            await this.addColumn(tableOrName, column)
14✔
1249
        }
12✔
1250
    }
10✔
1251

23✔
1252
    /**
23✔
1253
     * Renames column in the given table.
23✔
1254
     * @param tableOrName
23✔
1255
     * @param oldTableColumnOrName
23✔
1256
     * @param newTableColumnOrName
23✔
1257
     */
23✔
1258
    async renameColumn(
23✔
1259
        tableOrName: Table | string,
28✔
1260
        oldTableColumnOrName: TableColumn | string,
28✔
1261
        newTableColumnOrName: TableColumn | string,
28✔
1262
    ): Promise<void> {
28✔
1263
        const table = InstanceChecker.isTable(tableOrName)
28✔
1264
            ? tableOrName
28✔
1265
            : await this.getCachedTable(tableOrName)
28✔
1266
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
4✔
1267
            ? oldTableColumnOrName
28✔
1268
            : table.columns.find((c) => c.name === oldTableColumnOrName)
28✔
1269
        if (!oldColumn)
28✔
1270
            throw new TypeORMError(
28!
1271
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
×
1272
            )
×
1273

28✔
1274
        let newColumn: TableColumn
28✔
1275
        if (InstanceChecker.isTableColumn(newTableColumnOrName)) {
28✔
1276
            newColumn = newTableColumnOrName
18✔
1277
        } else {
28✔
1278
            newColumn = oldColumn.clone()
10✔
1279
            newColumn.name = newTableColumnOrName
10✔
1280
        }
10✔
1281

28✔
1282
        await this.changeColumn(table, oldColumn, newColumn)
28✔
1283
    }
28✔
1284

23✔
1285
    /**
23✔
1286
     * Changes a column in the table.
23✔
1287
     * @param tableOrName
23✔
1288
     * @param oldTableColumnOrName
23✔
1289
     * @param newColumn
23✔
1290
     */
23✔
1291
    async changeColumn(
23✔
1292
        tableOrName: Table | string,
104✔
1293
        oldTableColumnOrName: TableColumn | string,
104✔
1294
        newColumn: TableColumn,
104✔
1295
    ): Promise<void> {
104✔
1296
        const table = InstanceChecker.isTable(tableOrName)
104✔
1297
            ? tableOrName
104✔
1298
            : await this.getCachedTable(tableOrName)
104!
1299
        let clonedTable = table.clone()
×
1300
        const upQueries: Query[] = []
×
1301
        const downQueries: Query[] = []
×
1302

×
1303
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
×
1304
            ? oldTableColumnOrName
104✔
1305
            : table.columns.find(
104!
1306
                  (column) => column.name === oldTableColumnOrName,
×
1307
              )
104✔
1308
        if (!oldColumn)
104✔
1309
            throw new TypeORMError(
104!
1310
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
×
1311
            )
×
1312

104✔
1313
        if (
104✔
1314
            (newColumn.isGenerated !== oldColumn.isGenerated &&
104✔
1315
                newColumn.generationStrategy !== "uuid") ||
104✔
1316
            newColumn.type !== oldColumn.type ||
104✔
1317
            newColumn.length !== oldColumn.length ||
104✔
1318
            newColumn.asExpression !== oldColumn.asExpression ||
104✔
1319
            newColumn.generatedType !== oldColumn.generatedType
54✔
1320
        ) {
104✔
1321
            // SQL Server does not support changing of IDENTITY column, so we must drop column and recreate it again.
50✔
1322
            // Also, we recreate column if column type changed
50✔
1323
            await this.dropColumn(table, oldColumn)
50✔
1324
            await this.addColumn(table, newColumn)
50✔
1325

50✔
1326
            // update cloned table
50✔
1327
            clonedTable = table.clone()
50✔
1328
        } else {
104✔
1329
            if (newColumn.name !== oldColumn.name) {
54✔
1330
                // we need database name and schema name to rename FK constraints
28✔
1331
                let dbName: string | undefined = undefined
28✔
1332
                let schemaName: string | undefined = undefined
28✔
1333
                const splittedName = table.name.split(".")
28✔
1334
                if (splittedName.length === 3) {
28✔
1335
                    dbName = splittedName[0]
4✔
1336
                    if (splittedName[1] !== "") schemaName = splittedName[1]
4✔
1337
                } else if (splittedName.length === 2) {
28!
1338
                    schemaName = splittedName[0]
×
1339
                }
×
1340

28✔
1341
                // if we have tables with database which differs from database specified in config, we must change currently used database.
28✔
1342
                // This need because we can not rename objects from another database.
28✔
1343
                const currentDB = await this.getCurrentDatabase()
28✔
1344
                if (dbName && dbName !== currentDB) {
28✔
1345
                    upQueries.push(new Query(`USE "${dbName}"`))
4✔
1346
                    downQueries.push(new Query(`USE "${currentDB}"`))
4✔
1347
                }
4✔
1348

28✔
1349
                // rename the column
28✔
1350
                upQueries.push(
28✔
1351
                    new Query(
28✔
1352
                        `EXEC sp_rename "${this.getTablePath(table)}.${
28✔
1353
                            oldColumn.name
28✔
1354
                        }", "${newColumn.name}"`,
28✔
1355
                    ),
28✔
1356
                )
28✔
1357
                downQueries.push(
28✔
1358
                    new Query(
28✔
1359
                        `EXEC sp_rename "${this.getTablePath(table)}.${
28✔
1360
                            newColumn.name
28✔
1361
                        }", "${oldColumn.name}"`,
28✔
1362
                    ),
28✔
1363
                )
28✔
1364

28✔
1365
                // rename column primary key constraint
28✔
1366
                if (
28✔
1367
                    oldColumn.isPrimary === true &&
28✔
1368
                    !oldColumn.primaryKeyConstraintName
6✔
1369
                ) {
28✔
1370
                    const primaryColumns = clonedTable.primaryColumns
2✔
1371

2✔
1372
                    // build old primary constraint name
2✔
1373
                    const columnNames = primaryColumns.map(
2✔
1374
                        (column) => column.name,
2✔
1375
                    )
2✔
1376
                    const oldPkName =
2✔
1377
                        this.connection.namingStrategy.primaryKeyName(
2✔
1378
                            clonedTable,
2✔
1379
                            columnNames,
2✔
1380
                        )
2✔
1381

2✔
1382
                    // replace old column name with new column name
2✔
1383
                    columnNames.splice(columnNames.indexOf(oldColumn.name), 1)
2✔
1384
                    columnNames.push(newColumn.name)
2✔
1385

2✔
1386
                    // build new primary constraint name
2✔
1387
                    const newPkName =
2✔
1388
                        this.connection.namingStrategy.primaryKeyName(
2✔
1389
                            clonedTable,
2✔
1390
                            columnNames,
2✔
1391
                        )
2✔
1392

2✔
1393
                    // rename primary constraint
2✔
1394
                    upQueries.push(
2✔
1395
                        new Query(
2✔
1396
                            `EXEC sp_rename "${this.getTablePath(
2✔
1397
                                clonedTable,
2✔
1398
                            )}.${oldPkName}", "${newPkName}"`,
2✔
1399
                        ),
2✔
1400
                    )
2✔
1401
                    downQueries.push(
2✔
1402
                        new Query(
2✔
1403
                            `EXEC sp_rename "${this.getTablePath(
2✔
1404
                                clonedTable,
2✔
1405
                            )}.${newPkName}", "${oldPkName}"`,
2✔
1406
                        ),
2✔
1407
                    )
2✔
1408
                }
2✔
1409

28✔
1410
                // rename index constraints
28✔
1411
                clonedTable.findColumnIndices(oldColumn).forEach((index) => {
28✔
1412
                    const oldIndexName =
6✔
1413
                        this.connection.namingStrategy.indexName(
6✔
1414
                            clonedTable,
6✔
1415
                            index.columnNames,
6✔
1416
                            index.where,
6✔
1417
                        )
6✔
1418

6✔
1419
                    // Skip renaming if Index has user defined constraint name
6✔
1420
                    if (index.name !== oldIndexName) return
6✔
1421

2✔
1422
                    // build new constraint name
2✔
1423
                    index.columnNames.splice(
2✔
1424
                        index.columnNames.indexOf(oldColumn.name),
2✔
1425
                        1,
2✔
1426
                    )
2✔
1427
                    index.columnNames.push(newColumn.name)
2✔
1428
                    const newIndexName =
2✔
1429
                        this.connection.namingStrategy.indexName(
2✔
1430
                            clonedTable,
2✔
1431
                            index.columnNames,
2✔
1432
                            index.where,
2✔
1433
                        )
2✔
1434

2✔
1435
                    // build queries
2✔
1436
                    upQueries.push(
2✔
1437
                        new Query(
2✔
1438
                            `EXEC sp_rename "${this.getTablePath(
2✔
1439
                                clonedTable,
2✔
1440
                            )}.${index.name}", "${newIndexName}", "INDEX"`,
2✔
1441
                        ),
2✔
1442
                    )
2✔
1443
                    downQueries.push(
2✔
1444
                        new Query(
2✔
1445
                            `EXEC sp_rename "${this.getTablePath(
2✔
1446
                                clonedTable,
2✔
1447
                            )}.${newIndexName}", "${index.name}", "INDEX"`,
2✔
1448
                        ),
2✔
1449
                    )
2✔
1450

2✔
1451
                    // replace constraint name
2✔
1452
                    index.name = newIndexName
2✔
1453
                })
28✔
1454

28✔
1455
                // rename foreign key constraints
28✔
1456
                clonedTable
28✔
1457
                    .findColumnForeignKeys(oldColumn)
28✔
1458
                    .forEach((foreignKey) => {
28✔
1459
                        const foreignKeyName =
2✔
1460
                            this.connection.namingStrategy.foreignKeyName(
2✔
1461
                                clonedTable,
2✔
1462
                                foreignKey.columnNames,
2✔
1463
                                this.getTablePath(foreignKey),
2✔
1464
                                foreignKey.referencedColumnNames,
2✔
1465
                            )
2✔
1466

2✔
1467
                        // Skip renaming if foreign key has user defined constraint name
2✔
1468
                        if (foreignKey.name !== foreignKeyName) return
2!
1469

2✔
1470
                        // build new constraint name
2✔
1471
                        foreignKey.columnNames.splice(
2✔
1472
                            foreignKey.columnNames.indexOf(oldColumn.name),
2✔
1473
                            1,
2✔
1474
                        )
2✔
1475
                        foreignKey.columnNames.push(newColumn.name)
2✔
1476
                        const newForeignKeyName =
2✔
1477
                            this.connection.namingStrategy.foreignKeyName(
2✔
1478
                                clonedTable,
2✔
1479
                                foreignKey.columnNames,
2✔
1480
                                this.getTablePath(foreignKey),
2✔
1481
                                foreignKey.referencedColumnNames,
2✔
1482
                            )
2✔
1483

2✔
1484
                        // build queries
2✔
1485
                        upQueries.push(
2✔
1486
                            new Query(
2✔
1487
                                `EXEC sp_rename "${this.buildForeignKeyName(
2✔
1488
                                    foreignKey.name!,
2✔
1489
                                    schemaName,
2✔
1490
                                    dbName,
2✔
1491
                                )}", "${newForeignKeyName}"`,
2✔
1492
                            ),
2✔
1493
                        )
2✔
1494
                        downQueries.push(
2✔
1495
                            new Query(
2✔
1496
                                `EXEC sp_rename "${this.buildForeignKeyName(
2✔
1497
                                    newForeignKeyName,
2✔
1498
                                    schemaName,
2✔
1499
                                    dbName,
2✔
1500
                                )}", "${foreignKey.name}"`,
2✔
1501
                            ),
2✔
1502
                        )
2✔
1503

2✔
1504
                        // replace constraint name
2✔
1505
                        foreignKey.name = newForeignKeyName
2✔
1506
                    })
28✔
1507

28✔
1508
                // rename check constraints
28✔
1509
                clonedTable.findColumnChecks(oldColumn).forEach((check) => {
28✔
1510
                    // build new constraint name
×
1511
                    check.columnNames!.splice(
×
1512
                        check.columnNames!.indexOf(oldColumn.name),
×
1513
                        1,
×
1514
                    )
×
1515
                    check.columnNames!.push(newColumn.name)
×
1516
                    const newCheckName =
×
1517
                        this.connection.namingStrategy.checkConstraintName(
×
1518
                            clonedTable,
×
1519
                            check.expression!,
×
1520
                        )
×
1521

×
1522
                    // build queries
×
1523
                    upQueries.push(
×
1524
                        new Query(
×
1525
                            `EXEC sp_rename "${this.getTablePath(
×
1526
                                clonedTable,
×
1527
                            )}.${check.name}", "${newCheckName}"`,
×
1528
                        ),
×
1529
                    )
×
1530
                    downQueries.push(
×
1531
                        new Query(
×
1532
                            `EXEC sp_rename "${this.getTablePath(
×
1533
                                clonedTable,
×
1534
                            )}.${newCheckName}", "${check.name}"`,
×
1535
                        ),
×
1536
                    )
×
1537

×
1538
                    // replace constraint name
×
1539
                    check.name = newCheckName
×
1540
                })
28✔
1541

28✔
1542
                // rename unique constraints
28✔
1543
                clonedTable.findColumnUniques(oldColumn).forEach((unique) => {
28✔
1544
                    const oldUniqueName =
12✔
1545
                        this.connection.namingStrategy.uniqueConstraintName(
12✔
1546
                            clonedTable,
12✔
1547
                            unique.columnNames,
12✔
1548
                        )
12✔
1549

12✔
1550
                    // Skip renaming if Unique has user defined constraint name
12✔
1551
                    if (unique.name !== oldUniqueName) return
12✔
1552

8✔
1553
                    // build new constraint name
8✔
1554
                    unique.columnNames.splice(
8✔
1555
                        unique.columnNames.indexOf(oldColumn.name),
8✔
1556
                        1,
8✔
1557
                    )
8✔
1558
                    unique.columnNames.push(newColumn.name)
8✔
1559
                    const newUniqueName =
8✔
1560
                        this.connection.namingStrategy.uniqueConstraintName(
8✔
1561
                            clonedTable,
8✔
1562
                            unique.columnNames,
8✔
1563
                        )
8✔
1564

8✔
1565
                    // build queries
8✔
1566
                    upQueries.push(
8✔
1567
                        new Query(
8✔
1568
                            `EXEC sp_rename "${this.getTablePath(
8✔
1569
                                clonedTable,
8✔
1570
                            )}.${unique.name}", "${newUniqueName}"`,
8✔
1571
                        ),
8✔
1572
                    )
8✔
1573
                    downQueries.push(
8✔
1574
                        new Query(
8✔
1575
                            `EXEC sp_rename "${this.getTablePath(
8✔
1576
                                clonedTable,
8✔
1577
                            )}.${newUniqueName}", "${unique.name}"`,
8✔
1578
                        ),
8✔
1579
                    )
8✔
1580

8✔
1581
                    // replace constraint name
8✔
1582
                    unique.name = newUniqueName
8✔
1583
                })
28✔
1584

28✔
1585
                // rename default constraints
28✔
1586
                if (
28✔
1587
                    oldColumn.default !== null &&
28✔
1588
                    oldColumn.default !== undefined
28✔
1589
                ) {
28✔
1590
                    const oldDefaultName =
4✔
1591
                        this.connection.namingStrategy.defaultConstraintName(
4✔
1592
                            table,
4✔
1593
                            oldColumn.name,
4✔
1594
                        )
4✔
1595
                    const newDefaultName =
4✔
1596
                        this.connection.namingStrategy.defaultConstraintName(
4✔
1597
                            table,
4✔
1598
                            newColumn.name,
4✔
1599
                        )
4✔
1600

4✔
1601
                    upQueries.push(
4✔
1602
                        new Query(
4✔
1603
                            `ALTER TABLE ${this.escapePath(
4✔
1604
                                table,
4✔
1605
                            )} DROP CONSTRAINT "${oldDefaultName}"`,
4✔
1606
                        ),
4✔
1607
                    )
4✔
1608
                    downQueries.push(
4✔
1609
                        new Query(
4✔
1610
                            `ALTER TABLE ${this.escapePath(
4✔
1611
                                table,
4✔
1612
                            )} ADD CONSTRAINT "${oldDefaultName}" DEFAULT ${
4✔
1613
                                oldColumn.default
4✔
1614
                            } FOR "${newColumn.name}"`,
4✔
1615
                        ),
4✔
1616
                    )
4✔
1617

4✔
1618
                    upQueries.push(
4✔
1619
                        new Query(
4✔
1620
                            `ALTER TABLE ${this.escapePath(
4✔
1621
                                table,
4✔
1622
                            )} ADD CONSTRAINT "${newDefaultName}" DEFAULT ${
4✔
1623
                                oldColumn.default
4✔
1624
                            } FOR "${newColumn.name}"`,
4✔
1625
                        ),
4✔
1626
                    )
4✔
1627
                    downQueries.push(
4✔
1628
                        new Query(
4✔
1629
                            `ALTER TABLE ${this.escapePath(
4✔
1630
                                table,
4✔
1631
                            )} DROP CONSTRAINT "${newDefaultName}"`,
4✔
1632
                        ),
4✔
1633
                    )
4✔
1634
                }
4✔
1635

28✔
1636
                // change currently used database back to default db.
28✔
1637
                if (dbName && dbName !== currentDB) {
28✔
1638
                    upQueries.push(new Query(`USE "${currentDB}"`))
4✔
1639
                    downQueries.push(new Query(`USE "${dbName}"`))
4✔
1640
                }
4✔
1641

28✔
1642
                // rename old column in the Table object
28✔
1643
                const oldTableColumn = clonedTable.columns.find(
28✔
1644
                    (column) => column.name === oldColumn.name,
28✔
1645
                )
28✔
1646
                clonedTable.columns[
28✔
1647
                    clonedTable.columns.indexOf(oldTableColumn!)
28✔
1648
                ].name = newColumn.name
28✔
1649
                oldColumn.name = newColumn.name
28✔
1650
            }
28✔
1651

54✔
1652
            if (
54✔
1653
                this.isColumnChanged(oldColumn, newColumn, false, false, false)
54✔
1654
            ) {
54!
1655
                upQueries.push(
×
1656
                    new Query(
×
1657
                        `ALTER TABLE ${this.escapePath(
×
1658
                            table,
×
1659
                        )} ALTER COLUMN ${this.buildCreateColumnSql(
×
1660
                            table,
×
1661
                            newColumn,
×
1662
                            true,
×
1663
                            false,
×
1664
                            true,
×
1665
                        )}`,
×
1666
                    ),
×
1667
                )
×
1668
                downQueries.push(
×
1669
                    new Query(
×
1670
                        `ALTER TABLE ${this.escapePath(
×
1671
                            table,
×
1672
                        )} ALTER COLUMN ${this.buildCreateColumnSql(
×
1673
                            table,
×
1674
                            oldColumn,
×
1675
                            true,
×
1676
                            false,
×
1677
                            true,
×
1678
                        )}`,
×
1679
                    ),
×
1680
                )
×
1681
            }
×
1682

54✔
1683
            if (this.isEnumChanged(oldColumn, newColumn)) {
54✔
1684
                const oldExpression = this.getEnumExpression(oldColumn)
2✔
1685
                const oldCheck = new TableCheck({
2✔
1686
                    name: this.connection.namingStrategy.checkConstraintName(
2✔
1687
                        table,
2✔
1688
                        oldExpression,
2✔
1689
                        true,
2✔
1690
                    ),
2✔
1691
                    expression: oldExpression,
2✔
1692
                })
2✔
1693

2✔
1694
                const newExpression = this.getEnumExpression(newColumn)
2✔
1695
                const newCheck = new TableCheck({
2✔
1696
                    name: this.connection.namingStrategy.checkConstraintName(
2✔
1697
                        table,
2✔
1698
                        newExpression,
2✔
1699
                        true,
2✔
1700
                    ),
2✔
1701
                    expression: newExpression,
2✔
1702
                })
2✔
1703

2✔
1704
                upQueries.push(this.dropCheckConstraintSql(table, oldCheck))
2✔
1705
                upQueries.push(this.createCheckConstraintSql(table, newCheck))
2✔
1706

2✔
1707
                downQueries.push(this.dropCheckConstraintSql(table, newCheck))
2✔
1708
                downQueries.push(this.createCheckConstraintSql(table, oldCheck))
2✔
1709
            }
2✔
1710

54✔
1711
            if (newColumn.isPrimary !== oldColumn.isPrimary) {
54✔
1712
                const primaryColumns = clonedTable.primaryColumns
8✔
1713

8✔
1714
                // if primary column state changed, we must always drop existed constraint.
8✔
1715
                if (primaryColumns.length > 0) {
8✔
1716
                    const pkName = primaryColumns[0].primaryKeyConstraintName
6✔
1717
                        ? primaryColumns[0].primaryKeyConstraintName
6!
1718
                        : this.connection.namingStrategy.primaryKeyName(
6✔
1719
                              clonedTable,
6✔
1720
                              primaryColumns.map((column) => column.name),
6✔
1721
                          )
6✔
1722

6✔
1723
                    const columnNames = primaryColumns
6✔
1724
                        .map((column) => `"${column.name}"`)
6✔
1725
                        .join(", ")
6✔
1726
                    upQueries.push(
6✔
1727
                        new Query(
6✔
1728
                            `ALTER TABLE ${this.escapePath(
6✔
1729
                                table,
6✔
1730
                            )} DROP CONSTRAINT "${pkName}"`,
6✔
1731
                        ),
6✔
1732
                    )
6✔
1733
                    downQueries.push(
6✔
1734
                        new Query(
6✔
1735
                            `ALTER TABLE ${this.escapePath(
6✔
1736
                                table,
6✔
1737
                            )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
6✔
1738
                        ),
6✔
1739
                    )
6✔
1740
                }
6✔
1741

8✔
1742
                if (newColumn.isPrimary === true) {
8✔
1743
                    primaryColumns.push(newColumn)
4✔
1744
                    // update column in table
4✔
1745
                    const column = clonedTable.columns.find(
4✔
1746
                        (column) => column.name === newColumn.name,
4✔
1747
                    )
4✔
1748
                    column!.isPrimary = true
4✔
1749
                    const pkName = primaryColumns[0].primaryKeyConstraintName
4✔
1750
                        ? primaryColumns[0].primaryKeyConstraintName
4!
1751
                        : this.connection.namingStrategy.primaryKeyName(
4✔
1752
                              clonedTable,
4✔
1753
                              primaryColumns.map((column) => column.name),
4✔
1754
                          )
4✔
1755

4✔
1756
                    const columnNames = primaryColumns
4✔
1757
                        .map((column) => `"${column.name}"`)
4✔
1758
                        .join(", ")
4✔
1759
                    upQueries.push(
4✔
1760
                        new Query(
4✔
1761
                            `ALTER TABLE ${this.escapePath(
4✔
1762
                                table,
4✔
1763
                            )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
4✔
1764
                        ),
4✔
1765
                    )
4✔
1766
                    downQueries.push(
4✔
1767
                        new Query(
4✔
1768
                            `ALTER TABLE ${this.escapePath(
4✔
1769
                                table,
4✔
1770
                            )} DROP CONSTRAINT "${pkName}"`,
4✔
1771
                        ),
4✔
1772
                    )
4✔
1773
                } else {
4✔
1774
                    const primaryColumn = primaryColumns.find(
4✔
1775
                        (c) => c.name === newColumn.name,
4✔
1776
                    )
4✔
1777
                    primaryColumns.splice(
4✔
1778
                        primaryColumns.indexOf(primaryColumn!),
4✔
1779
                        1,
4✔
1780
                    )
4✔
1781

4✔
1782
                    // update column in table
4✔
1783
                    const column = clonedTable.columns.find(
4✔
1784
                        (column) => column.name === newColumn.name,
4✔
1785
                    )
4✔
1786
                    column!.isPrimary = false
4✔
1787

4✔
1788
                    // if we have another primary keys, we must recreate constraint.
4✔
1789
                    if (primaryColumns.length > 0) {
4✔
1790
                        const pkName = primaryColumns[0]
2✔
1791
                            .primaryKeyConstraintName
2✔
1792
                            ? primaryColumns[0].primaryKeyConstraintName
2!
1793
                            : this.connection.namingStrategy.primaryKeyName(
2✔
1794
                                  clonedTable,
2✔
1795
                                  primaryColumns.map((column) => column.name),
2✔
1796
                              )
2✔
1797

2✔
1798
                        const columnNames = primaryColumns
2✔
1799
                            .map((column) => `"${column.name}"`)
2✔
1800
                            .join(", ")
2✔
1801
                        upQueries.push(
2✔
1802
                            new Query(
2✔
1803
                                `ALTER TABLE ${this.escapePath(
2✔
1804
                                    table,
2✔
1805
                                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
2✔
1806
                            ),
2✔
1807
                        )
2✔
1808
                        downQueries.push(
2✔
1809
                            new Query(
2✔
1810
                                `ALTER TABLE ${this.escapePath(
2✔
1811
                                    table,
2✔
1812
                                )} DROP CONSTRAINT "${pkName}"`,
2✔
1813
                            ),
2✔
1814
                        )
2✔
1815
                    }
2✔
1816
                }
4✔
1817
            }
8✔
1818

54✔
1819
            if (newColumn.isUnique !== oldColumn.isUnique) {
54✔
1820
                if (newColumn.isUnique === true) {
2✔
1821
                    const uniqueConstraint = new TableUnique({
2✔
1822
                        name: this.connection.namingStrategy.uniqueConstraintName(
2✔
1823
                            table,
2✔
1824
                            [newColumn.name],
2✔
1825
                        ),
2✔
1826
                        columnNames: [newColumn.name],
2✔
1827
                    })
2✔
1828
                    clonedTable.uniques.push(uniqueConstraint)
2✔
1829
                    upQueries.push(
2✔
1830
                        new Query(
2✔
1831
                            `ALTER TABLE ${this.escapePath(
2✔
1832
                                table,
2✔
1833
                            )} ADD CONSTRAINT "${
2✔
1834
                                uniqueConstraint.name
2✔
1835
                            }" UNIQUE ("${newColumn.name}")`,
2✔
1836
                        ),
2✔
1837
                    )
2✔
1838
                    downQueries.push(
2✔
1839
                        new Query(
2✔
1840
                            `ALTER TABLE ${this.escapePath(
2✔
1841
                                table,
2✔
1842
                            )} DROP CONSTRAINT "${uniqueConstraint.name}"`,
2✔
1843
                        ),
2✔
1844
                    )
2✔
1845
                } else {
2!
1846
                    const uniqueConstraint = clonedTable.uniques.find(
×
1847
                        (unique) => {
×
1848
                            return (
×
1849
                                unique.columnNames.length === 1 &&
×
1850
                                !!unique.columnNames.find(
×
1851
                                    (columnName) =>
×
1852
                                        columnName === newColumn.name,
×
1853
                                )
×
1854
                            )
×
1855
                        },
×
1856
                    )
×
1857
                    clonedTable.uniques.splice(
×
1858
                        clonedTable.uniques.indexOf(uniqueConstraint!),
×
1859
                        1,
×
1860
                    )
×
1861
                    upQueries.push(
×
1862
                        new Query(
×
1863
                            `ALTER TABLE ${this.escapePath(
×
1864
                                table,
×
1865
                            )} DROP CONSTRAINT "${uniqueConstraint!.name}"`,
×
1866
                        ),
×
1867
                    )
×
1868
                    downQueries.push(
×
1869
                        new Query(
×
1870
                            `ALTER TABLE ${this.escapePath(
×
1871
                                table,
×
1872
                            )} ADD CONSTRAINT "${
×
1873
                                uniqueConstraint!.name
×
1874
                            }" UNIQUE ("${newColumn.name}")`,
×
1875
                        ),
×
1876
                    )
×
1877
                }
×
1878
            }
2✔
1879

54✔
1880
            if (newColumn.default !== oldColumn.default) {
54✔
1881
                // (note) if there is a previous default, we need to drop its constraint first
4✔
1882
                if (
4✔
1883
                    oldColumn.default !== null &&
4✔
1884
                    oldColumn.default !== undefined
4✔
1885
                ) {
4✔
1886
                    const defaultName =
2✔
1887
                        this.connection.namingStrategy.defaultConstraintName(
2✔
1888
                            table,
2✔
1889
                            oldColumn.name,
2✔
1890
                        )
2✔
1891
                    upQueries.push(
2✔
1892
                        new Query(
2✔
1893
                            `ALTER TABLE ${this.escapePath(
2✔
1894
                                table,
2✔
1895
                            )} DROP CONSTRAINT "${defaultName}"`,
2✔
1896
                        ),
2✔
1897
                    )
2✔
1898
                    downQueries.push(
2✔
1899
                        new Query(
2✔
1900
                            `ALTER TABLE ${this.escapePath(
2✔
1901
                                table,
2✔
1902
                            )} ADD CONSTRAINT "${defaultName}" DEFAULT ${
2✔
1903
                                oldColumn.default
2✔
1904
                            } FOR "${oldColumn.name}"`,
2✔
1905
                        ),
2✔
1906
                    )
2✔
1907
                }
2✔
1908

4✔
1909
                if (
4✔
1910
                    newColumn.default !== null &&
4✔
1911
                    newColumn.default !== undefined
4✔
1912
                ) {
4✔
1913
                    const defaultName =
4✔
1914
                        this.connection.namingStrategy.defaultConstraintName(
4✔
1915
                            table,
4✔
1916
                            newColumn.name,
4✔
1917
                        )
4✔
1918
                    upQueries.push(
4✔
1919
                        new Query(
4✔
1920
                            `ALTER TABLE ${this.escapePath(
4✔
1921
                                table,
4✔
1922
                            )} ADD CONSTRAINT "${defaultName}" DEFAULT ${
4✔
1923
                                newColumn.default
4✔
1924
                            } FOR "${newColumn.name}"`,
4✔
1925
                        ),
4✔
1926
                    )
4✔
1927
                    downQueries.push(
4✔
1928
                        new Query(
4✔
1929
                            `ALTER TABLE ${this.escapePath(
4✔
1930
                                table,
4✔
1931
                            )} DROP CONSTRAINT "${defaultName}"`,
4✔
1932
                        ),
4✔
1933
                    )
4✔
1934
                }
4✔
1935
            }
4✔
1936

54✔
1937
            await this.executeQueries(upQueries, downQueries)
54✔
1938
            this.replaceCachedTable(table, clonedTable)
54✔
1939
        }
54✔
1940
    }
104✔
1941

23✔
1942
    /**
23✔
1943
     * Changes a column in the table.
23✔
1944
     * @param tableOrName
23✔
1945
     * @param changedColumns
23✔
1946
     */
23✔
1947
    async changeColumns(
23✔
1948
        tableOrName: Table | string,
46✔
1949
        changedColumns: { newColumn: TableColumn; oldColumn: TableColumn }[],
46✔
1950
    ): Promise<void> {
46✔
1951
        for (const { oldColumn, newColumn } of changedColumns) {
46✔
1952
            await this.changeColumn(tableOrName, oldColumn, newColumn)
64✔
1953
        }
64✔
1954
    }
46✔
1955

23✔
1956
    /**
23✔
1957
     * Drops column in the table.
23✔
1958
     * @param tableOrName
23✔
1959
     * @param columnOrName
23✔
1960
     * @param ifExists
23✔
1961
     */
23✔
1962
    async dropColumn(
23✔
1963
        tableOrName: Table | string,
88✔
1964
        columnOrName: TableColumn | string,
88✔
1965
        ifExists?: boolean,
88✔
1966
    ): Promise<void> {
88✔
1967
        const table = InstanceChecker.isTable(tableOrName)
88✔
1968
            ? tableOrName
88✔
1969
            : await this.getCachedTable(tableOrName)
88✔
1970
        const column = InstanceChecker.isTableColumn(columnOrName)
8✔
1971
            ? columnOrName
88✔
1972
            : table.findColumnByName(columnOrName)
88✔
1973
        if (!column) {
88✔
1974
            if (ifExists) return
4✔
1975
            throw new TypeORMError(
2✔
1976
                `Column "${columnOrName}" was not found in table "${table.name}"`,
2✔
1977
            )
2✔
1978
        }
2✔
1979

84✔
1980
        const clonedTable = table.clone()
84✔
1981
        const upQueries: Query[] = []
84✔
1982
        const downQueries: Query[] = []
84✔
1983

84✔
1984
        // drop primary key constraint
84✔
1985
        if (column.isPrimary) {
88✔
1986
            const pkName = column.primaryKeyConstraintName
14✔
1987
                ? column.primaryKeyConstraintName
14!
1988
                : this.connection.namingStrategy.primaryKeyName(
14✔
1989
                      clonedTable,
14✔
1990
                      clonedTable.primaryColumns.map((column) => column.name),
14✔
1991
                  )
14✔
1992

14✔
1993
            const columnNames = clonedTable.primaryColumns
14✔
1994
                .map((primaryColumn) => `"${primaryColumn.name}"`)
14✔
1995
                .join(", ")
14✔
1996

14✔
1997
            upQueries.push(
14✔
1998
                new Query(
14✔
1999
                    `ALTER TABLE ${this.escapePath(
14✔
2000
                        clonedTable,
14✔
2001
                    )} DROP CONSTRAINT "${pkName}"`,
14✔
2002
                ),
14✔
2003
            )
14✔
2004
            downQueries.push(
14✔
2005
                new Query(
14✔
2006
                    `ALTER TABLE ${this.escapePath(
14✔
2007
                        clonedTable,
14✔
2008
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
14✔
2009
                ),
14✔
2010
            )
14✔
2011

14✔
2012
            // update column in table
14✔
2013
            const tableColumn = clonedTable.findColumnByName(column.name)
14✔
2014
            tableColumn!.isPrimary = false
14✔
2015

14✔
2016
            // if primary key have multiple columns, we must recreate it without dropped column
14✔
2017
            if (clonedTable.primaryColumns.length > 0) {
14✔
2018
                const pkName = clonedTable.primaryColumns[0]
2✔
2019
                    .primaryKeyConstraintName
2✔
2020
                    ? clonedTable.primaryColumns[0].primaryKeyConstraintName
2!
2021
                    : this.connection.namingStrategy.primaryKeyName(
2✔
2022
                          clonedTable,
2✔
2023
                          clonedTable.primaryColumns.map(
2✔
2024
                              (column) => column.name,
2✔
2025
                          ),
2✔
2026
                      )
2✔
2027

2✔
2028
                const columnNames = clonedTable.primaryColumns
2✔
2029
                    .map((primaryColumn) => `"${primaryColumn.name}"`)
2✔
2030
                    .join(", ")
2✔
2031
                upQueries.push(
2✔
2032
                    new Query(
2✔
2033
                        `ALTER TABLE ${this.escapePath(
2✔
2034
                            clonedTable,
2✔
2035
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
2✔
2036
                    ),
2✔
2037
                )
2✔
2038
                downQueries.push(
2✔
2039
                    new Query(
2✔
2040
                        `ALTER TABLE ${this.escapePath(
2✔
2041
                            clonedTable,
2✔
2042
                        )} DROP CONSTRAINT "${pkName}"`,
2✔
2043
                    ),
2✔
2044
                )
2✔
2045
            }
2✔
2046
        }
14✔
2047

84✔
2048
        // drop column index
84✔
2049
        const columnIndex = clonedTable.indices.find(
84✔
2050
            (index) =>
84✔
2051
                index.columnNames.length === 1 &&
2✔
2052
                index.columnNames[0] === column.name,
84✔
2053
        )
84✔
2054
        if (columnIndex) {
88!
2055
            clonedTable.indices.splice(
×
2056
                clonedTable.indices.indexOf(columnIndex),
×
2057
                1,
×
2058
            )
×
2059
            upQueries.push(this.dropIndexSql(table, columnIndex))
×
2060
            downQueries.push(this.createIndexSql(table, columnIndex))
×
2061
        }
×
2062

84✔
2063
        // drop column check
84✔
2064
        const columnCheck = clonedTable.checks.find(
84✔
2065
            (check) =>
84✔
2066
                !!check.columnNames &&
32✔
2067
                check.columnNames.length === 1 &&
32✔
2068
                check.columnNames[0] === column.name,
84✔
2069
        )
84✔
2070
        if (columnCheck) {
88✔
2071
            clonedTable.checks.splice(
4✔
2072
                clonedTable.checks.indexOf(columnCheck),
4✔
2073
                1,
4✔
2074
            )
4✔
2075
            upQueries.push(this.dropCheckConstraintSql(table, columnCheck))
4✔
2076
            downQueries.push(this.createCheckConstraintSql(table, columnCheck))
4✔
2077
        }
4✔
2078

84✔
2079
        // drop column unique
84✔
2080
        const columnUnique = clonedTable.uniques.find(
84✔
2081
            (unique) =>
84✔
2082
                unique.columnNames.length === 1 &&
60✔
2083
                unique.columnNames[0] === column.name,
84✔
2084
        )
84✔
2085
        if (columnUnique) {
88✔
2086
            clonedTable.uniques.splice(
6✔
2087
                clonedTable.uniques.indexOf(columnUnique),
6✔
2088
                1,
6✔
2089
            )
6✔
2090
            upQueries.push(this.dropUniqueConstraintSql(table, columnUnique))
6✔
2091
            downQueries.push(
6✔
2092
                this.createUniqueConstraintSql(table, columnUnique),
6✔
2093
            )
6✔
2094
        }
6✔
2095

84✔
2096
        // drop default constraint
84✔
2097
        if (column.default !== null && column.default !== undefined) {
88✔
2098
            const defaultName =
12✔
2099
                this.connection.namingStrategy.defaultConstraintName(
12✔
2100
                    table,
12✔
2101
                    column.name,
12✔
2102
                )
12✔
2103
            upQueries.push(
12✔
2104
                new Query(
12✔
2105
                    `ALTER TABLE ${this.escapePath(
12✔
2106
                        table,
12✔
2107
                    )} DROP CONSTRAINT "${defaultName}"`,
12✔
2108
                ),
12✔
2109
            )
12✔
2110
            downQueries.push(
12✔
2111
                new Query(
12✔
2112
                    `ALTER TABLE ${this.escapePath(
12✔
2113
                        table,
12✔
2114
                    )} ADD CONSTRAINT "${defaultName}" DEFAULT ${
12✔
2115
                        column.default
12✔
2116
                    } FOR "${column.name}"`,
12✔
2117
                ),
12✔
2118
            )
12✔
2119
        }
12✔
2120

84✔
2121
        if (column.generatedType && column.asExpression) {
88✔
2122
            const parsedTableName = this.driver.parseTableName(table)
8✔
2123

8✔
2124
            if (!parsedTableName.schema) {
8!
2125
                parsedTableName.schema = await this.getCurrentSchema()
×
2126
            }
×
2127

8✔
2128
            const deleteQuery = this.deleteTypeormMetadataSql({
8✔
2129
                database: parsedTableName.database,
8✔
2130
                schema: parsedTableName.schema,
8✔
2131
                table: parsedTableName.tableName,
8✔
2132
                type: MetadataTableType.GENERATED_COLUMN,
8✔
2133
                name: column.name,
8✔
2134
            })
8✔
2135
            const insertQuery = this.insertTypeormMetadataSql({
8✔
2136
                database: parsedTableName.database,
8✔
2137
                schema: parsedTableName.schema,
8✔
2138
                table: parsedTableName.tableName,
8✔
2139
                type: MetadataTableType.GENERATED_COLUMN,
8✔
2140
                name: column.name,
8✔
2141
                value: column.asExpression,
8✔
2142
            })
8✔
2143

8✔
2144
            upQueries.push(deleteQuery)
8✔
2145
            downQueries.push(insertQuery)
8✔
2146
        }
8✔
2147

84✔
2148
        upQueries.push(
84✔
2149
            new Query(
84✔
2150
                `ALTER TABLE ${this.escapePath(table)} DROP COLUMN "${
84✔
2151
                    column.name
84✔
2152
                }"`,
84✔
2153
            ),
84✔
2154
        )
84✔
2155
        downQueries.push(
84✔
2156
            new Query(
84✔
2157
                `ALTER TABLE ${this.escapePath(
84✔
2158
                    table,
84✔
2159
                )} ADD ${this.buildCreateColumnSql(
84✔
2160
                    table,
84✔
2161
                    column,
84✔
2162
                    false,
84✔
2163
                    false,
84✔
2164
                )}`,
84✔
2165
            ),
84✔
2166
        )
84✔
2167

84✔
2168
        await this.executeQueries(upQueries, downQueries)
84✔
2169

84✔
2170
        clonedTable.removeColumn(column)
84✔
2171
        this.replaceCachedTable(table, clonedTable)
84✔
2172
    }
84✔
2173

23✔
2174
    /**
23✔
2175
     * Drops the columns in the table.
23✔
2176
     * @param tableOrName
23✔
2177
     * @param columns
23✔
2178
     * @param ifExists
23✔
2179
     */
23✔
2180
    async dropColumns(
23✔
2181
        tableOrName: Table | string,
10✔
2182
        columns: TableColumn[] | string[],
10✔
2183
        ifExists?: boolean,
10✔
2184
    ): Promise<void> {
10✔
2185
        for (const column of [...columns]) {
10✔
2186
            await this.dropColumn(tableOrName, column, ifExists)
26✔
2187
        }
26✔
2188
    }
10✔
2189

23✔
2190
    /**
23✔
2191
     * Creates a new primary key.
23✔
2192
     * @param tableOrName
23✔
2193
     * @param columnNames
23✔
2194
     * @param constraintName
23✔
2195
     */
23✔
2196
    async createPrimaryKey(
23✔
2197
        tableOrName: Table | string,
4✔
2198
        columnNames: string[],
4✔
2199
        constraintName?: string,
4✔
2200
    ): Promise<void> {
4✔
2201
        const table = InstanceChecker.isTable(tableOrName)
4✔
2202
            ? tableOrName
4!
2203
            : await this.getCachedTable(tableOrName)
4✔
2204
        const clonedTable = table.clone()
4✔
2205

4✔
2206
        const up = this.createPrimaryKeySql(table, columnNames, constraintName)
4✔
2207

4✔
2208
        // mark columns as primary, because dropPrimaryKeySql build constraint name from table primary column names.
4✔
2209
        clonedTable.columns.forEach((column) => {
4✔
2210
            if (columnNames.find((columnName) => columnName === column.name))
10✔
2211
                column.isPrimary = true
10✔
2212
        })
4✔
2213
        const down = this.dropPrimaryKeySql(clonedTable)
4✔
2214

4✔
2215
        await this.executeQueries(up, down)
4✔
2216
        this.replaceCachedTable(table, clonedTable)
4✔
2217
    }
4✔
2218

23✔
2219
    /**
23✔
2220
     * Updates composite primary keys.
23✔
2221
     * @param tableOrName
23✔
2222
     * @param columns
23✔
2223
     */
23✔
2224
    async updatePrimaryKeys(
23✔
2225
        tableOrName: Table | string,
6✔
2226
        columns: TableColumn[],
6✔
2227
    ): Promise<void> {
6✔
2228
        const table = InstanceChecker.isTable(tableOrName)
6✔
2229
            ? tableOrName
6✔
2230
            : await this.getCachedTable(tableOrName)
6!
2231
        const clonedTable = table.clone()
×
2232
        const columnNames = columns.map((column) => column.name)
✔
2233
        const upQueries: Query[] = []
×
2234
        const downQueries: Query[] = []
×
2235

×
2236
        // if table already have primary columns, we must drop them.
×
2237
        const primaryColumns = clonedTable.primaryColumns
×
2238
        if (primaryColumns.length > 0) {
6✔
2239
            const pkName = primaryColumns[0].primaryKeyConstraintName
6✔
2240
                ? primaryColumns[0].primaryKeyConstraintName
6!
2241
                : this.connection.namingStrategy.primaryKeyName(
6✔
2242
                      clonedTable,
6✔
2243
                      primaryColumns.map((column) => column.name),
6✔
2244
                  )
6✔
2245

6✔
2246
            const columnNamesString = primaryColumns
6✔
2247
                .map((column) => `"${column.name}"`)
6✔
2248
                .join(", ")
6✔
2249

6✔
2250
            upQueries.push(
6✔
2251
                new Query(
6✔
2252
                    `ALTER TABLE ${this.escapePath(
6✔
2253
                        table,
6✔
2254
                    )} DROP CONSTRAINT "${pkName}"`,
6✔
2255
                ),
6✔
2256
            )
6✔
2257
            downQueries.push(
6✔
2258
                new Query(
6✔
2259
                    `ALTER TABLE ${this.escapePath(
6✔
2260
                        table,
6✔
2261
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
6✔
2262
                ),
6✔
2263
            )
6✔
2264
        }
6✔
2265

6✔
2266
        // update columns in table.
6✔
2267
        clonedTable.columns
6✔
2268
            .filter((column) => columnNames.indexOf(column.name) !== -1)
6✔
2269
            .forEach((column) => {
6✔
2270
                column.isPrimary = true
12✔
2271
            })
6✔
2272

6✔
2273
        const pkName = primaryColumns[0].primaryKeyConstraintName
6✔
2274
            ? primaryColumns[0].primaryKeyConstraintName
6!
2275
            : this.connection.namingStrategy.primaryKeyName(
6✔
2276
                  clonedTable,
6✔
2277
                  columnNames,
6✔
2278
              )
6✔
2279

6✔
2280
        const columnNamesString = columnNames
6✔
2281
            .map((columnName) => `"${columnName}"`)
6✔
2282
            .join(", ")
6✔
2283

6✔
2284
        upQueries.push(
6✔
2285
            new Query(
6✔
2286
                `ALTER TABLE ${this.escapePath(
6✔
2287
                    table,
6✔
2288
                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
6✔
2289
            ),
6✔
2290
        )
6✔
2291
        downQueries.push(
6✔
2292
            new Query(
6✔
2293
                `ALTER TABLE ${this.escapePath(
6✔
2294
                    table,
6✔
2295
                )} DROP CONSTRAINT "${pkName}"`,
6✔
2296
            ),
6✔
2297
        )
6✔
2298

6✔
2299
        await this.executeQueries(upQueries, downQueries)
6✔
2300
        this.replaceCachedTable(table, clonedTable)
6✔
2301
    }
6✔
2302

23✔
2303
    /**
23✔
2304
     * Drops a primary key.
23✔
2305
     * @param tableOrName
23✔
2306
     * @param constraintName
23✔
2307
     * @param ifExists
23✔
2308
     */
23✔
2309
    async dropPrimaryKey(
23✔
2310
        tableOrName: Table | string,
8✔
2311
        constraintName?: string,
8✔
2312
        ifExists?: boolean,
8✔
2313
    ): Promise<void> {
8✔
2314
        const table = InstanceChecker.isTable(tableOrName)
8✔
2315
            ? tableOrName
8✔
2316
            : await this.getCachedTable(tableOrName)
8✔
2317
        if (ifExists && table.primaryColumns.length === 0) return
8!
2318

8✔
2319
        const up = this.dropPrimaryKeySql(table)
8✔
2320
        const down = this.createPrimaryKeySql(
8✔
2321
            table,
8✔
2322
            table.primaryColumns.map((column) => column.name),
8✔
2323
            constraintName,
8✔
2324
        )
8✔
2325
        await this.executeQueries(up, down)
8✔
2326
        table.primaryColumns.forEach((column) => {
8✔
2327
            column.isPrimary = false
8✔
2328
        })
8✔
2329
    }
8✔
2330

23✔
2331
    /**
23✔
2332
     * Creates a new unique constraint.
23✔
2333
     * @param tableOrName
23✔
2334
     * @param uniqueConstraint
23✔
2335
     */
23✔
2336
    async createUniqueConstraint(
23✔
2337
        tableOrName: Table | string,
16✔
2338
        uniqueConstraint: TableUnique,
16✔
2339
    ): Promise<void> {
16✔
2340
        const table = InstanceChecker.isTable(tableOrName)
16✔
2341
            ? tableOrName
16✔
2342
            : await this.getCachedTable(tableOrName)
16✔
2343

12✔
2344
        // new unique constraint may be passed without name. In this case we generate unique name manually.
12✔
2345
        if (!uniqueConstraint.name)
12✔
2346
            uniqueConstraint.name =
16✔
2347
                this.connection.namingStrategy.uniqueConstraintName(
4✔
2348
                    table,
4✔
2349
                    uniqueConstraint.columnNames,
4✔
2350
                )
4✔
2351

16✔
2352
        const up = this.createUniqueConstraintSql(table, uniqueConstraint)
16✔
2353
        const down = this.dropUniqueConstraintSql(table, uniqueConstraint)
16✔
2354
        await this.executeQueries(up, down)
16✔
2355
        table.addUniqueConstraint(uniqueConstraint)
16✔
2356
    }
16✔
2357

23✔
2358
    /**
23✔
2359
     * Creates a new unique constraints.
23✔
2360
     * @param tableOrName
23✔
2361
     * @param uniqueConstraints
23✔
2362
     */
23✔
2363
    async createUniqueConstraints(
23✔
2364
        tableOrName: Table | string,
6✔
2365
        uniqueConstraints: TableUnique[],
6✔
2366
    ): Promise<void> {
6✔
2367
        const promises = uniqueConstraints.map((uniqueConstraint) =>
6✔
2368
            this.createUniqueConstraint(tableOrName, uniqueConstraint),
6✔
2369
        )
6✔
2370
        await Promise.all(promises)
6✔
2371
    }
6✔
2372

23✔
2373
    /**
23✔
2374
     * Drops unique constraint.
23✔
2375
     * @param tableOrName
23✔
2376
     * @param uniqueOrName
23✔
2377
     * @param ifExists
23✔
2378
     */
23✔
2379
    async dropUniqueConstraint(
23✔
2380
        tableOrName: Table | string,
18✔
2381
        uniqueOrName: TableUnique | string,
18✔
2382
        ifExists?: boolean,
18✔
2383
    ): Promise<void> {
18✔
2384
        const table = InstanceChecker.isTable(tableOrName)
18✔
2385
            ? tableOrName
18✔
2386
            : await this.getCachedTable(tableOrName)
18✔
2387
        const uniqueConstraint = InstanceChecker.isTableUnique(uniqueOrName)
10✔
2388
            ? uniqueOrName
18✔
2389
            : table.uniques.find((u) => u.name === uniqueOrName)
18✔
2390
        if (!uniqueConstraint) {
18✔
2391
            if (ifExists) return
2✔
2392
            throw new TypeORMError(
×
2393
                `Supplied unique constraint was not found in table ${table.name}`,
×
2394
            )
×
NEW
2395
        }
×
2396

16✔
2397
        const up = this.dropUniqueConstraintSql(table, uniqueConstraint)
16✔
2398
        const down = this.createUniqueConstraintSql(table, uniqueConstraint)
16✔
2399
        await this.executeQueries(up, down)
16✔
2400
        table.removeUniqueConstraint(uniqueConstraint)
16✔
2401
    }
16✔
2402

23✔
2403
    /**
23✔
2404
     * Drops unique constraints.
23✔
2405
     * @param tableOrName
23✔
2406
     * @param uniqueConstraints
23✔
2407
     * @param ifExists
23✔
2408
     */
23✔
2409
    async dropUniqueConstraints(
23✔
2410
        tableOrName: Table | string,
8✔
2411
        uniqueConstraints: TableUnique[],
8✔
2412
        ifExists?: boolean,
8✔
2413
    ): Promise<void> {
8✔
2414
        const promises = uniqueConstraints.map((uniqueConstraint) =>
8✔
2415
            this.dropUniqueConstraint(tableOrName, uniqueConstraint, ifExists),
8✔
2416
        )
8✔
2417
        await Promise.all(promises)
8✔
2418
    }
8✔
2419

23✔
2420
    /**
23✔
2421
     * Creates a new check constraint.
23✔
2422
     * @param tableOrName
23✔
2423
     * @param checkConstraint
23✔
2424
     */
23✔
2425
    async createCheckConstraint(
23✔
2426
        tableOrName: Table | string,
10✔
2427
        checkConstraint: TableCheck,
10✔
2428
    ): Promise<void> {
10✔
2429
        const table = InstanceChecker.isTable(tableOrName)
10✔
2430
            ? tableOrName
10✔
2431
            : await this.getCachedTable(tableOrName)
10✔
2432

6✔
2433
        // new unique constraint may be passed without name. In this case we generate unique name manually.
6✔
2434
        if (!checkConstraint.name)
6✔
2435
            checkConstraint.name =
6✔
2436
                this.connection.namingStrategy.checkConstraintName(
6✔
2437
                    table,
6✔
2438
                    checkConstraint.expression!,
6✔
2439
                )
6✔
2440

10✔
2441
        const up = this.createCheckConstraintSql(table, checkConstraint)
10✔
2442
        const down = this.dropCheckConstraintSql(table, checkConstraint)
10✔
2443
        await this.executeQueries(up, down)
10✔
2444
        table.addCheckConstraint(checkConstraint)
10✔
2445
    }
10✔
2446

23✔
2447
    /**
23✔
2448
     * Creates a new check constraints.
23✔
2449
     * @param tableOrName
23✔
2450
     * @param checkConstraints
23✔
2451
     */
23✔
2452
    async createCheckConstraints(
23✔
2453
        tableOrName: Table | string,
4✔
2454
        checkConstraints: TableCheck[],
4✔
2455
    ): Promise<void> {
4✔
2456
        const promises = checkConstraints.map((checkConstraint) =>
4✔
2457
            this.createCheckConstraint(tableOrName, checkConstraint),
4✔
2458
        )
4✔
2459
        await Promise.all(promises)
4✔
2460
    }
4✔
2461

23✔
2462
    /**
23✔
2463
     * Drops check constraint.
23✔
2464
     * @param tableOrName
23✔
2465
     * @param checkOrName
23✔
2466
     * @param ifExists
23✔
2467
     */
23✔
2468
    async dropCheckConstraint(
23✔
2469
        tableOrName: Table | string,
8✔
2470
        checkOrName: TableCheck | string,
8✔
2471
        ifExists?: boolean,
8✔
2472
    ): Promise<void> {
8✔
2473
        const table = InstanceChecker.isTable(tableOrName)
8✔
2474
            ? tableOrName
8✔
2475
            : await this.getCachedTable(tableOrName)
8✔
2476
        const checkConstraint = InstanceChecker.isTableCheck(checkOrName)
2✔
2477
            ? checkOrName
8✔
2478
            : table.checks.find((c) => c.name === checkOrName)
8✔
2479
        if (!checkConstraint) {
8✔
2480
            if (ifExists) return
2✔
2481
            throw new TypeORMError(
×
2482
                `Supplied check constraint was not found in table ${table.name}`,
×
2483
            )
×
NEW
2484
        }
×
2485

6✔
2486
        const up = this.dropCheckConstraintSql(table, checkConstraint)
6✔
2487
        const down = this.createCheckConstraintSql(table, checkConstraint)
6✔
2488
        await this.executeQueries(up, down)
6✔
2489
        table.removeCheckConstraint(checkConstraint)
6✔
2490
    }
6✔
2491

23✔
2492
    /**
23✔
2493
     * Drops check constraints.
23✔
2494
     * @param tableOrName
23✔
2495
     * @param checkConstraints
23✔
2496
     * @param ifExists
23✔
2497
     */
23✔
2498
    async dropCheckConstraints(
23✔
2499
        tableOrName: Table | string,
4✔
2500
        checkConstraints: TableCheck[],
4✔
2501
        ifExists?: boolean,
4✔
2502
    ): Promise<void> {
4✔
2503
        const promises = checkConstraints.map((checkConstraint) =>
4✔
2504
            this.dropCheckConstraint(tableOrName, checkConstraint, ifExists),
4✔
2505
        )
4✔
2506
        await Promise.all(promises)
4✔
2507
    }
4✔
2508

23✔
2509
    /**
23✔
2510
     * Creates a new exclusion constraint.
23✔
2511
     * @param tableOrName
23✔
2512
     * @param exclusionConstraint
23✔
2513
     */
23✔
2514
    async createExclusionConstraint(
23✔
2515
        tableOrName: Table | string,
×
2516
        exclusionConstraint: TableExclusion,
×
2517
    ): Promise<void> {
×
2518
        throw new TypeORMError(
×
2519
            `SqlServer does not support exclusion constraints.`,
×
2520
        )
×
2521
    }
×
2522

23✔
2523
    /**
23✔
2524
     * Creates a new exclusion constraints.
23✔
2525
     * @param tableOrName
23✔
2526
     * @param exclusionConstraints
23✔
2527
     */
23✔
2528
    async createExclusionConstraints(
23✔
2529
        tableOrName: Table | string,
×
2530
        exclusionConstraints: TableExclusion[],
×
2531
    ): Promise<void> {
×
2532
        throw new TypeORMError(
×
2533
            `SqlServer does not support exclusion constraints.`,
×
2534
        )
×
2535
    }
×
2536

23✔
2537
    /**
23✔
2538
     * Drops exclusion constraint.
23✔
2539
     * @param tableOrName
23✔
2540
     * @param exclusionOrName
23✔
2541
     * @param ifExists
23✔
2542
     */
23✔
2543
    async dropExclusionConstraint(
23✔
2544
        tableOrName: Table | string,
×
2545
        exclusionOrName: TableExclusion | string,
×
NEW
2546
        ifExists?: boolean,
×
2547
    ): Promise<void> {
×
2548
        throw new TypeORMError(
×
2549
            `SqlServer does not support exclusion constraints.`,
×
2550
        )
×
2551
    }
×
2552

23✔
2553
    /**
23✔
2554
     * Drops exclusion constraints.
23✔
2555
     * @param tableOrName
23✔
2556
     * @param exclusionConstraints
23✔
2557
     * @param ifExists
23✔
2558
     */
23✔
2559
    async dropExclusionConstraints(
23✔
2560
        tableOrName: Table | string,
×
2561
        exclusionConstraints: TableExclusion[],
×
NEW
2562
        ifExists?: boolean,
×
2563
    ): Promise<void> {
×
2564
        throw new TypeORMError(
×
2565
            `SqlServer does not support exclusion constraints.`,
×
2566
        )
×
2567
    }
×
2568

23✔
2569
    /**
23✔
2570
     * Creates a new foreign key.
23✔
2571
     * @param tableOrName
23✔
2572
     * @param foreignKey
23✔
2573
     */
23✔
2574
    async createForeignKey(
23✔
2575
        tableOrName: Table | string,
6,920✔
2576
        foreignKey: TableForeignKey,
6,920✔
2577
    ): Promise<void> {
6,920✔
2578
        const table = InstanceChecker.isTable(tableOrName)
6,920✔
2579
            ? tableOrName
6,920✔
2580
            : await this.getCachedTable(tableOrName)
6,920✔
2581
        const metadata = this.connection.hasMetadata(table.name)
10✔
2582
            ? this.connection.getMetadata(table.name)
6,920✔
2583
            : undefined
6,920✔
2584

6,920✔
2585
        if (
6,920✔
2586
            metadata &&
6,920✔
2587
            metadata.treeParentRelation &&
6,920✔
2588
            metadata.treeParentRelation!.isTreeParent &&
6,920✔
2589
            metadata.foreignKeys.find(
778✔
2590
                (foreignKey) => foreignKey.onDelete !== "NO ACTION",
778✔
2591
            )
6,920✔
2592
        )
6,920✔
2593
            throw new TypeORMError(
6,920!
2594
                "SqlServer does not support options in TreeParent.",
×
2595
            )
×
2596

6,920✔
2597
        // new FK may be passed without name. In this case we generate FK name manually.
6,920✔
2598
        if (!foreignKey.name)
6,920✔
2599
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
6,920✔
2600
                table,
2✔
2601
                foreignKey.columnNames,
2✔
2602
                this.getTablePath(foreignKey),
2✔
2603
                foreignKey.referencedColumnNames,
2✔
2604
            )
2✔
2605

6,920✔
2606
        const up = this.createForeignKeySql(table, foreignKey)
6,920✔
2607
        const down = this.dropForeignKeySql(table, foreignKey)
6,920✔
2608
        await this.executeQueries(up, down)
6,920✔
2609
        table.addForeignKey(foreignKey)
6,920✔
2610
    }
6,920✔
2611

23✔
2612
    /**
23✔
2613
     * Creates a new foreign keys.
23✔
2614
     * @param tableOrName
23✔
2615
     * @param foreignKeys
23✔
2616
     */
23✔
2617
    async createForeignKeys(
23✔
2618
        tableOrName: Table | string,
4,290✔
2619
        foreignKeys: TableForeignKey[],
4,290✔
2620
    ): Promise<void> {
4,290✔
2621
        const promises = foreignKeys.map((foreignKey) =>
4,290✔
2622
            this.createForeignKey(tableOrName, foreignKey),
4,290✔
2623
        )
4,290✔
2624
        await Promise.all(promises)
4,290✔
2625
    }
4,290✔
2626

23✔
2627
    /**
23✔
2628
     * Drops a foreign key from the table.
23✔
2629
     * @param tableOrName
23✔
2630
     * @param foreignKeyOrName
23✔
2631
     * @param ifExists
23✔
2632
     */
23✔
2633
    async dropForeignKey(
23✔
2634
        tableOrName: Table | string,
24✔
2635
        foreignKeyOrName: TableForeignKey | string,
24✔
2636
        ifExists?: boolean,
24✔
2637
    ): Promise<void> {
24✔
2638
        const table = InstanceChecker.isTable(tableOrName)
24✔
2639
            ? tableOrName
24✔
2640
            : await this.getCachedTable(tableOrName)
24✔
2641
        const foreignKey = InstanceChecker.isTableForeignKey(foreignKeyOrName)
10✔
2642
            ? foreignKeyOrName
24✔
2643
            : table.foreignKeys.find((fk) => fk.name === foreignKeyOrName)
24✔
2644
        if (!foreignKey) {
24✔
2645
            if (ifExists) return
2✔
2646
            throw new TypeORMError(
×
2647
                `Supplied foreign key was not found in table ${table.name}`,
×
2648
            )
×
NEW
2649
        }
×
2650

22✔
2651
        if (!foreignKey.name) {
24!
2652
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
×
2653
                table,
×
2654
                foreignKey.columnNames,
×
2655
                this.getTablePath(foreignKey),
×
2656
                foreignKey.referencedColumnNames,
×
2657
            )
×
2658
        }
×
2659

22✔
2660
        const up = this.dropForeignKeySql(table, foreignKey)
22✔
2661
        const down = this.createForeignKeySql(table, foreignKey)
22✔
2662
        await this.executeQueries(up, down)
22✔
2663
        table.removeForeignKey(foreignKey)
22✔
2664
    }
22✔
2665

23✔
2666
    /**
23✔
2667
     * Drops a foreign keys from the table.
23✔
2668
     * @param tableOrName
23✔
2669
     * @param foreignKeys
23✔
2670
     * @param ifExists
23✔
2671
     */
23✔
2672
    async dropForeignKeys(
23✔
2673
        tableOrName: Table | string,
14✔
2674
        foreignKeys: TableForeignKey[],
14✔
2675
        ifExists?: boolean,
14✔
2676
    ): Promise<void> {
14✔
2677
        const promises = foreignKeys.map((foreignKey) =>
14✔
2678
            this.dropForeignKey(tableOrName, foreignKey, ifExists),
14✔
2679
        )
14✔
2680
        await Promise.all(promises)
14✔
2681
    }
14✔
2682

23✔
2683
    /**
23✔
2684
     * Creates a new index.
23✔
2685
     * @param tableOrName
23✔
2686
     * @param index
23✔
2687
     */
23✔
2688
    async createIndex(
23✔
2689
        tableOrName: Table | string,
28✔
2690
        index: TableIndex,
28✔
2691
    ): Promise<void> {
28✔
2692
        const table = InstanceChecker.isTable(tableOrName)
28✔
2693
            ? tableOrName
28✔
2694
            : await this.getCachedTable(tableOrName)
28✔
2695

14✔
2696
        // new index may be passed without name. In this case we generate index name manually.
14✔
2697
        if (!index.name) index.name = this.generateIndexName(table, index)
28✔
2698

28✔
2699
        const up = this.createIndexSql(table, index)
28✔
2700
        const down = this.dropIndexSql(table, index)
28✔
2701
        await this.executeQueries(up, down)
28✔
2702
        table.addIndex(index)
28✔
2703
    }
28✔
2704

23✔
2705
    /**
23✔
2706
     * Creates a new indices
23✔
2707
     * @param tableOrName
23✔
2708
     * @param indices
23✔
2709
     */
23✔
2710
    async createIndices(
23✔
2711
        tableOrName: Table | string,
14✔
2712
        indices: TableIndex[],
14✔
2713
    ): Promise<void> {
14✔
2714
        const promises = indices.map((index) =>
14✔
2715
            this.createIndex(tableOrName, index),
14✔
2716
        )
14✔
2717
        await Promise.all(promises)
14✔
2718
    }
14✔
2719

23✔
2720
    /**
23✔
2721
     * Drops an index.
23✔
2722
     * @param tableOrName
23✔
2723
     * @param indexOrName
23✔
2724
     * @param ifExists
23✔
2725
     */
23✔
2726
    async dropIndex(
23✔
2727
        tableOrName: Table | string,
34✔
2728
        indexOrName: TableIndex | string,
34✔
2729
        ifExists?: boolean,
34✔
2730
    ): Promise<void> {
34✔
2731
        const table = InstanceChecker.isTable(tableOrName)
34✔
2732
            ? tableOrName
34✔
2733
            : await this.getCachedTable(tableOrName)
34✔
2734
        const index = InstanceChecker.isTableIndex(indexOrName)
12✔
2735
            ? indexOrName
34✔
2736
            : table.indices.find((i) => i.name === indexOrName)
34✔
2737
        if (!index) {
34✔
2738
            if (ifExists) return
2✔
2739
            throw new TypeORMError(
×
2740
                `Supplied index was not found in table ${table.name}`,
×
2741
            )
×
NEW
2742
        }
×
2743

32✔
2744
        // old index may be passed without name. In this case we generate index name manually.
32✔
2745
        if (!index.name) index.name = this.generateIndexName(table, index)
34✔
2746

32✔
2747
        const up = this.dropIndexSql(table, index)
32✔
2748
        const down = this.createIndexSql(table, index)
32✔
2749
        await this.executeQueries(up, down)
32✔
2750
        table.removeIndex(index)
32✔
2751
    }
32✔
2752

23✔
2753
    /**
23✔
2754
     * Drops an indices from the table.
23✔
2755
     * @param tableOrName
23✔
2756
     * @param indices
23✔
2757
     * @param ifExists
23✔
2758
     */
23✔
2759
    async dropIndices(
23✔
2760
        tableOrName: Table | string,
4✔
2761
        indices: TableIndex[],
4✔
2762
        ifExists?: boolean,
4✔
2763
    ): Promise<void> {
4✔
2764
        const promises = indices.map((index) =>
4✔
2765
            this.dropIndex(tableOrName, index, ifExists),
4✔
2766
        )
4✔
2767
        await Promise.all(promises)
4✔
2768
    }
4✔
2769

23✔
2770
    /**
23✔
2771
     * Clears all table contents.
23✔
2772
     * Note: this operation uses SQL's TRUNCATE query which cannot be reverted in transactions.
23✔
2773
     * @param tablePath
23✔
2774
     * @param options
23✔
2775
     * @param options.cascade
23✔
2776
     */
23✔
2777
    async clearTable(
23✔
2778
        tablePath: string,
10✔
2779
        options?: { cascade?: boolean },
10✔
2780
    ): Promise<void> {
10✔
2781
        if (options?.cascade) {
10✔
2782
            throw new TypeORMError(
2✔
2783
                `SqlServer does not support clearing table with cascade option`,
2✔
2784
            )
2✔
2785
        }
2✔
2786
        await this.query(`TRUNCATE TABLE ${this.escapePath(tablePath)}`)
8✔
2787
    }
6✔
2788

23✔
2789
    /**
23✔
2790
     * Removes all tables from the currently connected database.
23✔
2791
     * @param database
23✔
2792
     */
23✔
2793
    async clearDatabase(database?: string): Promise<void> {
23✔
2794
        if (database) {
3,008✔
2795
            const isDatabaseExist = await this.hasDatabase(database)
3,008✔
2796
            if (!isDatabaseExist) return Promise.resolve()
3,008!
2797
        }
3,008✔
2798

3,008✔
2799
        const isAnotherTransactionActive = this.isTransactionActive
3,008✔
2800
        if (!isAnotherTransactionActive) await this.startTransaction()
3,008✔
2801
        try {
3,008✔
2802
            const allViewsSql = database
3,008✔
2803
                ? `SELECT * FROM "${database}"."INFORMATION_SCHEMA"."VIEWS"`
3,008✔
2804
                : `SELECT * FROM "INFORMATION_SCHEMA"."VIEWS"`
3,008!
2805
            const allViewsResults: ObjectLiteral[] =
3,008✔
2806
                await this.query(allViewsSql)
3,008✔
2807

3,008✔
2808
            await Promise.all(
3,008✔
2809
                allViewsResults.map((viewResult) => {
3,008✔
2810
                    // 'DROP VIEW' does not allow specifying the database name as a prefix to the object name.
16✔
2811
                    const dropTableSql = `DROP VIEW "${viewResult["TABLE_SCHEMA"]}"."${viewResult["TABLE_NAME"]}"`
16✔
2812
                    return this.query(dropTableSql)
16✔
2813
                }),
3,008✔
2814
            )
3,008✔
2815

3,008✔
2816
            const allTablesSql = database
3,008✔
2817
                ? `SELECT * FROM "${database}"."INFORMATION_SCHEMA"."TABLES" WHERE "TABLE_TYPE" = 'BASE TABLE'`
3,008✔
2818
                : `SELECT * FROM "INFORMATION_SCHEMA"."TABLES" WHERE "TABLE_TYPE" = 'BASE TABLE'`
3,008!
2819
            const allTablesResults: ObjectLiteral[] =
3,008✔
2820
                await this.query(allTablesSql)
3,008✔
2821

3,008✔
2822
            if (allTablesResults.length > 0) {
3,008✔
2823
                const tablesByCatalog: {
2,964✔
2824
                    [key: string]: {
2,964✔
2825
                        TABLE_NAME: string
2,964✔
2826
                        TABLE_SCHEMA: string
2,964✔
2827
                    }[]
2,964✔
2828
                } = allTablesResults.reduce(
2,964✔
2829
                    (c, { TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME }) => {
2,964✔
2830
                        c[TABLE_CATALOG] = c[TABLE_CATALOG] || []
10,462✔
2831
                        c[TABLE_CATALOG].push({ TABLE_SCHEMA, TABLE_NAME })
10,462✔
2832
                        return c
10,462✔
2833
                    },
2,964✔
2834
                    {},
2,964✔
2835
                )
2,964✔
2836

2,964✔
2837
                const foreignKeysSql = Object.entries(tablesByCatalog)
2,964✔
2838
                    .map(([TABLE_CATALOG, tables]) => {
2,964✔
2839
                        const conditions = tables
2,964✔
2840
                            .map(({ TABLE_SCHEMA, TABLE_NAME }) => {
2,964✔
2841
                                return `("fk"."referenced_object_id" = OBJECT_ID('"${TABLE_CATALOG}"."${TABLE_SCHEMA}"."${TABLE_NAME}"'))`
10,462✔
2842
                            })
2,964✔
2843
                            .join(" OR ")
2,964✔
2844

2,964✔
2845
                        return `
2,964✔
2846
                        SELECT DISTINCT '${TABLE_CATALOG}' AS                                              "TABLE_CATALOG",
2,964✔
2847
                                        OBJECT_SCHEMA_NAME("fk"."parent_object_id",
2,964✔
2848
                                                           DB_ID('${TABLE_CATALOG}')) AS                   "TABLE_SCHEMA",
2,964✔
2849
                                        OBJECT_NAME("fk"."parent_object_id", DB_ID('${TABLE_CATALOG}')) AS "TABLE_NAME",
2,964✔
2850
                                        "fk"."name" AS                                                     "CONSTRAINT_NAME"
2,964✔
2851
                        FROM "${TABLE_CATALOG}"."sys"."foreign_keys" AS "fk"
2,964✔
2852
                        WHERE (${conditions})
2,964✔
2853
                    `
2,964✔
2854
                    })
2,964✔
2855
                    .join(" UNION ALL ")
2,964✔
2856

2,964✔
2857
                const foreignKeys: {
2,964✔
2858
                    TABLE_CATALOG: string
2,964✔
2859
                    TABLE_SCHEMA: string
2,964✔
2860
                    TABLE_NAME: string
2,964✔
2861
                    CONSTRAINT_NAME: string
2,964✔
2862
                }[] = await this.query(foreignKeysSql)
2,964✔
2863

2,964✔
2864
                await Promise.all(
2,964✔
2865
                    foreignKeys.map(
2,964✔
2866
                        async ({
2,964✔
2867
                            TABLE_CATALOG,
6,886✔
2868
                            TABLE_SCHEMA,
6,886✔
2869
                            TABLE_NAME,
6,886✔
2870
                            CONSTRAINT_NAME,
6,886✔
2871
                        }) => {
6,886✔
2872
                            // Disable the constraint first.
6,886✔
2873
                            await this.query(
6,886✔
2874
                                `ALTER TABLE "${TABLE_CATALOG}"."${TABLE_SCHEMA}"."${TABLE_NAME}" ` +
6,886✔
2875
                                    `NOCHECK CONSTRAINT "${CONSTRAINT_NAME}"`,
6,886✔
2876
                            )
6,886✔
2877

6,886✔
2878
                            await this.query(
6,886✔
2879
                                `ALTER TABLE "${TABLE_CATALOG}"."${TABLE_SCHEMA}"."${TABLE_NAME}" ` +
6,886✔
2880
                                    `DROP CONSTRAINT "${CONSTRAINT_NAME}" -- FROM CLEAR`,
6,886✔
2881
                            )
6,886✔
2882
                        },
2,964✔
2883
                    ),
2,964✔
2884
                )
2,964✔
2885

2,964✔
2886
                await Promise.all(
2,964✔
2887
                    allTablesResults.map(async (tablesResult) => {
2,964✔
2888
                        if (tablesResult["TABLE_NAME"].startsWith("#")) {
10,462!
2889
                            // don't try to drop temporary tables
×
2890
                            return
×
2891
                        }
×
2892

10,462✔
2893
                        const dropTableSql = `DROP TABLE "${tablesResult["TABLE_CATALOG"]}"."${tablesResult["TABLE_SCHEMA"]}"."${tablesResult["TABLE_NAME"]}"`
10,462✔
2894
                        return this.query(dropTableSql)
10,462✔
2895
                    }),
2,964✔
2896
                )
2,964✔
2897
            }
2,964✔
2898

3,008✔
2899
            if (!isAnotherTransactionActive) await this.commitTransaction()
3,008✔
2900
        } catch (error) {
3,008!
2901
            try {
×
2902
                // we throw original error even if rollback thrown an error
×
2903
                if (!isAnotherTransactionActive)
×
2904
                    await this.rollbackTransaction()
×
2905
            } catch (rollbackError) {}
×
2906
            throw error
×
2907
        }
×
2908
    }
3,008✔
2909

23✔
2910
    // -------------------------------------------------------------------------
23✔
2911
    // Protected Methods
23✔
2912
    // -------------------------------------------------------------------------
23✔
2913

23✔
2914
    protected async loadViews(viewPaths?: string[]): Promise<View[]> {
23✔
2915
        const hasTable = await this.hasTable(this.getTypeormMetadataTableName())
3,118✔
2916
        if (!hasTable) {
3,118✔
2917
            return []
3,088✔
2918
        }
3,088✔
2919

30✔
2920
        if (!viewPaths) {
3,118!
2921
            viewPaths = []
×
2922
        }
×
2923

30✔
2924
        const currentSchema = await this.getCurrentSchema()
30✔
2925
        const currentDatabase = await this.getCurrentDatabase()
30✔
2926

30✔
2927
        const dbNames = viewPaths
30✔
2928
            .map((viewPath) => this.driver.parseTableName(viewPath).database)
30✔
2929
            .filter((database) => database)
30✔
2930

30✔
2931
        if (
30✔
2932
            this.driver.database &&
30✔
2933
            !dbNames.find((dbName) => dbName === this.driver.database)
30✔
2934
        )
3,118✔
2935
            dbNames.push(this.driver.database)
3,118✔
2936

30✔
2937
        const viewsCondition = viewPaths
30✔
2938
            .map((viewPath) => {
30✔
2939
                let { schema, tableName: name } =
22✔
2940
                    this.driver.parseTableName(viewPath)
22✔
2941

22✔
2942
                if (!schema) {
22!
2943
                    schema = currentSchema
×
2944
                }
×
2945
                return `("T"."SCHEMA" = '${schema}' AND "T"."NAME" = '${name}')`
22✔
2946
            })
30✔
2947
            .join(" OR ")
30✔
2948

30✔
2949
        const query = dbNames
30✔
2950
            .map((dbName) => {
30✔
2951
                return (
38✔
2952
                    `SELECT "T".*, "V"."CHECK_OPTION" FROM ${this.escapePath(
38✔
2953
                        this.getTypeormMetadataTableName(),
38✔
2954
                    )} "t" ` +
38✔
2955
                    `INNER JOIN "${dbName}"."INFORMATION_SCHEMA"."VIEWS" "V" ON "V"."TABLE_SCHEMA" = "T"."SCHEMA" AND "v"."TABLE_NAME" = "T"."NAME" WHERE "T"."TYPE" = '${
38✔
2956
                        MetadataTableType.VIEW
38✔
2957
                    }' ${viewsCondition ? `AND (${viewsCondition})` : ""}`
38✔
2958
                )
38✔
2959
            })
30✔
2960
            .join(" UNION ALL ")
30✔
2961

30✔
2962
        const dbViews = await this.query(query)
30✔
2963
        return dbViews.map((dbView: any) => {
30✔
2964
            const view = new View()
6✔
2965
            const db =
6✔
2966
                dbView["TABLE_CATALOG"] === currentDatabase
6✔
2967
                    ? undefined
6!
2968
                    : dbView["TABLE_CATALOG"]
6✔
2969
            const schema =
6✔
2970
                dbView["schema"] === currentSchema &&
6✔
2971
                !this.driver.options.schema
6✔
2972
                    ? undefined
6✔
2973
                    : dbView["schema"]
6!
2974
            view.database = dbView["TABLE_CATALOG"]
6✔
2975
            view.schema = dbView["schema"]
6✔
2976
            view.name = this.driver.buildTableName(dbView["name"], schema, db)
6✔
2977
            view.expression = dbView["value"]
6✔
2978
            return view
6✔
2979
        })
30✔
2980
    }
30✔
2981

23✔
2982
    /**
23✔
2983
     * Loads all tables (with given names) from the database and creates a Table from them.
23✔
2984
     * @param tableNames
23✔
2985
     */
23✔
2986
    protected async loadTables(tableNames?: string[]): Promise<Table[]> {
23✔
2987
        // if no tables given then no need to proceed
3,704✔
2988
        if (tableNames && tableNames.length === 0) {
3,704✔
2989
            return []
14✔
2990
        }
14✔
2991

3,690✔
2992
        const currentSchema = await this.getCurrentSchema()
3,690✔
2993
        const currentDatabase = await this.getCurrentDatabase()
3,690✔
2994

3,690✔
2995
        const dbTables: {
3,690✔
2996
            TABLE_CATALOG: string
3,690✔
2997
            TABLE_SCHEMA: string
3,690✔
2998
            TABLE_NAME: string
3,690✔
2999
        }[] = []
3,690✔
3000

3,690✔
3001
        if (!tableNames) {
3,704!
3002
            const databasesSql =
×
3003
                `SELECT DISTINCT "name" ` +
×
3004
                `FROM "master"."dbo"."sysdatabases" ` +
×
3005
                `WHERE "name" NOT IN ('master', 'model', 'msdb')`
×
3006
            const dbDatabases: { name: string }[] =
×
3007
                await this.query(databasesSql)
×
3008

×
3009
            const tablesSql = dbDatabases
×
3010
                .map(({ name }) => {
×
3011
                    return `
×
3012
                    SELECT DISTINCT
×
3013
                        "TABLE_CATALOG", "TABLE_SCHEMA", "TABLE_NAME"
×
3014
                    FROM "${name}"."INFORMATION_SCHEMA"."TABLES"
×
3015
                    WHERE
×
3016
                      "TABLE_TYPE" = 'BASE TABLE'
×
3017
                      AND
×
3018
                      "TABLE_CATALOG" = '${name}'
×
3019
                      AND
×
3020
                      ISNULL(Objectproperty(Object_id("TABLE_CATALOG" + '.' + "TABLE_SCHEMA" + '.' + "TABLE_NAME"), 'IsMSShipped'), 0) = 0
×
3021
                `
×
3022
                })
×
3023
                .join(" UNION ALL ")
×
3024

×
3025
            dbTables.push(...(await this.query(tablesSql)))
×
3026
        } else {
3,704✔
3027
            const tableNamesByCatalog = tableNames
3,690✔
3028
                .map((tableName) => this.driver.parseTableName(tableName))
3,690✔
3029
                .reduce(
3,690✔
3030
                    (c, { database, ...other }) => {
3,690✔
3031
                        database = database || currentDatabase
11,472!
3032
                        c[database] = c[database] || []
11,472✔
3033
                        c[database].push({
11,472✔
3034
                            schema: other.schema || currentSchema,
11,472!
3035
                            tableName: other.tableName,
11,472✔
3036
                        })
11,472✔
3037
                        return c
11,472✔
3038
                    },
3,690✔
3039
                    {} as {
3,690✔
3040
                        [key: string]: { schema: string; tableName: string }[]
3,690✔
3041
                    },
3,690✔
3042
                )
3,690✔
3043

3,690✔
3044
            const tablesSql = Object.entries(tableNamesByCatalog)
3,690✔
3045
                .map(([database, tables]) => {
3,690✔
3046
                    const tablesCondition = tables
3,698✔
3047
                        .map(({ schema, tableName }) => {
3,698✔
3048
                            return `("TABLE_SCHEMA" = '${schema}' AND "TABLE_NAME" = '${tableName}')`
11,472✔
3049
                        })
3,698✔
3050
                        .join(" OR ")
3,698✔
3051

3,698✔
3052
                    return `
3,698✔
3053
                    SELECT DISTINCT
3,698✔
3054
                        "TABLE_CATALOG", "TABLE_SCHEMA", "TABLE_NAME"
3,698✔
3055
                    FROM "${database}"."INFORMATION_SCHEMA"."TABLES"
3,698✔
3056
                    WHERE
3,698✔
3057
                          "TABLE_TYPE" = 'BASE TABLE' AND
3,698✔
3058
                          "TABLE_CATALOG" = '${database}' AND
3,698✔
3059
                          ${tablesCondition}
3,698✔
3060
                `
3,698✔
3061
                })
3,690✔
3062
                .join(" UNION ALL ")
3,690✔
3063

3,690✔
3064
            dbTables.push(...(await this.query(tablesSql)))
3,690✔
3065
        }
3,690✔
3066

3,690✔
3067
        // if tables were not found in the db, no need to proceed
3,690✔
3068
        if (dbTables.length === 0) {
3,704✔
3069
            return []
3,012✔
3070
        }
3,012✔
3071

678✔
3072
        const dbTablesByCatalog = dbTables.reduce(
678✔
3073
            (c, { TABLE_CATALOG, ...other }) => {
678✔
3074
                c[TABLE_CATALOG] = c[TABLE_CATALOG] || []
1,002✔
3075
                c[TABLE_CATALOG].push(other)
1,002✔
3076
                return c
1,002✔
3077
            },
678✔
3078
            {} as {
678✔
3079
                [key: string]: { TABLE_NAME: string; TABLE_SCHEMA: string }[]
678✔
3080
            },
678✔
3081
        )
678✔
3082

678✔
3083
        const columnsSql = Object.entries(dbTablesByCatalog)
678✔
3084
            .map(([TABLE_CATALOG, tables]) => {
678✔
3085
                const condition = tables
678✔
3086
                    .map(
678✔
3087
                        ({ TABLE_SCHEMA, TABLE_NAME }) =>
678✔
3088
                            `("TABLE_SCHEMA" = '${TABLE_SCHEMA}' AND "TABLE_NAME" = '${TABLE_NAME}')`,
678✔
3089
                    )
678✔
3090
                    .join("OR")
678✔
3091

678✔
3092
                return (
678✔
3093
                    `SELECT "COLUMNS".*, "cc"."is_persisted", "cc"."definition" ` +
678✔
3094
                    `FROM "${TABLE_CATALOG}"."INFORMATION_SCHEMA"."COLUMNS" ` +
678✔
3095
                    `LEFT JOIN "sys"."computed_columns" "cc" ON COL_NAME("cc"."object_id", "cc"."column_id") = "column_name" ` +
678✔
3096
                    `WHERE (${condition})`
678✔
3097
                )
678✔
3098
            })
678✔
3099
            .join(" UNION ALL ")
678✔
3100

678✔
3101
        const constraintsSql = Object.entries(dbTablesByCatalog)
678✔
3102
            .map(([TABLE_CATALOG, tables]) => {
678✔
3103
                const conditions = tables
678✔
3104
                    .map(
678✔
3105
                        ({ TABLE_NAME, TABLE_SCHEMA }) =>
678✔
3106
                            `("columnUsages"."TABLE_SCHEMA" = '${TABLE_SCHEMA}' AND "columnUsages"."TABLE_NAME" = '${TABLE_NAME}')`,
678✔
3107
                    )
678✔
3108
                    .join(" OR ")
678✔
3109

678✔
3110
                return (
678✔
3111
                    `SELECT "columnUsages".*, "tableConstraints"."CONSTRAINT_TYPE", "chk"."definition" ` +
678✔
3112
                    `FROM "${TABLE_CATALOG}"."INFORMATION_SCHEMA"."CONSTRAINT_COLUMN_USAGE" "columnUsages" ` +
678✔
3113
                    `INNER JOIN "${TABLE_CATALOG}"."INFORMATION_SCHEMA"."TABLE_CONSTRAINTS" "tableConstraints" ` +
678✔
3114
                    `ON ` +
678✔
3115
                    `"tableConstraints"."CONSTRAINT_NAME" = "columnUsages"."CONSTRAINT_NAME" AND ` +
678✔
3116
                    `"tableConstraints"."TABLE_SCHEMA" = "columnUsages"."TABLE_SCHEMA" AND ` +
678✔
3117
                    `"tableConstraints"."TABLE_NAME" = "columnUsages"."TABLE_NAME" ` +
678✔
3118
                    `LEFT JOIN "${TABLE_CATALOG}"."sys"."check_constraints" "chk" ` +
678✔
3119
                    `ON ` +
678✔
3120
                    `"chk"."object_id" = OBJECT_ID("columnUsages"."TABLE_CATALOG" + '.' + "columnUsages"."TABLE_SCHEMA" + '.' + "columnUsages"."CONSTRAINT_NAME") ` +
678✔
3121
                    `WHERE ` +
678✔
3122
                    `(${conditions}) AND ` +
678✔
3123
                    `"tableConstraints"."CONSTRAINT_TYPE" IN ('PRIMARY KEY', 'UNIQUE', 'CHECK')`
678✔
3124
                )
678✔
3125
            })
678✔
3126
            .join(" UNION ALL ")
678✔
3127

678✔
3128
        const foreignKeysSql = Object.entries(dbTablesByCatalog)
678✔
3129
            .map(([TABLE_CATALOG, tables]) => {
678✔
3130
                const conditions = tables
678✔
3131
                    .map(
678✔
3132
                        ({ TABLE_NAME, TABLE_SCHEMA }) =>
678✔
3133
                            `("s1"."name" = '${TABLE_SCHEMA}' AND "t1"."name" = '${TABLE_NAME}')`,
678✔
3134
                    )
678✔
3135
                    .join(" OR ")
678✔
3136

678✔
3137
                return (
678✔
3138
                    `SELECT "fk"."name" AS "FK_NAME", '${TABLE_CATALOG}' AS "TABLE_CATALOG", "s1"."name" AS "TABLE_SCHEMA", "t1"."name" AS "TABLE_NAME", ` +
678✔
3139
                    `"col1"."name" AS "COLUMN_NAME", "s2"."name" AS "REF_SCHEMA", "t2"."name" AS "REF_TABLE", "col2"."name" AS "REF_COLUMN", ` +
678✔
3140
                    `"fk"."delete_referential_action_desc" AS "ON_DELETE", "fk"."update_referential_action_desc" AS "ON_UPDATE" ` +
678✔
3141
                    `FROM "${TABLE_CATALOG}"."sys"."foreign_keys" "fk" ` +
678✔
3142
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."foreign_key_columns" "fkc" ON "fkc"."constraint_object_id" = "fk"."object_id" ` +
678✔
3143
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."tables" "t1" ON "t1"."object_id" = "fk"."parent_object_id" ` +
678✔
3144
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."schemas" "s1" ON "s1"."schema_id" = "t1"."schema_id" ` +
678✔
3145
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."tables" "t2" ON "t2"."object_id" = "fk"."referenced_object_id" ` +
678✔
3146
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."schemas" "s2" ON "s2"."schema_id" = "t2"."schema_id" ` +
678✔
3147
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."columns" "col1" ON "col1"."column_id" = "fkc"."parent_column_id" AND "col1"."object_id" = "fk"."parent_object_id" ` +
678✔
3148
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."columns" "col2" ON "col2"."column_id" = "fkc"."referenced_column_id" AND "col2"."object_id" = "fk"."referenced_object_id" ` +
678✔
3149
                    `WHERE (${conditions})`
678✔
3150
                )
678✔
3151
            })
678✔
3152
            .join(" UNION ALL ")
678✔
3153

678✔
3154
        const identityColumnsSql = Object.entries(dbTablesByCatalog)
678✔
3155
            .map(([TABLE_CATALOG, tables]) => {
678✔
3156
                const conditions = tables
678✔
3157
                    .map(
678✔
3158
                        ({ TABLE_NAME, TABLE_SCHEMA }) =>
678✔
3159
                            `("TABLE_SCHEMA" = '${TABLE_SCHEMA}' AND "TABLE_NAME" = '${TABLE_NAME}')`,
678✔
3160
                    )
678✔
3161
                    .join(" OR ")
678✔
3162

678✔
3163
                return (
678✔
3164
                    `SELECT "TABLE_CATALOG", "TABLE_SCHEMA", "COLUMN_NAME", "TABLE_NAME" ` +
678✔
3165
                    `FROM "${TABLE_CATALOG}"."INFORMATION_SCHEMA"."COLUMNS" ` +
678✔
3166
                    `WHERE ` +
678✔
3167
                    `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 ` +
678✔
3168
                    `(${conditions})`
678✔
3169
                )
678✔
3170
            })
678✔
3171
            .join(" UNION ALL ")
678✔
3172

678✔
3173
        const dbCollationsSql = `SELECT "NAME", "COLLATION_NAME" FROM "sys"."databases"`
678✔
3174

678✔
3175
        const indicesSql = Object.entries(dbTablesByCatalog)
678✔
3176
            .map(([TABLE_CATALOG, tables]) => {
678✔
3177
                const conditions = tables
678✔
3178
                    .map(
678✔
3179
                        ({ TABLE_NAME, TABLE_SCHEMA }) =>
678✔
3180
                            `("s"."name" = '${TABLE_SCHEMA}' AND "t"."name" = '${TABLE_NAME}')`,
678✔
3181
                    )
678✔
3182
                    .join(" OR ")
678✔
3183

678✔
3184
                return (
678✔
3185
                    `SELECT '${TABLE_CATALOG}' AS "TABLE_CATALOG", "s"."name" AS "TABLE_SCHEMA", "t"."name" AS "TABLE_NAME", ` +
678✔
3186
                    `"ind"."name" AS "INDEX_NAME", "col"."name" AS "COLUMN_NAME", "ind"."is_unique" AS "IS_UNIQUE", "ind"."filter_definition" as "CONDITION" ` +
678✔
3187
                    `FROM "${TABLE_CATALOG}"."sys"."indexes" "ind" ` +
678✔
3188
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."index_columns" "ic" ON "ic"."object_id" = "ind"."object_id" AND "ic"."index_id" = "ind"."index_id" ` +
678✔
3189
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."columns" "col" ON "col"."object_id" = "ic"."object_id" AND "col"."column_id" = "ic"."column_id" ` +
678✔
3190
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."tables" "t" ON "t"."object_id" = "ind"."object_id" ` +
678✔
3191
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."schemas" "s" ON "s"."schema_id" = "t"."schema_id" ` +
678✔
3192
                    `WHERE ` +
678✔
3193
                    `"ind"."is_primary_key" = 0 AND "ind"."is_unique_constraint" = 0 AND "t"."is_ms_shipped" = 0 AND ` +
678✔
3194
                    `(${conditions})`
678✔
3195
                )
678✔
3196
            })
678✔
3197
            .join(" UNION ALL ")
678✔
3198

678✔
3199
        const [
678✔
3200
            dbColumns,
678✔
3201
            dbConstraints,
678✔
3202
            dbForeignKeys,
678✔
3203
            dbIdentityColumns,
678✔
3204
            dbCollations,
678✔
3205
            dbIndices,
678✔
3206
        ]: ObjectLiteral[][] = await Promise.all([
678✔
3207
            this.query(columnsSql),
678✔
3208
            this.query(constraintsSql),
678✔
3209
            this.query(foreignKeysSql),
678✔
3210
            this.query(identityColumnsSql),
678✔
3211
            this.query(dbCollationsSql),
678✔
3212
            this.query(indicesSql),
678✔
3213
        ])
678✔
3214

678✔
3215
        // create table schemas for loaded tables
678✔
3216
        return await Promise.all(
678✔
3217
            dbTables.map(async (dbTable) => {
678✔
3218
                const table = new Table()
1,002✔
3219

1,002✔
3220
                const getSchemaFromKey = (dbObject: any, key: string) => {
1,002✔
3221
                    return dbObject[key] === currentSchema &&
1,340✔
3222
                        (!this.driver.options.schema ||
1,250✔
3223
                            this.driver.options.schema === currentSchema)
1,250✔
3224
                        ? undefined
1,340✔
3225
                        : dbObject[key]
1,340✔
3226
                }
1,340✔
3227

1,002✔
3228
                // We do not need to join schema and database names, when db or schema is by default.
1,002✔
3229
                const db =
1,002✔
3230
                    dbTable["TABLE_CATALOG"] === currentDatabase
1,002✔
3231
                        ? undefined
1,002✔
3232
                        : dbTable["TABLE_CATALOG"]
1,002✔
3233
                const schema = getSchemaFromKey(dbTable, "TABLE_SCHEMA")
1,002✔
3234
                table.database = dbTable["TABLE_CATALOG"]
1,002✔
3235
                table.schema = dbTable["TABLE_SCHEMA"]
1,002✔
3236
                table.name = this.driver.buildTableName(
1,002✔
3237
                    dbTable["TABLE_NAME"],
1,002✔
3238
                    schema,
1,002✔
3239
                    db,
1,002✔
3240
                )
1,002✔
3241

1,002✔
3242
                const defaultCollation = dbCollations.find(
1,002✔
3243
                    (dbCollation) =>
1,002✔
3244
                        dbCollation["NAME"] === dbTable["TABLE_CATALOG"],
1,002✔
3245
                )!
1,002✔
3246

1,002✔
3247
                // create columns from the loaded columns
1,002✔
3248
                table.columns = await Promise.all(
1,002✔
3249
                    dbColumns
1,002✔
3250
                        .filter(
1,002✔
3251
                            (dbColumn) =>
1,002✔
3252
                                dbColumn["TABLE_NAME"] ===
10,760✔
3253
                                    dbTable["TABLE_NAME"] &&
10,760✔
3254
                                dbColumn["TABLE_SCHEMA"] ===
3,606✔
3255
                                    dbTable["TABLE_SCHEMA"] &&
10,760✔
3256
                                dbColumn["TABLE_CATALOG"] ===
3,606✔
3257
                                    dbTable["TABLE_CATALOG"],
1,002✔
3258
                        )
1,002✔
3259
                        .map(async (dbColumn) => {
1,002✔
3260
                            const columnConstraints = dbConstraints.filter(
3,606✔
3261
                                (dbConstraint) =>
3,606✔
3262
                                    dbConstraint["TABLE_NAME"] ===
18,980✔
3263
                                        dbColumn["TABLE_NAME"] &&
18,980✔
3264
                                    dbConstraint["TABLE_SCHEMA"] ===
8,126✔
3265
                                        dbColumn["TABLE_SCHEMA"] &&
18,980✔
3266
                                    dbConstraint["TABLE_CATALOG"] ===
8,126✔
3267
                                        dbColumn["TABLE_CATALOG"] &&
18,980✔
3268
                                    dbConstraint["COLUMN_NAME"] ===
8,126✔
3269
                                        dbColumn["COLUMN_NAME"],
3,606✔
3270
                            )
3,606✔
3271

3,606✔
3272
                            const uniqueConstraints = columnConstraints.filter(
3,606✔
3273
                                (constraint) =>
3,606✔
3274
                                    constraint["CONSTRAINT_TYPE"] === "UNIQUE",
3,606✔
3275
                            )
3,606✔
3276
                            const isConstraintComposite =
3,606✔
3277
                                uniqueConstraints.every((uniqueConstraint) => {
3,606✔
3278
                                    return dbConstraints.some(
626✔
3279
                                        (dbConstraint) =>
626✔
3280
                                            dbConstraint["CONSTRAINT_TYPE"] ===
2,722✔
3281
                                                "UNIQUE" &&
2,722✔
3282
                                            dbConstraint["CONSTRAINT_NAME"] ===
1,286✔
3283
                                                uniqueConstraint[
1,286✔
3284
                                                    "CONSTRAINT_NAME"
1,286✔
3285
                                                ] &&
2,722✔
3286
                                            dbConstraint["TABLE_SCHEMA"] ===
810✔
3287
                                                dbColumn["TABLE_SCHEMA"] &&
2,722✔
3288
                                            dbConstraint["TABLE_CATALOG"] ===
810✔
3289
                                                dbColumn["TABLE_CATALOG"] &&
2,722✔
3290
                                            dbConstraint["COLUMN_NAME"] !==
810✔
3291
                                                dbColumn["COLUMN_NAME"],
626✔
3292
                                    )
626✔
3293
                                })
3,606✔
3294

3,606✔
3295
                            const isGenerated = !!dbIdentityColumns.find(
3,606✔
3296
                                (column) =>
3,606✔
3297
                                    column["TABLE_NAME"] ===
5,696✔
3298
                                        dbColumn["TABLE_NAME"] &&
5,696✔
3299
                                    column["TABLE_SCHEMA"] ===
1,770✔
3300
                                        dbColumn["TABLE_SCHEMA"] &&
5,696✔
3301
                                    column["TABLE_CATALOG"] ===
1,770✔
3302
                                        dbColumn["TABLE_CATALOG"] &&
5,696✔
3303
                                    column["COLUMN_NAME"] ===
1,770✔
3304
                                        dbColumn["COLUMN_NAME"],
3,606✔
3305
                            )
3,606✔
3306

3,606✔
3307
                            const tableColumn = new TableColumn()
3,606✔
3308
                            tableColumn.name = dbColumn["COLUMN_NAME"]
3,606✔
3309
                            tableColumn.type =
3,606✔
3310
                                dbColumn["DATA_TYPE"].toLowerCase()
3,606✔
3311

3,606✔
3312
                            // check only columns that have length property
3,606✔
3313
                            if (
3,606✔
3314
                                this.driver.withLengthColumnTypes.indexOf(
3,606✔
3315
                                    tableColumn.type as ColumnType,
3,606✔
3316
                                ) !== -1 &&
3,606✔
3317
                                dbColumn["CHARACTER_MAXIMUM_LENGTH"]
1,964✔
3318
                            ) {
3,606✔
3319
                                const length =
1,964✔
3320
                                    dbColumn[
1,964✔
3321
                                        "CHARACTER_MAXIMUM_LENGTH"
1,964✔
3322
                                    ].toString()
1,964✔
3323
                                if (length === "-1") {
1,964✔
3324
                                    tableColumn.length = "MAX"
8✔
3325
                                } else {
1,964✔
3326
                                    if (tableColumn.type === "vector") {
1,956✔
3327
                                        const len = +length
2✔
3328
                                        // NOTE: real returned length is (N*4 + 8) where N is desired dimensions
2✔
3329
                                        if (!Number.isNaN(len)) {
2✔
3330
                                            tableColumn.length = String(
2✔
3331
                                                (len - 8) / 4,
2✔
3332
                                            )
2✔
3333
                                        }
2✔
3334
                                    } else {
1,956✔
3335
                                        tableColumn.length =
1,954✔
3336
                                            !this.isDefaultColumnLength(
1,954✔
3337
                                                table,
1,954✔
3338
                                                tableColumn,
1,954✔
3339
                                                length,
1,954✔
3340
                                            )
1,954✔
3341
                                                ? length
1,954✔
3342
                                                : ""
1,954✔
3343
                                    }
1,954✔
3344
                                }
1,956✔
3345
                            }
1,964✔
3346

3,606✔
3347
                            if (
3,606✔
3348
                                tableColumn.type === "decimal" ||
3,606✔
3349
                                tableColumn.type === "numeric"
3,596✔
3350
                            ) {
3,606✔
3351
                                if (
14✔
3352
                                    dbColumn["NUMERIC_PRECISION"] !== null &&
14✔
3353
                                    !this.isDefaultColumnPrecision(
14✔
3354
                                        table,
14✔
3355
                                        tableColumn,
14✔
3356
                                        dbColumn["NUMERIC_PRECISION"],
14✔
3357
                                    )
14✔
3358
                                )
14✔
3359
                                    tableColumn.precision =
14✔
3360
                                        dbColumn["NUMERIC_PRECISION"]
8✔
3361
                                if (
14✔
3362
                                    dbColumn["NUMERIC_SCALE"] !== null &&
14✔
3363
                                    !this.isDefaultColumnScale(
14✔
3364
                                        table,
14✔
3365
                                        tableColumn,
14✔
3366
                                        dbColumn["NUMERIC_SCALE"],
14✔
3367
                                    )
14✔
3368
                                )
14✔
3369
                                    tableColumn.scale =
14✔
3370
                                        dbColumn["NUMERIC_SCALE"]
8✔
3371
                            }
14✔
3372

3,606✔
3373
                            if (tableColumn.type === "nvarchar") {
3,606✔
3374
                                // Check if this is an enum
1,630✔
3375
                                const columnCheckConstraints =
1,630✔
3376
                                    columnConstraints.filter(
1,630✔
3377
                                        (constraint) =>
1,630✔
3378
                                            constraint["CONSTRAINT_TYPE"] ===
542✔
3379
                                            "CHECK",
1,630✔
3380
                                    )
1,630✔
3381
                                if (columnCheckConstraints.length) {
1,630✔
3382
                                    // const isEnumRegexp = new RegExp("^\\(\\[" + tableColumn.name + "\\]='[^']+'(?: OR \\[" + tableColumn.name + "\\]='[^']+')*\\)$");
22✔
3383
                                    for (const checkConstraint of columnCheckConstraints) {
22✔
3384
                                        if (
22✔
3385
                                            this.isEnumCheckConstraint(
22✔
3386
                                                checkConstraint[
22✔
3387
                                                    "CONSTRAINT_NAME"
22✔
3388
                                                ],
22✔
3389
                                            )
22✔
3390
                                        ) {
22✔
3391
                                            // This is an enum constraint, make column into an enum
10✔
3392
                                            tableColumn.enum = []
10✔
3393
                                            const enumValueRegexp = new RegExp(
10✔
3394
                                                "\\[" +
10✔
3395
                                                    tableColumn.name +
10✔
3396
                                                    "\\]='([^']+)'",
10✔
3397
                                                "g",
10✔
3398
                                            )
10✔
3399
                                            let result
10✔
3400
                                            while (
10✔
3401
                                                (result = enumValueRegexp.exec(
10✔
3402
                                                    checkConstraint[
10✔
3403
                                                        "definition"
10✔
3404
                                                    ],
10✔
3405
                                                )) !== null
10✔
3406
                                            ) {
10✔
3407
                                                tableColumn.enum.unshift(
26✔
3408
                                                    result[1],
26✔
3409
                                                )
26✔
3410
                                            }
26✔
3411
                                            // Skip other column constraints
10✔
3412
                                            break
10✔
3413
                                        }
10✔
3414
                                    }
22✔
3415
                                }
22✔
3416
                            }
1,630✔
3417

3,606✔
3418
                            const primaryConstraint = columnConstraints.find(
3,606✔
3419
                                (constraint) =>
3,606✔
3420
                                    constraint["CONSTRAINT_TYPE"] ===
1,854✔
3421
                                    "PRIMARY KEY",
3,606✔
3422
                            )
3,606✔
3423
                            if (primaryConstraint) {
3,606✔
3424
                                tableColumn.isPrimary = true
1,038✔
3425
                                // find another columns involved in primary key constraint
1,038✔
3426
                                const anotherPrimaryConstraints =
1,038✔
3427
                                    dbConstraints.filter(
1,038✔
3428
                                        (constraint) =>
1,038✔
3429
                                            constraint["TABLE_NAME"] ===
5,482✔
3430
                                                dbColumn["TABLE_NAME"] &&
5,482✔
3431
                                            constraint["TABLE_SCHEMA"] ===
1,980✔
3432
                                                dbColumn["TABLE_SCHEMA"] &&
5,482✔
3433
                                            constraint["TABLE_CATALOG"] ===
1,980✔
3434
                                                dbColumn["TABLE_CATALOG"] &&
5,482✔
3435
                                            constraint["COLUMN_NAME"] !==
1,980✔
3436
                                                dbColumn["COLUMN_NAME"] &&
5,482✔
3437
                                            constraint["CONSTRAINT_TYPE"] ===
922✔
3438
                                                "PRIMARY KEY",
1,038✔
3439
                                    )
1,038✔
3440

1,038✔
3441
                                // collect all column names
1,038✔
3442
                                const columnNames =
1,038✔
3443
                                    anotherPrimaryConstraints.map(
1,038✔
3444
                                        (constraint) =>
1,038✔
3445
                                            constraint["COLUMN_NAME"],
1,038✔
3446
                                    )
1,038✔
3447
                                columnNames.push(dbColumn["COLUMN_NAME"])
1,038✔
3448

1,038✔
3449
                                // build default primary key constraint name
1,038✔
3450
                                const pkName =
1,038✔
3451
                                    this.connection.namingStrategy.primaryKeyName(
1,038✔
3452
                                        table,
1,038✔
3453
                                        columnNames,
1,038✔
3454
                                    )
1,038✔
3455

1,038✔
3456
                                // if primary key has user-defined constraint name, write it in table column
1,038✔
3457
                                if (
1,038✔
3458
                                    primaryConstraint["CONSTRAINT_NAME"] !==
1,038✔
3459
                                    pkName
1,038✔
3460
                                ) {
1,038✔
3461
                                    tableColumn.primaryKeyConstraintName =
52✔
3462
                                        primaryConstraint["CONSTRAINT_NAME"]
52✔
3463
                                }
52✔
3464
                            }
1,038✔
3465

3,606✔
3466
                            tableColumn.default =
3,606✔
3467
                                dbColumn["COLUMN_DEFAULT"] !== null &&
3,606✔
3468
                                dbColumn["COLUMN_DEFAULT"] !== undefined
224✔
3469
                                    ? this.removeParenthesisFromDefault(
3,606✔
3470
                                          dbColumn["COLUMN_DEFAULT"],
224✔
3471
                                      )
3,606✔
3472
                                    : undefined
3,606✔
3473
                            tableColumn.isNullable =
3,606✔
3474
                                dbColumn["IS_NULLABLE"] === "YES"
3,606✔
3475
                            tableColumn.isUnique =
3,606✔
3476
                                uniqueConstraints.length > 0 &&
3,606✔
3477
                                !isConstraintComposite
626✔
3478
                            tableColumn.isGenerated = isGenerated
3,606✔
3479
                            if (isGenerated)
3,606✔
3480
                                tableColumn.generationStrategy = "increment"
3,606✔
3481
                            if (tableColumn.default === "newsequentialid()") {
3,606✔
3482
                                tableColumn.isGenerated = true
14✔
3483
                                tableColumn.generationStrategy = "uuid"
14✔
3484
                                tableColumn.default = undefined
14✔
3485
                            }
14✔
3486

3,606✔
3487
                            // todo: unable to get default charset
3,606✔
3488
                            // tableColumn.charset = dbColumn["CHARACTER_SET_NAME"];
3,606✔
3489
                            if (dbColumn["COLLATION_NAME"])
3,606✔
3490
                                tableColumn.collation =
3,606✔
3491
                                    dbColumn["COLLATION_NAME"] ===
1,916✔
3492
                                    defaultCollation["COLLATION_NAME"]
1,916✔
3493
                                        ? undefined
1,916✔
3494
                                        : dbColumn["COLLATION_NAME"]
1,916✔
3495

3,606✔
3496
                            if (
3,606✔
3497
                                tableColumn.type === "datetime2" ||
3,606✔
3498
                                tableColumn.type === "time" ||
3,606✔
3499
                                tableColumn.type === "datetimeoffset"
3,578✔
3500
                            ) {
3,606✔
3501
                                tableColumn.precision =
32✔
3502
                                    !this.isDefaultColumnPrecision(
32✔
3503
                                        table,
32✔
3504
                                        tableColumn,
32✔
3505
                                        dbColumn["DATETIME_PRECISION"],
32✔
3506
                                    )
32✔
3507
                                        ? dbColumn["DATETIME_PRECISION"]
32✔
3508
                                        : undefined
32✔
3509
                            }
32✔
3510

3,606✔
3511
                            if (
3,606✔
3512
                                dbColumn["is_persisted"] !== null &&
3,606✔
3513
                                dbColumn["is_persisted"] !== undefined &&
3,606✔
3514
                                dbColumn["definition"]
94✔
3515
                            ) {
3,606✔
3516
                                tableColumn.generatedType =
94✔
3517
                                    dbColumn["is_persisted"] === true
94✔
3518
                                        ? "STORED"
94✔
3519
                                        : "VIRTUAL"
94✔
3520
                                // We cannot relay on information_schema.columns.generation_expression, because it is formatted different.
94✔
3521
                                const asExpressionQuery =
94✔
3522
                                    this.selectTypeormMetadataSql({
94✔
3523
                                        database: dbTable["TABLE_CATALOG"],
94✔
3524
                                        schema: dbTable["TABLE_SCHEMA"],
94✔
3525
                                        table: dbTable["TABLE_NAME"],
94✔
3526
                                        type: MetadataTableType.GENERATED_COLUMN,
94✔
3527
                                        name: tableColumn.name,
94✔
3528
                                    })
94✔
3529

94✔
3530
                                const results = await this.query(
94✔
3531
                                    asExpressionQuery.query,
94✔
3532
                                    asExpressionQuery.parameters,
94✔
3533
                                )
94✔
3534
                                if (results[0] && results[0].value) {
94✔
3535
                                    tableColumn.asExpression = results[0].value
94✔
3536
                                } else {
94!
3537
                                    tableColumn.asExpression = ""
×
3538
                                }
×
3539
                            }
94✔
3540

3,606✔
3541
                            return tableColumn
3,606✔
3542
                        }),
1,002✔
3543
                )
1,002✔
3544

1,002✔
3545
                // find unique constraints of table, group them by constraint name and build TableUnique.
1,002✔
3546
                const tableUniqueConstraints = OrmUtils.uniq(
1,002✔
3547
                    dbConstraints.filter(
1,002✔
3548
                        (dbConstraint) =>
1,002✔
3549
                            dbConstraint["TABLE_NAME"] ===
5,372✔
3550
                                dbTable["TABLE_NAME"] &&
5,372✔
3551
                            dbConstraint["TABLE_SCHEMA"] ===
1,874✔
3552
                                dbTable["TABLE_SCHEMA"] &&
5,372✔
3553
                            dbConstraint["TABLE_CATALOG"] ===
1,874✔
3554
                                dbTable["TABLE_CATALOG"] &&
5,372✔
3555
                            dbConstraint["CONSTRAINT_TYPE"] === "UNIQUE",
1,002✔
3556
                    ),
1,002✔
3557
                    (dbConstraint) => dbConstraint["CONSTRAINT_NAME"],
1,002✔
3558
                )
1,002✔
3559

1,002✔
3560
                table.uniques = tableUniqueConstraints.map((constraint) => {
1,002✔
3561
                    const uniques = dbConstraints.filter(
438✔
3562
                        (dbC) =>
438✔
3563
                            dbC["CONSTRAINT_NAME"] ===
2,714✔
3564
                            constraint["CONSTRAINT_NAME"],
438✔
3565
                    )
438✔
3566
                    return new TableUnique({
438✔
3567
                        name: constraint["CONSTRAINT_NAME"],
438✔
3568
                        columnNames: uniques.map((u) => u["COLUMN_NAME"]),
438✔
3569
                    })
438✔
3570
                })
1,002✔
3571

1,002✔
3572
                // find check constraints of table, group them by constraint name and build TableCheck.
1,002✔
3573
                const tableCheckConstraints = OrmUtils.uniq(
1,002✔
3574
                    dbConstraints.filter(
1,002✔
3575
                        (dbConstraint) =>
1,002✔
3576
                            dbConstraint["TABLE_NAME"] ===
5,372✔
3577
                                dbTable["TABLE_NAME"] &&
5,372✔
3578
                            dbConstraint["TABLE_SCHEMA"] ===
1,874✔
3579
                                dbTable["TABLE_SCHEMA"] &&
5,372✔
3580
                            dbConstraint["TABLE_CATALOG"] ===
1,874✔
3581
                                dbTable["TABLE_CATALOG"] &&
5,372✔
3582
                            dbConstraint["CONSTRAINT_TYPE"] === "CHECK",
1,002✔
3583
                    ),
1,002✔
3584
                    (dbConstraint) => dbConstraint["CONSTRAINT_NAME"],
1,002✔
3585
                )
1,002✔
3586

1,002✔
3587
                table.checks = tableCheckConstraints
1,002✔
3588
                    .filter(
1,002✔
3589
                        (constraint) =>
1,002✔
3590
                            !this.isEnumCheckConstraint(
196✔
3591
                                constraint["CONSTRAINT_NAME"],
196✔
3592
                            ),
1,002✔
3593
                    )
1,002✔
3594
                    .map((constraint) => {
1,002✔
3595
                        const checks = dbConstraints.filter(
186✔
3596
                            (dbC) =>
186✔
3597
                                dbC["CONSTRAINT_NAME"] ===
1,278✔
3598
                                constraint["CONSTRAINT_NAME"],
186✔
3599
                        )
186✔
3600
                        return new TableCheck({
186✔
3601
                            name: constraint["CONSTRAINT_NAME"],
186✔
3602
                            columnNames: checks.map((c) => c["COLUMN_NAME"]),
186✔
3603
                            expression: constraint["definition"],
186✔
3604
                        })
186✔
3605
                    })
1,002✔
3606

1,002✔
3607
                // find foreign key constraints of table, group them by constraint name and build TableForeignKey.
1,002✔
3608
                const tableForeignKeyConstraints = OrmUtils.uniq(
1,002✔
3609
                    dbForeignKeys.filter(
1,002✔
3610
                        (dbForeignKey) =>
1,002✔
3611
                            dbForeignKey["TABLE_NAME"] ===
1,280✔
3612
                                dbTable["TABLE_NAME"] &&
1,280✔
3613
                            dbForeignKey["TABLE_SCHEMA"] ===
346✔
3614
                                dbTable["TABLE_SCHEMA"] &&
1,280✔
3615
                            dbForeignKey["TABLE_CATALOG"] ===
346✔
3616
                                dbTable["TABLE_CATALOG"],
1,002✔
3617
                    ),
1,002✔
3618
                    (dbForeignKey) => dbForeignKey["FK_NAME"],
1,002✔
3619
                )
1,002✔
3620

1,002✔
3621
                table.foreignKeys = tableForeignKeyConstraints.map(
1,002✔
3622
                    (dbForeignKey) => {
1,002✔
3623
                        const foreignKeys = dbForeignKeys.filter(
338✔
3624
                            (dbFk) =>
338✔
3625
                                dbFk["FK_NAME"] === dbForeignKey["FK_NAME"],
338✔
3626
                        )
338✔
3627

338✔
3628
                        // if referenced table located in currently used db and schema, we don't need to concat db and schema names to table name.
338✔
3629
                        const db =
338✔
3630
                            dbForeignKey["TABLE_CATALOG"] === currentDatabase
338✔
3631
                                ? undefined
338✔
3632
                                : dbForeignKey["TABLE_CATALOG"]
338✔
3633
                        const schema = getSchemaFromKey(
338✔
3634
                            dbForeignKey,
338✔
3635
                            "REF_SCHEMA",
338✔
3636
                        )
338✔
3637
                        const referencedTableName = this.driver.buildTableName(
338✔
3638
                            dbForeignKey["REF_TABLE"],
338✔
3639
                            schema,
338✔
3640
                            db,
338✔
3641
                        )
338✔
3642

338✔
3643
                        return new TableForeignKey({
338✔
3644
                            name: dbForeignKey["FK_NAME"],
338✔
3645
                            columnNames: foreignKeys.map(
338✔
3646
                                (dbFk) => dbFk["COLUMN_NAME"],
338✔
3647
                            ),
338✔
3648
                            referencedDatabase: dbForeignKey["TABLE_CATALOG"],
338✔
3649
                            referencedSchema: dbForeignKey["REF_SCHEMA"],
338✔
3650
                            referencedTableName: referencedTableName,
338✔
3651
                            referencedColumnNames: foreignKeys.map(
338✔
3652
                                (dbFk) => dbFk["REF_COLUMN"],
338✔
3653
                            ),
338✔
3654
                            onDelete: dbForeignKey["ON_DELETE"].replace(
338✔
3655
                                "_",
338✔
3656
                                " ",
338✔
3657
                            ), // SqlServer returns NO_ACTION, instead of NO ACTION
338✔
3658
                            onUpdate: dbForeignKey["ON_UPDATE"].replace(
338✔
3659
                                "_",
338✔
3660
                                " ",
338✔
3661
                            ), // SqlServer returns NO_ACTION, instead of NO ACTION
338✔
3662
                        })
338✔
3663
                    },
1,002✔
3664
                )
1,002✔
3665

1,002✔
3666
                // find index constraints of table, group them by constraint name and build TableIndex.
1,002✔
3667
                const tableIndexConstraints = OrmUtils.uniq(
1,002✔
3668
                    dbIndices.filter(
1,002✔
3669
                        (dbIndex) =>
1,002✔
3670
                            dbIndex["TABLE_NAME"] === dbTable["TABLE_NAME"] &&
722✔
3671
                            dbIndex["TABLE_SCHEMA"] ===
332✔
3672
                                dbTable["TABLE_SCHEMA"] &&
722✔
3673
                            dbIndex["TABLE_CATALOG"] ===
332✔
3674
                                dbTable["TABLE_CATALOG"],
1,002✔
3675
                    ),
1,002✔
3676
                    (dbIndex) => dbIndex["INDEX_NAME"],
1,002✔
3677
                )
1,002✔
3678

1,002✔
3679
                table.indices = tableIndexConstraints.map((constraint) => {
1,002✔
3680
                    const indices = dbIndices.filter((index) => {
288✔
3681
                        return (
964✔
3682
                            index["TABLE_CATALOG"] ===
964✔
3683
                                constraint["TABLE_CATALOG"] &&
964✔
3684
                            index["TABLE_SCHEMA"] ===
964✔
3685
                                constraint["TABLE_SCHEMA"] &&
964✔
3686
                            index["TABLE_NAME"] === constraint["TABLE_NAME"] &&
964✔
3687
                            index["INDEX_NAME"] === constraint["INDEX_NAME"]
694✔
3688
                        )
964✔
3689
                    })
288✔
3690
                    return new TableIndex(<TableIndexOptions>{
288✔
3691
                        table: table,
288✔
3692
                        name: constraint["INDEX_NAME"],
288✔
3693
                        columnNames: indices.map((i) => i["COLUMN_NAME"]),
288✔
3694
                        isUnique: constraint["IS_UNIQUE"],
288✔
3695
                        where: constraint["CONDITION"],
288✔
3696
                    })
288✔
3697
                })
1,002✔
3698

1,002✔
3699
                return table
1,002✔
3700
            }),
678✔
3701
        )
678✔
3702
    }
678✔
3703

23✔
3704
    /**
23✔
3705
     * Builds and returns SQL for create table.
23✔
3706
     * @param table
23✔
3707
     * @param createForeignKeys
23✔
3708
     */
23✔
3709
    protected createTableSql(table: Table, createForeignKeys?: boolean): Query {
23✔
3710
        const columnDefinitions = table.columns
10,554✔
3711
            .map((column) =>
10,554✔
3712
                this.buildCreateColumnSql(table, column, false, true),
10,554✔
3713
            )
10,554✔
3714
            .join(", ")
10,554✔
3715
        let sql = `CREATE TABLE ${this.escapePath(table)} (${columnDefinitions}`
10,554✔
3716

10,554✔
3717
        table.columns
10,554✔
3718
            .filter((column) => column.isUnique)
10,554✔
3719
            .forEach((column) => {
10,554✔
3720
                const isUniqueExist = table.uniques.some(
714✔
3721
                    (unique) =>
714✔
3722
                        unique.columnNames.length === 1 &&
864✔
3723
                        unique.columnNames[0] === column.name,
714✔
3724
                )
714✔
3725
                if (!isUniqueExist)
714✔
3726
                    table.uniques.push(
714✔
3727
                        new TableUnique({
8✔
3728
                            name: this.connection.namingStrategy.uniqueConstraintName(
8✔
3729
                                table,
8✔
3730
                                [column.name],
8✔
3731
                            ),
8✔
3732
                            columnNames: [column.name],
8✔
3733
                        }),
8✔
3734
                    )
8✔
3735
            })
10,554✔
3736

10,554✔
3737
        if (table.uniques.length > 0) {
10,554✔
3738
            const uniquesSql = table.uniques
652✔
3739
                .map((unique) => {
652✔
3740
                    const uniqueName = unique.name
956✔
3741
                        ? unique.name
956✔
3742
                        : this.connection.namingStrategy.uniqueConstraintName(
956✔
3743
                              table,
4✔
3744
                              unique.columnNames,
4✔
3745
                          )
956✔
3746
                    const columnNames = unique.columnNames
956✔
3747
                        .map((columnName) => `"${columnName}"`)
956✔
3748
                        .join(", ")
956✔
3749
                    return `CONSTRAINT "${uniqueName}" UNIQUE (${columnNames})`
956✔
3750
                })
652✔
3751
                .join(", ")
652✔
3752

652✔
3753
            sql += `, ${uniquesSql}`
652✔
3754
        }
652✔
3755

10,554✔
3756
        if (table.checks.length > 0) {
10,554✔
3757
            const checksSql = table.checks
162✔
3758
                .map((check) => {
162✔
3759
                    const checkName = check.name
164✔
3760
                        ? check.name
164✔
3761
                        : this.connection.namingStrategy.checkConstraintName(
164✔
3762
                              table,
2✔
3763
                              check.expression!,
2✔
3764
                          )
164✔
3765
                    return `CONSTRAINT "${checkName}" CHECK (${check.expression})`
164✔
3766
                })
162✔
3767
                .join(", ")
162✔
3768

162✔
3769
            sql += `, ${checksSql}`
162✔
3770
        }
162✔
3771

10,554✔
3772
        if (table.foreignKeys.length > 0 && createForeignKeys) {
10,554✔
3773
            const foreignKeysSql = table.foreignKeys
8✔
3774
                .map((fk) => {
8✔
3775
                    const columnNames = fk.columnNames
10✔
3776
                        .map((columnName) => `"${columnName}"`)
10✔
3777
                        .join(", ")
10✔
3778
                    if (!fk.name)
10✔
3779
                        fk.name = this.connection.namingStrategy.foreignKeyName(
10✔
3780
                            table,
6✔
3781
                            fk.columnNames,
6✔
3782
                            this.getTablePath(fk),
6✔
3783
                            fk.referencedColumnNames,
6✔
3784
                        )
6✔
3785
                    const referencedColumnNames = fk.referencedColumnNames
10✔
3786
                        .map((columnName) => `"${columnName}"`)
10✔
3787
                        .join(", ")
10✔
3788

10✔
3789
                    let constraint = `CONSTRAINT "${
10✔
3790
                        fk.name
10✔
3791
                    }" FOREIGN KEY (${columnNames}) REFERENCES ${this.escapePath(
10✔
3792
                        this.getTablePath(fk),
10✔
3793
                    )} (${referencedColumnNames})`
10✔
3794
                    if (fk.onDelete) constraint += ` ON DELETE ${fk.onDelete}`
10✔
3795
                    if (fk.onUpdate) constraint += ` ON UPDATE ${fk.onUpdate}`
10✔
3796

10✔
3797
                    return constraint
10✔
3798
                })
8✔
3799
                .join(", ")
8✔
3800

8✔
3801
            sql += `, ${foreignKeysSql}`
8✔
3802
        }
8✔
3803

10,554✔
3804
        const primaryColumns = table.columns.filter(
10,554✔
3805
            (column) => column.isPrimary,
10,554✔
3806
        )
10,554✔
3807
        if (primaryColumns.length > 0) {
10,554✔
3808
            const primaryKeyName = primaryColumns[0].primaryKeyConstraintName
10,528✔
3809
                ? primaryColumns[0].primaryKeyConstraintName
10,528✔
3810
                : this.connection.namingStrategy.primaryKeyName(
10,528✔
3811
                      table,
10,496✔
3812
                      primaryColumns.map((column) => column.name),
10,496✔
3813
                  )
10,528✔
3814

10,528✔
3815
            const columnNames = primaryColumns
10,528✔
3816
                .map((column) => `"${column.name}"`)
10,528✔
3817
                .join(", ")
10,528✔
3818
            sql += `, CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNames})`
10,528✔
3819
        }
10,528✔
3820

10,554✔
3821
        sql += `)`
10,554✔
3822

10,554✔
3823
        return new Query(sql)
10,554✔
3824
    }
10,554✔
3825

23✔
3826
    /**
23✔
3827
     * Builds drop table sql.
23✔
3828
     * @param tableOrName
23✔
3829
     * @param ifExists
23✔
3830
     */
23✔
3831
    protected dropTableSql(
23✔
3832
        tableOrName: Table | string,
10,554✔
3833
        ifExists?: boolean,
10,554✔
3834
    ): Query {
10,554✔
3835
        const query = ifExists
10,554✔
3836
            ? `DROP TABLE IF EXISTS ${this.escapePath(tableOrName)}`
10,554!
3837
            : `DROP TABLE ${this.escapePath(tableOrName)}`
10,554✔
3838
        return new Query(query)
10,554✔
3839
    }
10,554✔
3840

23✔
3841
    protected createViewSql(view: View): Query {
23✔
3842
        const parsedName = this.driver.parseTableName(view)
16✔
3843

16✔
3844
        // Can't use `escapePath` here because `CREATE VIEW` does not accept database names.
16✔
3845
        const viewIdentifier = parsedName.schema
16✔
3846
            ? `"${parsedName.schema}"."${parsedName.tableName}"`
16✔
3847
            : `"${parsedName.tableName}"`
16!
3848

16✔
3849
        if (typeof view.expression === "string") {
16✔
3850
            return new Query(
4✔
3851
                `CREATE VIEW ${viewIdentifier} AS ${view.expression}`,
4✔
3852
            )
4✔
3853
        } else {
16✔
3854
            return new Query(
12✔
3855
                `CREATE VIEW ${viewIdentifier} AS ${view
12✔
3856
                    .expression(this.connection)
12✔
3857
                    .getQuery()}`,
12✔
3858
            )
12✔
3859
        }
12✔
3860
    }
16✔
3861

23✔
3862
    protected async insertViewDefinitionSql(view: View): Promise<Query> {
23✔
3863
        const parsedTableName = this.driver.parseTableName(view)
16✔
3864

16✔
3865
        if (!parsedTableName.schema) {
16!
3866
            parsedTableName.schema = await this.getCurrentSchema()
×
3867
        }
×
3868

16✔
3869
        const expression =
16✔
3870
            typeof view.expression === "string"
16✔
3871
                ? view.expression.trim()
16✔
3872
                : view.expression(this.connection).getQuery()
16✔
3873
        return this.insertTypeormMetadataSql({
16✔
3874
            type: MetadataTableType.VIEW,
16✔
3875
            database: parsedTableName.database,
16✔
3876
            schema: parsedTableName.schema,
16✔
3877
            name: parsedTableName.tableName,
16✔
3878
            value: expression,
16✔
3879
        })
16✔
3880
    }
16✔
3881

23✔
3882
    /**
23✔
3883
     * Builds drop view sql.
23✔
3884
     * @param viewOrPath
23✔
3885
     * @param ifExists
23✔
3886
     */
23✔
3887
    protected dropViewSql(
23✔
3888
        viewOrPath: View | string,
16✔
3889
        ifExists?: boolean,
16✔
3890
    ): Query {
16✔
3891
        const query = ifExists
16✔
3892
            ? `DROP VIEW IF EXISTS ${this.escapePath(viewOrPath)}`
16!
3893
            : `DROP VIEW ${this.escapePath(viewOrPath)}`
16✔
3894
        return new Query(query)
16✔
3895
    }
16✔
3896

23✔
3897
    /**
23✔
3898
     * Builds remove view sql.
23✔
3899
     * @param viewOrPath
23✔
3900
     */
23✔
3901
    protected async deleteViewDefinitionSql(
23✔
3902
        viewOrPath: View | string,
16✔
3903
    ): Promise<Query> {
16✔
3904
        const parsedTableName = this.driver.parseTableName(viewOrPath)
16✔
3905

16✔
3906
        if (!parsedTableName.schema) {
16!
3907
            parsedTableName.schema = await this.getCurrentSchema()
×
3908
        }
×
3909

16✔
3910
        return this.deleteTypeormMetadataSql({
16✔
3911
            type: MetadataTableType.VIEW,
16✔
3912
            database: parsedTableName.database,
16✔
3913
            schema: parsedTableName.schema,
16✔
3914
            name: parsedTableName.tableName,
16✔
3915
        })
16✔
3916
    }
16✔
3917

23✔
3918
    /**
23✔
3919
     * Builds create index sql.
23✔
3920
     * @param table
23✔
3921
     * @param index
23✔
3922
     */
23✔
3923
    protected createIndexSql(table: Table, index: TableIndex): Query {
23✔
3924
        const columns = index.columnNames
4,534✔
3925
            .map((columnName) => `"${columnName}"`)
4,534✔
3926
            .join(", ")
4,534✔
3927
        return new Query(
4,534✔
3928
            `CREATE ${index.isUnique ? "UNIQUE " : ""}INDEX "${
4,534✔
3929
                index.name
4,534✔
3930
            }" ON ${this.escapePath(table)} (${columns}) ${
4,534✔
3931
                index.where ? "WHERE " + index.where : ""
4,534✔
3932
            }`,
4,534✔
3933
        )
4,534✔
3934
    }
4,534✔
3935

23✔
3936
    /**
23✔
3937
     * Builds drop index sql.
23✔
3938
     * @param table
23✔
3939
     * @param indexOrName
23✔
3940
     */
23✔
3941
    protected dropIndexSql(
23✔
3942
        table: Table,
4,534✔
3943
        indexOrName: TableIndex | string,
4,534✔
3944
    ): Query {
4,534✔
3945
        const indexName = InstanceChecker.isTableIndex(indexOrName)
4,534✔
3946
            ? indexOrName.name
4,534✔
3947
            : indexOrName
4,534!
3948
        return new Query(
4,534✔
3949
            `DROP INDEX "${indexName}" ON ${this.escapePath(table)}`,
4,534✔
3950
        )
4,534✔
3951
    }
4,534✔
3952

23✔
3953
    /**
23✔
3954
     * Builds create primary key sql.
23✔
3955
     * @param table
23✔
3956
     * @param columnNames
23✔
3957
     * @param constraintName
23✔
3958
     */
23✔
3959
    protected createPrimaryKeySql(
23✔
3960
        table: Table,
12✔
3961
        columnNames: string[],
12✔
3962
        constraintName?: string,
12✔
3963
    ): Query {
12✔
3964
        const primaryKeyName = constraintName
12✔
3965
            ? constraintName
12✔
3966
            : this.connection.namingStrategy.primaryKeyName(table, columnNames)
12✔
3967

12✔
3968
        const columnNamesString = columnNames
12✔
3969
            .map((columnName) => `"${columnName}"`)
12✔
3970
            .join(", ")
12✔
3971
        return new Query(
12✔
3972
            `ALTER TABLE ${this.escapePath(
12✔
3973
                table,
12✔
3974
            )} ADD CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNamesString})`,
12✔
3975
        )
12✔
3976
    }
12✔
3977

23✔
3978
    /**
23✔
3979
     * Builds drop primary key sql.
23✔
3980
     * @param table
23✔
3981
     */
23✔
3982
    protected dropPrimaryKeySql(table: Table): Query {
23✔
3983
        const columnNames = table.primaryColumns.map((column) => column.name)
12✔
3984
        const constraintName = table.primaryColumns[0].primaryKeyConstraintName
12✔
3985
        const primaryKeyName = constraintName
12✔
3986
            ? constraintName
12!
3987
            : this.connection.namingStrategy.primaryKeyName(table, columnNames)
12✔
3988

12✔
3989
        return new Query(
12✔
3990
            `ALTER TABLE ${this.escapePath(
12✔
3991
                table,
12✔
3992
            )} DROP CONSTRAINT "${primaryKeyName}"`,
12✔
3993
        )
12✔
3994
    }
12✔
3995

23✔
3996
    /**
23✔
3997
     * Builds create unique constraint sql.
23✔
3998
     * @param table
23✔
3999
     * @param uniqueConstraint
23✔
4000
     */
23✔
4001
    protected createUniqueConstraintSql(
23✔
4002
        table: Table,
38✔
4003
        uniqueConstraint: TableUnique,
38✔
4004
    ): Query {
38✔
4005
        const columnNames = uniqueConstraint.columnNames
38✔
4006
            .map((column) => `"` + column + `"`)
38✔
4007
            .join(", ")
38✔
4008
        return new Query(
38✔
4009
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
38✔
4010
                uniqueConstraint.name
38✔
4011
            }" UNIQUE (${columnNames})`,
38✔
4012
        )
38✔
4013
    }
38✔
4014

23✔
4015
    /**
23✔
4016
     * Builds drop unique constraint sql.
23✔
4017
     * @param table
23✔
4018
     * @param uniqueOrName
23✔
4019
     */
23✔
4020
    protected dropUniqueConstraintSql(
23✔
4021
        table: Table,
38✔
4022
        uniqueOrName: TableUnique | string,
38✔
4023
    ): Query {
38✔
4024
        const uniqueName = InstanceChecker.isTableUnique(uniqueOrName)
38✔
4025
            ? uniqueOrName.name
38✔
4026
            : uniqueOrName
38!
4027
        return new Query(
38✔
4028
            `ALTER TABLE ${this.escapePath(
38✔
4029
                table,
38✔
4030
            )} DROP CONSTRAINT "${uniqueName}"`,
38✔
4031
        )
38✔
4032
    }
38✔
4033

23✔
4034
    /**
23✔
4035
     * Builds create check constraint sql.
23✔
4036
     * @param table
23✔
4037
     * @param checkConstraint
23✔
4038
     */
23✔
4039
    protected createCheckConstraintSql(
23✔
4040
        table: Table,
24✔
4041
        checkConstraint: TableCheck,
24✔
4042
    ): Query {
24✔
4043
        return new Query(
24✔
4044
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
24✔
4045
                checkConstraint.name
24✔
4046
            }" CHECK (${checkConstraint.expression})`,
24✔
4047
        )
24✔
4048
    }
24✔
4049

23✔
4050
    /**
23✔
4051
     * Builds drop check constraint sql.
23✔
4052
     * @param table
23✔
4053
     * @param checkOrName
23✔
4054
     */
23✔
4055
    protected dropCheckConstraintSql(
23✔
4056
        table: Table,
24✔
4057
        checkOrName: TableCheck | string,
24✔
4058
    ): Query {
24✔
4059
        const checkName = InstanceChecker.isTableCheck(checkOrName)
24✔
4060
            ? checkOrName.name
24✔
4061
            : checkOrName
24!
4062
        return new Query(
24✔
4063
            `ALTER TABLE ${this.escapePath(
24✔
4064
                table,
24✔
4065
            )} DROP CONSTRAINT "${checkName}"`,
24✔
4066
        )
24✔
4067
    }
24✔
4068

23✔
4069
    /**
23✔
4070
     * Builds create foreign key sql.
23✔
4071
     * @param table
23✔
4072
     * @param foreignKey
23✔
4073
     */
23✔
4074
    protected createForeignKeySql(
23✔
4075
        table: Table,
6,942✔
4076
        foreignKey: TableForeignKey,
6,942✔
4077
    ): Query {
6,942✔
4078
        const columnNames = foreignKey.columnNames
6,942✔
4079
            .map((column) => `"` + column + `"`)
6,942✔
4080
            .join(", ")
6,942✔
4081
        const referencedColumnNames = foreignKey.referencedColumnNames
6,942✔
4082
            .map((column) => `"` + column + `"`)
6,942✔
4083
            .join(",")
6,942✔
4084
        let sql =
6,942✔
4085
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
6,942✔
4086
                foreignKey.name
6,942✔
4087
            }" FOREIGN KEY (${columnNames}) ` +
6,942✔
4088
            `REFERENCES ${this.escapePath(
6,942✔
4089
                this.getTablePath(foreignKey),
6,942✔
4090
            )}(${referencedColumnNames})`
6,942✔
4091
        if (foreignKey.onDelete) sql += ` ON DELETE ${foreignKey.onDelete}`
6,942✔
4092
        if (foreignKey.onUpdate) sql += ` ON UPDATE ${foreignKey.onUpdate}`
6,942✔
4093

6,942✔
4094
        return new Query(sql)
6,942✔
4095
    }
6,942✔
4096

23✔
4097
    /**
23✔
4098
     * Builds drop foreign key sql.
23✔
4099
     * @param table
23✔
4100
     * @param foreignKeyOrName
23✔
4101
     */
23✔
4102
    protected dropForeignKeySql(
23✔
4103
        table: Table,
6,952✔
4104
        foreignKeyOrName: TableForeignKey | string,
6,952✔
4105
    ): Query {
6,952✔
4106
        const foreignKeyName = InstanceChecker.isTableForeignKey(
6,952✔
4107
            foreignKeyOrName,
6,952✔
4108
        )
6,952✔
4109
            ? foreignKeyOrName.name
6,952✔
4110
            : foreignKeyOrName
6,952!
4111
        return new Query(
6,952✔
4112
            `ALTER TABLE ${this.escapePath(
6,952✔
4113
                table,
6,952✔
4114
            )} DROP CONSTRAINT "${foreignKeyName}"`,
6,952✔
4115
        )
6,952✔
4116
    }
6,952✔
4117

23✔
4118
    /**
23✔
4119
     * Escapes given table or View path.
23✔
4120
     * @param target
23✔
4121
     */
23✔
4122
    protected escapePath(target: Table | View | string): string {
23✔
4123
        const { database, schema, tableName } =
51,742✔
4124
            this.driver.parseTableName(target)
51,742✔
4125

51,742✔
4126
        if (database && database !== this.driver.database) {
51,742✔
4127
            if (schema && schema !== this.driver.searchSchema) {
96✔
4128
                return `"${database}"."${schema}"."${tableName}"`
84✔
4129
            }
84✔
4130

12✔
4131
            return `"${database}".."${tableName}"`
12✔
4132
        }
12✔
4133

51,646✔
4134
        if (schema && schema !== this.driver.searchSchema) {
51,742✔
4135
            return `"${schema}"."${tableName}"`
148✔
4136
        }
148✔
4137

51,498✔
4138
        return `"${tableName}"`
51,498✔
4139
    }
51,498✔
4140

23✔
4141
    /**
23✔
4142
     * Concat database name and schema name to the foreign key name.
23✔
4143
     * Needs because FK name is relevant to the schema and database.
23✔
4144
     * @param fkName
23✔
4145
     * @param schemaName
23✔
4146
     * @param dbName
23✔
4147
     */
23✔
4148
    protected buildForeignKeyName(
23✔
4149
        fkName: string,
8✔
4150
        schemaName: string | undefined,
8✔
4151
        dbName: string | undefined,
8✔
4152
    ): string {
8✔
4153
        let joinedFkName = fkName
8✔
4154
        if (schemaName && schemaName !== this.driver.searchSchema)
8✔
4155
            joinedFkName = schemaName + "." + joinedFkName
8✔
4156
        if (dbName && dbName !== this.driver.database)
8✔
4157
            joinedFkName = dbName + "." + joinedFkName
8✔
4158

8✔
4159
        return joinedFkName
8✔
4160
    }
8✔
4161

23✔
4162
    /**
23✔
4163
     * Removes parenthesis around default value.
23✔
4164
     * Sql server returns default value with parenthesis around, e.g.
23✔
4165
     *  ('My text') - for string
23✔
4166
     *  ((1)) - for number
23✔
4167
     *  (newsequentialId()) - for function
23✔
4168
     * @param defaultValue
23✔
4169
     */
23✔
4170
    protected removeParenthesisFromDefault(defaultValue: string): any {
23✔
4171
        if (defaultValue.substr(0, 1) !== "(") return defaultValue
452✔
4172
        const normalizedDefault = defaultValue.substr(
228✔
4173
            1,
228✔
4174
            defaultValue.lastIndexOf(")") - 1,
228✔
4175
        )
228✔
4176
        return this.removeParenthesisFromDefault(normalizedDefault)
228✔
4177
    }
228✔
4178

23✔
4179
    /**
23✔
4180
     * Builds a query for create column.
23✔
4181
     * @param table
23✔
4182
     * @param column
23✔
4183
     * @param skipIdentity
23✔
4184
     * @param createDefault
23✔
4185
     * @param skipEnum
23✔
4186
     */
23✔
4187
    protected buildCreateColumnSql(
23✔
4188
        table: Table,
34,142✔
4189
        column: TableColumn,
34,142✔
4190
        skipIdentity: boolean,
34,142✔
4191
        createDefault: boolean,
34,142✔
4192
        skipEnum?: boolean,
34,142✔
4193
    ) {
34,142✔
4194
        let c = `"${column.name}" ${this.connection.driver.createFullType(
34,142✔
4195
            column,
34,142✔
4196
        )}`
34,142✔
4197

34,142✔
4198
        if (!skipEnum && column.enum) {
34,142✔
4199
            const expression = this.getEnumExpression(column)
48✔
4200
            const checkName =
48✔
4201
                this.connection.namingStrategy.checkConstraintName(
48✔
4202
                    table,
48✔
4203
                    expression,
48✔
4204
                    true,
48✔
4205
                )
48✔
4206
            c += ` CONSTRAINT ${checkName} CHECK(${expression})`
48✔
4207
        }
48✔
4208

34,142✔
4209
        if (column.collation) c += " COLLATE " + column.collation
34,142✔
4210

34,142✔
4211
        if (column.asExpression) {
34,142✔
4212
            c += ` AS (${column.asExpression})`
78✔
4213
            if (column.generatedType === "STORED") {
78✔
4214
                c += ` PERSISTED`
42✔
4215

42✔
4216
                // NOT NULL can be specified for computed columns only if PERSISTED is also specified
42✔
4217
                if (column.isNullable !== true) c += " NOT NULL"
42✔
4218
            }
42✔
4219
        } else {
34,142✔
4220
            if (column.isNullable !== true) c += " NOT NULL"
34,064✔
4221
        }
34,064✔
4222

34,142✔
4223
        if (
34,142✔
4224
            column.isGenerated === true &&
34,142✔
4225
            column.generationStrategy === "increment" &&
34,142✔
4226
            !skipIdentity
6,266✔
4227
        )
34,142✔
4228
            // don't use skipPrimary here since updates can update already exist primary without auto inc.
34,142✔
4229
            c += " IDENTITY(1,1)"
34,142✔
4230

34,142✔
4231
        if (
34,142✔
4232
            column.default !== undefined &&
34,142✔
4233
            column.default !== null &&
34,142✔
4234
            createDefault
2,006✔
4235
        ) {
34,142✔
4236
            // we create named constraint to be able to delete this constraint when column been dropped
1,994✔
4237
            const defaultName =
1,994✔
4238
                this.connection.namingStrategy.defaultConstraintName(
1,994✔
4239
                    table,
1,994✔
4240
                    column.name,
1,994✔
4241
                )
1,994✔
4242
            c += ` CONSTRAINT "${defaultName}" DEFAULT ${column.default}`
1,994✔
4243
        }
1,994✔
4244

34,142✔
4245
        if (
34,142✔
4246
            column.isGenerated &&
34,142✔
4247
            column.generationStrategy === "uuid" &&
34,142✔
4248
            !column.default
272✔
4249
        ) {
34,142✔
4250
            // we create named constraint to be able to delete this constraint when column been dropped
272✔
4251
            const defaultName =
272✔
4252
                this.connection.namingStrategy.defaultConstraintName(
272✔
4253
                    table,
272✔
4254
                    column.name,
272✔
4255
                )
272✔
4256
            c += ` CONSTRAINT "${defaultName}" DEFAULT NEWSEQUENTIALID()`
272✔
4257
        }
272✔
4258
        return c
34,142✔
4259
    }
34,142✔
4260

23✔
4261
    private getEnumExpression(column: TableColumn) {
23✔
4262
        if (!column.enum) {
52!
4263
            throw new Error(`Enum is not defined in column ${column.name}`)
×
4264
        }
×
4265
        return (
52✔
4266
            column.name +
52✔
4267
            " IN (" +
52✔
4268
            column.enum.map((val) => "'" + val + "'").join(",") +
52✔
4269
            ")"
52✔
4270
        )
52✔
4271
    }
52✔
4272

23✔
4273
    protected isEnumCheckConstraint(name: string): boolean {
23✔
4274
        return name.indexOf("CHK_") !== -1 && name.indexOf("_ENUM") !== -1
218✔
4275
    }
218✔
4276

23✔
4277
    /**
23✔
4278
     * Converts MssqlParameter into real mssql parameter type.
23✔
4279
     * @param parameter
23✔
4280
     */
23✔
4281
    protected mssqlParameterToNativeParameter(parameter: MssqlParameter): any {
23✔
4282
        switch (this.driver.normalizeType({ type: parameter.type as any })) {
54,214✔
4283
            case "bit":
54,214✔
4284
                return this.driver.mssql.Bit
4,104✔
4285
            case "bigint":
54,214✔
4286
                return this.driver.mssql.BigInt
64✔
4287
            case "decimal":
54,214✔
4288
                return this.driver.mssql.Decimal(...parameter.params)
8✔
4289
            case "float":
54,214✔
4290
                return this.driver.mssql.Float
60✔
4291
            case "int":
54,214✔
4292
                return this.driver.mssql.Int
24,626✔
4293
            case "money":
54,214✔
4294
                return this.driver.mssql.Money
2✔
4295
            case "numeric":
54,214✔
4296
                return this.driver.mssql.Numeric(...parameter.params)
4✔
4297
            case "smallint":
54,214✔
4298
                return this.driver.mssql.SmallInt
2✔
4299
            case "smallmoney":
54,214✔
4300
                return this.driver.mssql.SmallMoney
2✔
4301
            case "real":
54,214✔
4302
                return this.driver.mssql.Real
2✔
4303
            case "tinyint":
54,214✔
4304
                return this.driver.mssql.TinyInt
2✔
4305
            case "char":
54,214✔
4306
                if (
10✔
4307
                    this.driver.options.options
10✔
4308
                        ?.disableAsciiToUnicodeParamConversion
10✔
4309
                ) {
10✔
4310
                    return this.driver.mssql.Char(...parameter.params)
2✔
4311
                }
2✔
4312
                return this.driver.mssql.NChar(...parameter.params)
8✔
4313
            case "nchar":
54,214✔
4314
                return this.driver.mssql.NChar(...parameter.params)
4✔
4315
            case "text":
54,214✔
4316
                if (
10✔
4317
                    this.driver.options.options
10✔
4318
                        ?.disableAsciiToUnicodeParamConversion
10!
4319
                ) {
10!
4320
                    return this.driver.mssql.Text
×
4321
                }
×
4322
                return this.driver.mssql.Ntext
10✔
4323
            case "ntext":
54,214✔
4324
                return this.driver.mssql.Ntext
14✔
4325
            case "varchar":
54,214✔
4326
                if (
200✔
4327
                    this.driver.options.options
200✔
4328
                        ?.disableAsciiToUnicodeParamConversion
200✔
4329
                ) {
200✔
4330
                    return this.driver.mssql.VarChar(...parameter.params)
2✔
4331
                }
2✔
4332
                return this.driver.mssql.NVarChar(...parameter.params)
198✔
4333
            case "nvarchar":
54,214✔
4334
                return this.driver.mssql.NVarChar(...parameter.params)
24,418✔
4335
            case "xml":
54,214!
4336
                return this.driver.mssql.Xml
×
4337
            case "time":
54,214✔
4338
                return this.driver.mssql.Time(...parameter.params)
12✔
4339
            case "date":
54,214✔
4340
                return this.driver.mssql.Date
10✔
4341
            case "datetime":
54,214✔
4342
                return this.driver.mssql.DateTime
50✔
4343
            case "datetime2":
54,214✔
4344
                return this.driver.mssql.DateTime2(...parameter.params)
24✔
4345
            case "datetimeoffset":
54,214✔
4346
                return this.driver.mssql.DateTimeOffset(...parameter.params)
8✔
4347
            case "smalldatetime":
54,214✔
4348
                return this.driver.mssql.SmallDateTime
2✔
4349
            case "uniqueidentifier":
54,214✔
4350
                return this.driver.mssql.UniqueIdentifier
504✔
4351
            case "variant":
54,214!
4352
                return this.driver.mssql.Variant
×
4353
            case "binary":
54,214✔
4354
                return this.driver.mssql.Binary
26✔
4355
            case "varbinary":
54,214✔
4356
                return this.driver.mssql.VarBinary(...parameter.params)
4✔
4357
            case "image":
54,214✔
4358
                return this.driver.mssql.Image
2✔
4359
            case "udt":
54,214!
4360
                return this.driver.mssql.UDT
×
4361
            case "rowversion":
54,214!
4362
                return this.driver.mssql.RowVersion
×
4363
            case "vector":
54,214✔
4364
                return this.driver.mssql.Ntext
20✔
4365
        }
54,214✔
4366
    }
54,214✔
4367

23✔
4368
    /**
23✔
4369
     * Converts string literal of isolation level to enum.
23✔
4370
     * The underlying mssql driver requires an enum for the isolation level.
23✔
4371
     * @param isolation
23✔
4372
     */
23✔
4373
    convertIsolationLevel(isolation: IsolationLevel) {
23✔
4374
        const ISOLATION_LEVEL = this.driver.mssql.ISOLATION_LEVEL
12✔
4375
        switch (isolation) {
12✔
4376
            case "READ UNCOMMITTED":
12✔
4377
                return ISOLATION_LEVEL.READ_UNCOMMITTED
4✔
4378
            case "REPEATABLE READ":
12✔
4379
                return ISOLATION_LEVEL.REPEATABLE_READ
2✔
4380
            case "SERIALIZABLE":
12✔
4381
                return ISOLATION_LEVEL.SERIALIZABLE
4✔
4382

12✔
4383
            case "READ COMMITTED":
12✔
4384
            default:
12✔
4385
                return ISOLATION_LEVEL.READ_COMMITTED
2✔
4386
        }
12✔
4387
    }
12✔
4388

23✔
4389
    /**
23✔
4390
     * Change table comment.
23✔
4391
     * @param tableOrName
23✔
4392
     * @param comment
23✔
4393
     */
23✔
4394
    changeTableComment(
23✔
4395
        tableOrName: Table | string,
×
4396
        comment?: string,
×
4397
    ): Promise<void> {
×
4398
        throw new TypeORMError(
×
4399
            `sqlserver driver does not support change table comment.`,
×
4400
        )
×
4401
    }
×
4402
}
23✔
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