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

typeorm / typeorm / 23305316903

19 Mar 2026 04:27PM UTC coverage: 73.281% (-0.005%) from 73.286%
23305316903

push

github

web-flow
refactor(repository)!: remove deprecated findOneById methods (#12198)

37488 of 47755 branches covered (78.5%)

Branch coverage included in aggregate %.

83716 of 117642 relevant lines covered (71.16%)

64521.65 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

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

18,864✔
110
            if (this.transactionDepth === 0) {
18,864✔
111
                const pool = await (this.mode === "slave"
18,854✔
112
                    ? this.driver.obtainSlaveConnection()
18,854!
113
                    : this.driver.obtainMasterConnection())
18,854✔
114
                this.databaseConnection = pool.transaction()
18,854✔
115
                this.connection.logger.logQuery("BEGIN TRANSACTION")
18,854✔
116
                if (isolationLevel) {
18,854✔
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 {
18,854✔
125
                    this.databaseConnection.begin(transactionCallback)
18,842✔
126
                }
18,842✔
127
            } else {
18,864✔
128
                await this.query(
10✔
129
                    `SAVE TRANSACTION typeorm_${this.transactionDepth}`,
10✔
130
                )
10✔
131
                ok()
10✔
132
            }
10✔
133
            this.transactionDepth += 1
18,864✔
134
        })
18,864✔
135

18,864✔
136
        await this.broadcaster.broadcast("AfterTransactionStart")
18,864✔
137
    }
18,864✔
138

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

18,814✔
146
        if (!this.isTransactionActive) throw new TransactionNotStartedError()
18,814!
147

18,814✔
148
        await this.broadcaster.broadcast("BeforeTransactionCommit")
18,814✔
149

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

18,810✔
157
                    await this.broadcaster.broadcast("AfterTransactionCommit")
18,810✔
158

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

28✔
168
    /**
28✔
169
     * Rollbacks transaction.
28✔
170
     * Error will be thrown if transaction was not started.
28✔
171
     */
28✔
172
    async rollbackTransaction(): Promise<void> {
28✔
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

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

109,836✔
214
        const release = await this.lock.acquire()
109,836✔
215

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

109,836✔
219
        const broadcasterResult = new BroadcasterResult()
109,836✔
220

109,836✔
221
        try {
109,836✔
222
            const pool = await (this.mode === "slave"
109,836✔
223
                ? this.driver.obtainSlaveConnection()
109,836✔
224
                : this.driver.obtainMasterConnection())
109,836✔
225
            const request = new this.driver.mssql.Request(
109,836✔
226
                this.isTransactionActive ? this.databaseConnection : pool,
109,836✔
227
            )
109,836✔
228
            if (parameters && parameters.length) {
109,836✔
229
                parameters.forEach((parameter, index) => {
36,282✔
230
                    const parameterName = index.toString()
80,326✔
231
                    if (InstanceChecker.isMssqlParameter(parameter)) {
80,326✔
232
                        const mssqlParameter =
53,014✔
233
                            this.mssqlParameterToNativeParameter(parameter)
53,014✔
234
                        if (mssqlParameter) {
53,014✔
235
                            request.input(
52,950✔
236
                                parameterName,
52,950✔
237
                                mssqlParameter,
52,950✔
238
                                parameter.value,
52,950✔
239
                            )
52,950✔
240
                        } else {
53,014✔
241
                            request.input(parameterName, parameter.value)
64✔
242
                        }
64✔
243
                    } else {
80,326✔
244
                        request.input(parameterName, parameter)
27,312✔
245
                    }
27,312✔
246
                })
36,282✔
247
            }
36,282✔
248
            const queryStartTime = Date.now()
109,836✔
249

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

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

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

109,836✔
280
                    if (err) {
109,836✔
281
                        fail(new QueryFailedError(query, parameters, err))
48✔
282
                    }
48✔
283

109,836✔
284
                    ok(raw)
109,836✔
285
                })
109,836✔
286
            })
109,836✔
287

109,788✔
288
            const result = new QueryResult()
109,788✔
289

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

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

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

109,788✔
308
            if (useStructuredResult) {
109,836✔
309
                return result
31,286✔
310
            } else {
109,836✔
311
                return result.raw
78,502✔
312
            }
78,502✔
313
        } catch (err) {
109,836✔
314
            this.driver.connection.logger.logQueryError(
48✔
315
                err,
48✔
316
                query,
48✔
317
                parameters,
48✔
318
                this,
48✔
319
            )
48✔
320
            this.broadcaster.broadcastAfterQueryEvent(
48✔
321
                broadcasterResult,
48✔
322
                query,
48✔
323
                parameters,
48✔
324
                false,
48✔
325
                undefined,
48✔
326
                undefined,
48✔
327
                err,
48✔
328
            )
48✔
329

48✔
330
            throw err
48✔
331
        } finally {
109,836!
332
            await broadcasterResult.wait()
109,836✔
333

109,836✔
334
            release()
109,836✔
335
        }
109,836✔
336
    }
109,836✔
337

28✔
338
    /**
28✔
339
     * Returns raw data stream.
28✔
340
     * @param query
28✔
341
     * @param parameters
28✔
342
     * @param onEnd
28✔
343
     * @param onError
28✔
344
     */
28✔
345
    async stream(
28✔
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

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

28✔
414
    /**
28✔
415
     * Returns all available schema names including system schemas.
28✔
416
     * If database parameter specified, returns schemas of that database.
28✔
417
     * @param database
28✔
418
     */
28✔
419
    async getSchemas(database?: string): Promise<string[]> {
28✔
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

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

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

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

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

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

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

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

3,268✔
484
        const sql = `SELECT * FROM ${this.driver.escape(parsedTableName.database)}."INFORMATION_SCHEMA"."TABLES" WHERE "TABLE_NAME" = @0 AND "TABLE_SCHEMA" = @1`
3,268✔
485
        const result = await this.query(sql, [
3,268✔
486
            parsedTableName.tableName,
3,268✔
487
            parsedTableName.schema,
3,268✔
488
        ])
3,268✔
489
        return result.length ? true : false
3,268✔
490
    }
3,268✔
491

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

56✔
503
        if (!parsedTableName.database) {
56!
504
            parsedTableName.database = await this.getCurrentDatabase()
×
505
        }
×
506

56✔
507
        if (!parsedTableName.schema) {
56!
508
            parsedTableName.schema = await this.getCurrentSchema()
×
509
        }
×
510

56✔
511
        const sql = `SELECT * FROM ${this.driver.escape(parsedTableName.database)}."INFORMATION_SCHEMA"."COLUMNS" WHERE "TABLE_NAME" = @0 AND "TABLE_SCHEMA" = @1 AND "COLUMN_NAME" = @2`
56✔
512
        const result = await this.query(sql, [
56✔
513
            parsedTableName.tableName,
56✔
514
            parsedTableName.schema,
56✔
515
            columnName,
56✔
516
        ])
56✔
517
        return result.length ? true : false
56✔
518
    }
56✔
519

28✔
520
    /**
28✔
521
     * Creates a new database.
28✔
522
     * @param database
28✔
523
     * @param ifNotExists
28✔
524
     */
28✔
525
    async createDatabase(
28✔
526
        database: string,
42✔
527
        ifNotExists?: boolean,
42✔
528
    ): Promise<void> {
42✔
529
        const escapedQuote = database.replaceAll("'", "''")
42✔
530
        const up = ifNotExists
42✔
531
            ? `IF DB_ID('${escapedQuote}') IS NULL CREATE DATABASE ${this.driver.escape(database)}`
42✔
532
            : `CREATE DATABASE ${this.driver.escape(database)}`
42!
533
        const down = `DROP DATABASE ${this.driver.escape(database)}`
42✔
534
        await this.executeQueries(new Query(up), new Query(down))
42✔
535
    }
40✔
536

28✔
537
    /**
28✔
538
     * Drops database.
28✔
539
     * @param database
28✔
540
     * @param ifExists
28✔
541
     */
28✔
542
    async dropDatabase(database: string, ifExists?: boolean): Promise<void> {
28✔
543
        const escapedQuote = database.replaceAll("'", "''")
30✔
544
        const up = ifExists
30✔
545
            ? `IF DB_ID('${escapedQuote}') IS NOT NULL DROP DATABASE ${this.driver.escape(database)}`
30✔
546
            : `DROP DATABASE ${this.driver.escape(database)}`
30✔
547
        const down = `CREATE DATABASE ${this.driver.escape(database)}`
30✔
548
        await this.executeQueries(new Query(up), new Query(down))
30✔
549
    }
28✔
550

28✔
551
    /**
28✔
552
     * Creates table schema.
28✔
553
     * If database name also specified (e.g. 'dbName.schemaName') schema will be created in specified database.
28✔
554
     * @param schemaPath
28✔
555
     * @param ifNotExists
28✔
556
     */
28✔
557
    async createSchema(
28✔
558
        schemaPath: string,
54✔
559
        ifNotExists?: boolean,
54✔
560
    ): Promise<void> {
54✔
561
        const upQueries: Query[] = []
54✔
562
        const downQueries: Query[] = []
54✔
563

54✔
564
        if (schemaPath.indexOf(".") === -1) {
54✔
565
            const escapedQuote = schemaPath.replaceAll("'", "''")
40✔
566
            const escapedExec = this.driver
40✔
567
                .escape(schemaPath)
40✔
568
                .replaceAll("'", "''")
40✔
569
            const upQuery = ifNotExists
40✔
570
                ? `IF SCHEMA_ID('${escapedQuote}') IS NULL BEGIN EXEC ('CREATE SCHEMA ${escapedExec}') END`
40✔
571
                : `CREATE SCHEMA ${this.driver.escape(schemaPath)}`
40!
572
            upQueries.push(new Query(upQuery))
40✔
573
            downQueries.push(
40✔
574
                new Query(`DROP SCHEMA ${this.driver.escape(schemaPath)}`),
40✔
575
            )
40✔
576
        } else {
54✔
577
            const dbName = schemaPath.split(".")[0]
14✔
578
            const schema = schemaPath.split(".")[1]
14✔
579
            const escapedSchemaQuote = schema.replaceAll("'", "''")
14✔
580
            const escapedSchemaExec = this.driver
14✔
581
                .escape(schema)
14✔
582
                .replaceAll("'", "''")
14✔
583
            const currentDB = await this.getCurrentDatabase()
14✔
584
            upQueries.push(new Query(`USE ${this.driver.escape(dbName)}`))
14✔
585
            downQueries.push(new Query(`USE ${this.driver.escape(currentDB)}`))
14✔
586

14✔
587
            const upQuery = ifNotExists
14✔
588
                ? `IF SCHEMA_ID('${escapedSchemaQuote}') IS NULL BEGIN EXEC ('CREATE SCHEMA ${escapedSchemaExec}') END`
14✔
589
                : `CREATE SCHEMA ${this.driver.escape(schema)}`
14!
590
            upQueries.push(new Query(upQuery))
14✔
591
            downQueries.push(
14✔
592
                new Query(`DROP SCHEMA ${this.driver.escape(schema)}`),
14✔
593
            )
14✔
594

14✔
595
            upQueries.push(new Query(`USE ${this.driver.escape(currentDB)}`))
14✔
596
            downQueries.push(new Query(`USE ${this.driver.escape(dbName)}`))
14✔
597
        }
14✔
598

54✔
599
        await this.executeQueries(upQueries, downQueries)
54✔
600
    }
52✔
601

28✔
602
    /**
28✔
603
     * Drops table schema.
28✔
604
     * If database name also specified (e.g. 'dbName.schemaName') schema will be dropped in specified database.
28✔
605
     * @param schemaPath
28✔
606
     * @param ifExists
28✔
607
     */
28✔
608
    async dropSchema(schemaPath: string, ifExists?: boolean): Promise<void> {
28✔
609
        const upQueries: Query[] = []
28✔
610
        const downQueries: Query[] = []
28✔
611

28✔
612
        if (schemaPath.indexOf(".") === -1) {
28✔
613
            const escapedQuote = schemaPath.replaceAll("'", "''")
28✔
614
            const escapedExec = this.driver
28✔
615
                .escape(schemaPath)
28✔
616
                .replaceAll("'", "''")
28✔
617
            const upQuery = ifExists
28✔
618
                ? `IF SCHEMA_ID('${escapedQuote}') IS NOT NULL BEGIN EXEC ('DROP SCHEMA ${escapedExec}') END`
28✔
619
                : `DROP SCHEMA ${this.driver.escape(schemaPath)}`
28✔
620
            upQueries.push(new Query(upQuery))
28✔
621
            downQueries.push(
28✔
622
                new Query(`CREATE SCHEMA ${this.driver.escape(schemaPath)}`),
28✔
623
            )
28✔
624
        } else {
28!
625
            const dbName = schemaPath.split(".")[0]
×
626
            const schema = schemaPath.split(".")[1]
×
627
            const escapedSchemaQuote = schema.replaceAll("'", "''")
×
628
            const escapedSchemaExec = this.driver
×
629
                .escape(schema)
×
630
                .replaceAll("'", "''")
×
631
            const currentDB = await this.getCurrentDatabase()
×
632
            upQueries.push(new Query(`USE ${this.driver.escape(dbName)}`))
×
633
            downQueries.push(new Query(`USE ${this.driver.escape(currentDB)}`))
×
634

×
635
            const upQuery = ifExists
×
636
                ? `IF SCHEMA_ID('${escapedSchemaQuote}') IS NOT NULL BEGIN EXEC ('DROP SCHEMA ${escapedSchemaExec}') END`
×
637
                : `DROP SCHEMA ${this.driver.escape(schema)}`
×
638
            upQueries.push(new Query(upQuery))
×
639
            downQueries.push(
×
640
                new Query(`CREATE SCHEMA ${this.driver.escape(schema)}`),
×
641
            )
×
642

×
643
            upQueries.push(new Query(`USE ${this.driver.escape(currentDB)}`))
×
644
            downQueries.push(new Query(`USE ${this.driver.escape(dbName)}`))
×
645
        }
×
646

28✔
647
        await this.executeQueries(upQueries, downQueries)
28✔
648
    }
28✔
649

28✔
650
    /**
28✔
651
     * Creates a new table.
28✔
652
     * @param table
28✔
653
     * @param ifNotExists
28✔
654
     * @param createForeignKeys
28✔
655
     * @param createIndices
28✔
656
     */
28✔
657
    async createTable(
28✔
658
        table: Table,
10,398✔
659
        ifNotExists: boolean = false,
10,398✔
660
        createForeignKeys: boolean = true,
10,398✔
661
        createIndices: boolean = true,
10,398✔
662
    ): Promise<void> {
10,398✔
663
        if (ifNotExists) {
10,398✔
664
            const isTableExist = await this.hasTable(table)
66✔
665
            if (isTableExist) return Promise.resolve()
66✔
666
        }
66✔
667
        const upQueries: Query[] = []
10,394✔
668
        const downQueries: Query[] = []
10,394✔
669

10,394✔
670
        upQueries.push(this.createTableSql(table, createForeignKeys))
10,394✔
671
        downQueries.push(this.dropTableSql(table))
10,394✔
672

10,394✔
673
        // if createForeignKeys is true, we must drop created foreign keys in down query.
10,394✔
674
        // createTable does not need separate method to create foreign keys, because it create fk's in the same query with table creation.
10,394✔
675
        if (createForeignKeys)
10,394✔
676
            table.foreignKeys.forEach((foreignKey) =>
10,398✔
677
                downQueries.push(this.dropForeignKeySql(table, foreignKey)),
110✔
678
            )
110✔
679

10,394✔
680
        if (createIndices) {
10,394✔
681
            table.indices.forEach((index) => {
10,394✔
682
                // new index may be passed without name. In this case we generate index name manually.
4,330✔
683
                if (!index.name)
4,330✔
684
                    index.name = this.connection.namingStrategy.indexName(
4,330✔
685
                        table,
6✔
686
                        index.columnNames,
6✔
687
                        index.where,
6✔
688
                    )
6✔
689
                upQueries.push(this.createIndexSql(table, index))
4,330✔
690
                downQueries.push(this.dropIndexSql(table, index))
4,330✔
691
            })
10,394✔
692
        }
10,394✔
693

10,394✔
694
        // if table have column with generated type, we must add the expression to the metadata table
10,394✔
695
        const generatedColumns = table.columns.filter(
10,394✔
696
            (column) => column.generatedType && column.asExpression,
10,394✔
697
        )
10,394✔
698

10,394✔
699
        for (const column of generatedColumns) {
10,398✔
700
            const parsedTableName = this.driver.parseTableName(table)
56✔
701

56✔
702
            if (!parsedTableName.schema) {
56!
703
                parsedTableName.schema = await this.getCurrentSchema()
×
704
            }
×
705

56✔
706
            const insertQuery = this.insertTypeormMetadataSql({
56✔
707
                database: parsedTableName.database,
56✔
708
                schema: parsedTableName.schema,
56✔
709
                table: parsedTableName.tableName,
56✔
710
                type: MetadataTableType.GENERATED_COLUMN,
56✔
711
                name: column.name,
56✔
712
                value: column.asExpression,
56✔
713
            })
56✔
714

56✔
715
            const deleteQuery = this.deleteTypeormMetadataSql({
56✔
716
                database: parsedTableName.database,
56✔
717
                schema: parsedTableName.schema,
56✔
718
                table: parsedTableName.tableName,
56✔
719
                type: MetadataTableType.GENERATED_COLUMN,
56✔
720
                name: column.name,
56✔
721
            })
56✔
722

56✔
723
            upQueries.push(insertQuery)
56✔
724
            downQueries.push(deleteQuery)
56✔
725
        }
56✔
726

10,394✔
727
        await this.executeQueries(upQueries, downQueries)
10,394✔
728
    }
10,394✔
729

28✔
730
    /**
28✔
731
     * Drops the table.
28✔
732
     * @param tableOrName
28✔
733
     * @param ifExists
28✔
734
     * @param dropForeignKeys
28✔
735
     * @param dropIndices
28✔
736
     */
28✔
737
    async dropTable(
28✔
738
        tableOrName: Table | string,
50✔
739
        ifExists?: boolean,
50✔
740
        dropForeignKeys: boolean = true,
50✔
741
        dropIndices: boolean = true,
50✔
742
    ): Promise<void> {
50✔
743
        if (ifExists) {
50✔
744
            const isTableExist = await this.hasTable(tableOrName)
30✔
745
            if (!isTableExist) return Promise.resolve()
30✔
746
        }
30✔
747

24✔
748
        // if dropTable called with dropForeignKeys = true, we must create foreign keys in down query.
24✔
749
        const createForeignKeys: boolean = dropForeignKeys
24✔
750
        const table = InstanceChecker.isTable(tableOrName)
24✔
751
            ? tableOrName
50✔
752
            : await this.getCachedTable(tableOrName)
50✔
753
        const upQueries: Query[] = []
16✔
754
        const downQueries: Query[] = []
16✔
755

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

16✔
759
        if (dropIndices) {
50✔
760
            table.indices.forEach((index) => {
24✔
761
                upQueries.push(this.dropIndexSql(table, index))
2✔
762
                downQueries.push(this.createIndexSql(table, index))
2✔
763
            })
24✔
764
        }
24✔
765

24✔
766
        // if dropForeignKeys is true, we just drop the table, otherwise we also drop table foreign keys.
24✔
767
        // createTable does not need separate method to create foreign keys, because it create fk's in the same query with table creation.
24✔
768
        if (dropForeignKeys)
24✔
769
            table.foreignKeys.forEach((foreignKey) =>
50✔
770
                upQueries.push(this.dropForeignKeySql(table, foreignKey)),
20✔
771
            )
20✔
772

24✔
773
        upQueries.push(this.dropTableSql(table))
24✔
774
        downQueries.push(this.createTableSql(table, createForeignKeys))
24✔
775

24✔
776
        // if table had columns with generated type, we must remove the expression from the metadata table
24✔
777
        const generatedColumns = table.columns.filter(
24✔
778
            (column) => column.generatedType && column.asExpression,
24✔
779
        )
24✔
780

24✔
781
        for (const column of generatedColumns) {
50✔
782
            const parsedTableName = this.driver.parseTableName(table)
8✔
783

8✔
784
            if (!parsedTableName.schema) {
8!
785
                parsedTableName.schema = await this.getCurrentSchema()
×
786
            }
×
787

8✔
788
            const deleteQuery = this.deleteTypeormMetadataSql({
8✔
789
                database: parsedTableName.database,
8✔
790
                schema: parsedTableName.schema,
8✔
791
                table: parsedTableName.tableName,
8✔
792
                type: MetadataTableType.GENERATED_COLUMN,
8✔
793
                name: column.name,
8✔
794
            })
8✔
795

8✔
796
            const insertQuery = this.insertTypeormMetadataSql({
8✔
797
                database: parsedTableName.database,
8✔
798
                schema: parsedTableName.schema,
8✔
799
                table: parsedTableName.tableName,
8✔
800
                type: MetadataTableType.GENERATED_COLUMN,
8✔
801
                name: column.name,
8✔
802
                value: column.asExpression,
8✔
803
            })
8✔
804

8✔
805
            upQueries.push(deleteQuery)
8✔
806
            downQueries.push(insertQuery)
8✔
807
        }
8✔
808

24✔
809
        await this.executeQueries(upQueries, downQueries)
24✔
810
    }
24✔
811

28✔
812
    /**
28✔
813
     * Creates a new view.
28✔
814
     * @param view
28✔
815
     * @param syncWithMetadata
28✔
816
     */
28✔
817
    async createView(
28✔
818
        view: View,
16✔
819
        syncWithMetadata: boolean = false,
16✔
820
    ): Promise<void> {
16✔
821
        const upQueries: Query[] = []
16✔
822
        const downQueries: Query[] = []
16✔
823
        upQueries.push(this.createViewSql(view))
16✔
824
        if (syncWithMetadata)
16✔
825
            upQueries.push(await this.insertViewDefinitionSql(view))
16✔
826
        downQueries.push(this.dropViewSql(view))
16✔
827
        if (syncWithMetadata)
16✔
828
            downQueries.push(await this.deleteViewDefinitionSql(view))
16✔
829
        await this.executeQueries(upQueries, downQueries)
16✔
830
    }
16✔
831

28✔
832
    /**
28✔
833
     * Drops the view.
28✔
834
     * @param target
28✔
835
     * @param ifExists
28✔
836
     */
28✔
837
    async dropView(target: View | string, ifExists?: boolean): Promise<void> {
28✔
838
        const viewName = InstanceChecker.isView(target) ? target.name : target
×
839
        let view: View
×
840
        try {
×
841
            view = await this.getCachedView(viewName)
×
842
        } catch {
×
843
            if (ifExists) return
×
844
            throw new TypeORMError(`View "${viewName}" does not exist.`)
×
845
        }
×
846

×
847
        await this.executeQueries(
×
848
            [
×
849
                await this.deleteViewDefinitionSql(view),
×
850
                this.dropViewSql(view, ifExists),
×
851
            ],
×
852
            [
×
853
                await this.insertViewDefinitionSql(view),
×
854
                this.createViewSql(view),
×
855
            ],
×
856
        )
×
857
    }
×
858

28✔
859
    /**
28✔
860
     * Renames a table.
28✔
861
     * @param oldTableOrName
28✔
862
     * @param newTableName
28✔
863
     */
28✔
864
    async renameTable(
28✔
865
        oldTableOrName: Table | string,
34✔
866
        newTableName: string,
34✔
867
    ): Promise<void> {
34✔
868
        const upQueries: Query[] = []
34✔
869
        const downQueries: Query[] = []
34✔
870
        const oldTable = InstanceChecker.isTable(oldTableOrName)
34✔
871
            ? oldTableOrName
34✔
872
            : await this.getCachedTable(oldTableOrName)
34✔
873
        const newTable = oldTable.clone()
30✔
874

30✔
875
        // we need database name and schema name to rename FK constraints
30✔
876
        let dbName: string | undefined = undefined
30✔
877
        let schemaName: string | undefined = undefined
30✔
878
        let oldTableName: string = oldTable.name
30✔
879
        const splittedName = oldTable.name.split(".")
30✔
880
        if (splittedName.length === 3) {
34✔
881
            dbName = splittedName[0]
4✔
882
            oldTableName = splittedName[2]
4✔
883
            if (splittedName[1] !== "") schemaName = splittedName[1]
4✔
884
        } else if (splittedName.length === 2) {
34!
885
            schemaName = splittedName[0]
×
886
            oldTableName = splittedName[1]
×
887
        }
×
888

34✔
889
        newTable.name = this.driver.buildTableName(
34✔
890
            newTableName,
34✔
891
            schemaName,
34✔
892
            dbName,
34✔
893
        )
34✔
894

34✔
895
        // if we have tables with database which differs from database specified in config, we must change currently used database.
34✔
896
        // This need because we can not rename objects from another database.
34✔
897
        const currentDB = await this.getCurrentDatabase()
34✔
898
        if (dbName && dbName !== currentDB) {
34✔
899
            upQueries.push(new Query(`USE "${dbName}"`))
4✔
900
            downQueries.push(new Query(`USE "${currentDB}"`))
4✔
901
        }
4✔
902

34✔
903
        // rename table
34✔
904
        upQueries.push(
34✔
905
            new Query(
34✔
906
                `EXEC sp_rename "${this.getTablePath(
34✔
907
                    oldTable,
34✔
908
                )}", "${newTableName}"`,
34✔
909
            ),
34✔
910
        )
34✔
911
        downQueries.push(
34✔
912
            new Query(
34✔
913
                `EXEC sp_rename "${this.getTablePath(
34✔
914
                    newTable,
34✔
915
                )}", "${oldTableName}"`,
34✔
916
            ),
34✔
917
        )
34✔
918

34✔
919
        // rename primary key constraint
34✔
920
        if (
34✔
921
            newTable.primaryColumns.length > 0 &&
34✔
922
            !newTable.primaryColumns[0].primaryKeyConstraintName
34✔
923
        ) {
34✔
924
            const columnNames = newTable.primaryColumns.map(
26✔
925
                (column) => column.name,
26✔
926
            )
26✔
927

26✔
928
            const oldPkName = this.connection.namingStrategy.primaryKeyName(
26✔
929
                oldTable,
26✔
930
                columnNames,
26✔
931
            )
26✔
932
            const newPkName = this.connection.namingStrategy.primaryKeyName(
26✔
933
                newTable,
26✔
934
                columnNames,
26✔
935
            )
26✔
936

26✔
937
            // rename primary constraint
26✔
938
            upQueries.push(
26✔
939
                new Query(
26✔
940
                    `EXEC sp_rename "${this.getTablePath(
26✔
941
                        newTable,
26✔
942
                    )}.${oldPkName}", "${newPkName}"`,
26✔
943
                ),
26✔
944
            )
26✔
945
            downQueries.push(
26✔
946
                new Query(
26✔
947
                    `EXEC sp_rename "${this.getTablePath(
26✔
948
                        newTable,
26✔
949
                    )}.${newPkName}", "${oldPkName}"`,
26✔
950
                ),
26✔
951
            )
26✔
952
        }
26✔
953

34✔
954
        // rename unique constraints
34✔
955
        newTable.uniques.forEach((unique) => {
34✔
956
            const oldUniqueName =
10✔
957
                this.connection.namingStrategy.uniqueConstraintName(
10✔
958
                    oldTable,
10✔
959
                    unique.columnNames,
10✔
960
                )
10✔
961

10✔
962
            // Skip renaming if Unique has user defined constraint name
10✔
963
            if (unique.name !== oldUniqueName) return
10✔
964

6✔
965
            // build new constraint name
6✔
966
            const newUniqueName =
6✔
967
                this.connection.namingStrategy.uniqueConstraintName(
6✔
968
                    newTable,
6✔
969
                    unique.columnNames,
6✔
970
                )
6✔
971

6✔
972
            // build queries
6✔
973
            upQueries.push(
6✔
974
                new Query(
6✔
975
                    `EXEC sp_rename "${this.getTablePath(newTable)}.${
6✔
976
                        unique.name
6✔
977
                    }", "${newUniqueName}"`,
6✔
978
                ),
6✔
979
            )
6✔
980
            downQueries.push(
6✔
981
                new Query(
6✔
982
                    `EXEC sp_rename "${this.getTablePath(
6✔
983
                        newTable,
6✔
984
                    )}.${newUniqueName}", "${unique.name}"`,
6✔
985
                ),
6✔
986
            )
6✔
987

6✔
988
            // replace constraint name
6✔
989
            unique.name = newUniqueName
6✔
990
        })
34✔
991

34✔
992
        // rename index constraints
34✔
993
        newTable.indices.forEach((index) => {
34✔
994
            const oldIndexName = this.connection.namingStrategy.indexName(
22✔
995
                oldTable,
22✔
996
                index.columnNames,
22✔
997
                index.where,
22✔
998
            )
22✔
999

22✔
1000
            // Skip renaming if Index has user defined constraint name
22✔
1001
            if (index.name !== oldIndexName) return
22✔
1002

10✔
1003
            // build new constraint name
10✔
1004
            const newIndexName = this.connection.namingStrategy.indexName(
10✔
1005
                newTable,
10✔
1006
                index.columnNames,
10✔
1007
                index.where,
10✔
1008
            )
10✔
1009

10✔
1010
            // build queries
10✔
1011
            upQueries.push(
10✔
1012
                new Query(
10✔
1013
                    `EXEC sp_rename "${this.getTablePath(newTable)}.${
10✔
1014
                        index.name
10✔
1015
                    }", "${newIndexName}", "INDEX"`,
10✔
1016
                ),
10✔
1017
            )
10✔
1018
            downQueries.push(
10✔
1019
                new Query(
10✔
1020
                    `EXEC sp_rename "${this.getTablePath(
10✔
1021
                        newTable,
10✔
1022
                    )}.${newIndexName}", "${index.name}", "INDEX"`,
10✔
1023
                ),
10✔
1024
            )
10✔
1025

10✔
1026
            // replace constraint name
10✔
1027
            index.name = newIndexName
10✔
1028
        })
34✔
1029

34✔
1030
        // rename foreign key constraints
34✔
1031
        newTable.foreignKeys.forEach((foreignKey) => {
34✔
1032
            const oldForeignKeyName =
18✔
1033
                this.connection.namingStrategy.foreignKeyName(
18✔
1034
                    oldTable,
18✔
1035
                    foreignKey.columnNames,
18✔
1036
                    this.getTablePath(foreignKey),
18✔
1037
                    foreignKey.referencedColumnNames,
18✔
1038
                )
18✔
1039

18✔
1040
            // Skip renaming if foreign key has user defined constraint name
18✔
1041
            if (foreignKey.name !== oldForeignKeyName) return
18✔
1042

2✔
1043
            // build new constraint name
2✔
1044
            const newForeignKeyName =
2✔
1045
                this.connection.namingStrategy.foreignKeyName(
2✔
1046
                    newTable,
2✔
1047
                    foreignKey.columnNames,
2✔
1048
                    this.getTablePath(foreignKey),
2✔
1049
                    foreignKey.referencedColumnNames,
2✔
1050
                )
2✔
1051

2✔
1052
            // build queries
2✔
1053
            upQueries.push(
2✔
1054
                new Query(
2✔
1055
                    `EXEC sp_rename "${this.buildForeignKeyName(
2✔
1056
                        foreignKey.name!,
2✔
1057
                        schemaName,
2✔
1058
                        dbName,
2✔
1059
                    )}", "${newForeignKeyName}"`,
2✔
1060
                ),
2✔
1061
            )
2✔
1062
            downQueries.push(
2✔
1063
                new Query(
2✔
1064
                    `EXEC sp_rename "${this.buildForeignKeyName(
2✔
1065
                        newForeignKeyName,
2✔
1066
                        schemaName,
2✔
1067
                        dbName,
2✔
1068
                    )}", "${foreignKey.name}"`,
2✔
1069
                ),
2✔
1070
            )
2✔
1071

2✔
1072
            // replace constraint name
2✔
1073
            foreignKey.name = newForeignKeyName
2✔
1074
        })
34✔
1075

34✔
1076
        // change currently used database back to default db.
34✔
1077
        if (dbName && dbName !== currentDB) {
34✔
1078
            upQueries.push(new Query(`USE "${currentDB}"`))
4✔
1079
            downQueries.push(new Query(`USE "${dbName}"`))
4✔
1080
        }
4✔
1081

34✔
1082
        await this.executeQueries(upQueries, downQueries)
34✔
1083

34✔
1084
        // rename old table and replace it in cached tabled;
34✔
1085
        oldTable.name = newTable.name
34✔
1086
        this.replaceCachedTable(oldTable, newTable)
34✔
1087
    }
34✔
1088

28✔
1089
    /**
28✔
1090
     * Creates a new column from the column in the table.
28✔
1091
     * @param tableOrName
28✔
1092
     * @param column
28✔
1093
     */
28✔
1094
    async addColumn(
28✔
1095
        tableOrName: Table | string,
74✔
1096
        column: TableColumn,
74✔
1097
    ): Promise<void> {
74✔
1098
        const table = InstanceChecker.isTable(tableOrName)
74✔
1099
            ? tableOrName
74✔
1100
            : await this.getCachedTable(tableOrName)
74✔
1101
        const clonedTable = table.clone()
4✔
1102
        const upQueries: Query[] = []
4✔
1103
        const downQueries: Query[] = []
4✔
1104

4✔
1105
        upQueries.push(
4✔
1106
            new Query(
4✔
1107
                `ALTER TABLE ${this.escapePath(
4✔
1108
                    table,
4✔
1109
                )} ADD ${this.buildCreateColumnSql(
4✔
1110
                    table,
4✔
1111
                    column,
4✔
1112
                    false,
4✔
1113
                    true,
4✔
1114
                )}`,
4✔
1115
            ),
4✔
1116
        )
4✔
1117
        downQueries.push(
4✔
1118
            new Query(
4✔
1119
                `ALTER TABLE ${this.escapePath(table)} DROP COLUMN "${
4✔
1120
                    column.name
4✔
1121
                }"`,
4✔
1122
            ),
4✔
1123
        )
4✔
1124

4✔
1125
        // create or update primary key constraint
4✔
1126
        if (column.isPrimary) {
74✔
1127
            const primaryColumns = clonedTable.primaryColumns
14✔
1128
            // if table already have primary key, me must drop it and recreate again
14✔
1129
            if (primaryColumns.length > 0) {
14✔
1130
                const pkName = primaryColumns[0].primaryKeyConstraintName
4✔
1131
                    ? primaryColumns[0].primaryKeyConstraintName
4!
1132
                    : this.connection.namingStrategy.primaryKeyName(
4✔
1133
                          clonedTable,
4✔
1134
                          primaryColumns.map((column) => column.name),
4✔
1135
                      )
4✔
1136

4✔
1137
                const columnNames = primaryColumns
4✔
1138
                    .map((column) => `"${column.name}"`)
4✔
1139
                    .join(", ")
4✔
1140

4✔
1141
                upQueries.push(
4✔
1142
                    new Query(
4✔
1143
                        `ALTER TABLE ${this.escapePath(
4✔
1144
                            table,
4✔
1145
                        )} DROP CONSTRAINT "${pkName}"`,
4✔
1146
                    ),
4✔
1147
                )
4✔
1148
                downQueries.push(
4✔
1149
                    new Query(
4✔
1150
                        `ALTER TABLE ${this.escapePath(
4✔
1151
                            table,
4✔
1152
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
4✔
1153
                    ),
4✔
1154
                )
4✔
1155
            }
4✔
1156

14✔
1157
            primaryColumns.push(column)
14✔
1158
            const pkName = primaryColumns[0].primaryKeyConstraintName
14✔
1159
                ? primaryColumns[0].primaryKeyConstraintName
14!
1160
                : this.connection.namingStrategy.primaryKeyName(
14✔
1161
                      clonedTable,
14✔
1162
                      primaryColumns.map((column) => column.name),
14✔
1163
                  )
14✔
1164

14✔
1165
            const columnNames = primaryColumns
14✔
1166
                .map((column) => `"${column.name}"`)
14✔
1167
                .join(", ")
14✔
1168
            upQueries.push(
14✔
1169
                new Query(
14✔
1170
                    `ALTER TABLE ${this.escapePath(
14✔
1171
                        table,
14✔
1172
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
14✔
1173
                ),
14✔
1174
            )
14✔
1175
            downQueries.push(
14✔
1176
                new Query(
14✔
1177
                    `ALTER TABLE ${this.escapePath(
14✔
1178
                        table,
14✔
1179
                    )} DROP CONSTRAINT "${pkName}"`,
14✔
1180
                ),
14✔
1181
            )
14✔
1182
        }
14✔
1183

74✔
1184
        // create column index
74✔
1185
        const columnIndex = clonedTable.indices.find(
74✔
1186
            (index) =>
74✔
1187
                index.columnNames.length === 1 &&
2✔
1188
                index.columnNames[0] === column.name,
74✔
1189
        )
74✔
1190
        if (columnIndex) {
74!
1191
            upQueries.push(this.createIndexSql(table, columnIndex))
×
1192
            downQueries.push(this.dropIndexSql(table, columnIndex))
×
1193
        }
×
1194

74✔
1195
        // create unique constraint
74✔
1196
        if (column.isUnique) {
74✔
1197
            const uniqueConstraint = new TableUnique({
6✔
1198
                name: this.connection.namingStrategy.uniqueConstraintName(
6✔
1199
                    table,
6✔
1200
                    [column.name],
6✔
1201
                ),
6✔
1202
                columnNames: [column.name],
6✔
1203
            })
6✔
1204
            clonedTable.uniques.push(uniqueConstraint)
6✔
1205
            upQueries.push(
6✔
1206
                new Query(
6✔
1207
                    `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
6✔
1208
                        uniqueConstraint.name
6✔
1209
                    }" UNIQUE ("${column.name}")`,
6✔
1210
                ),
6✔
1211
            )
6✔
1212
            downQueries.push(
6✔
1213
                new Query(
6✔
1214
                    `ALTER TABLE ${this.escapePath(table)} DROP CONSTRAINT "${
6✔
1215
                        uniqueConstraint.name
6✔
1216
                    }"`,
6✔
1217
                ),
6✔
1218
            )
6✔
1219
        }
6✔
1220

74✔
1221
        // remove default constraint
74✔
1222
        if (column.default !== null && column.default !== undefined) {
74✔
1223
            const defaultName =
10✔
1224
                this.connection.namingStrategy.defaultConstraintName(
10✔
1225
                    table,
10✔
1226
                    column.name,
10✔
1227
                )
10✔
1228
            downQueries.push(
10✔
1229
                new Query(
10✔
1230
                    `ALTER TABLE ${this.escapePath(
10✔
1231
                        table,
10✔
1232
                    )} DROP CONSTRAINT "${defaultName}"`,
10✔
1233
                ),
10✔
1234
            )
10✔
1235
        }
10✔
1236

74✔
1237
        if (column.generatedType && column.asExpression) {
74✔
1238
            const parsedTableName = this.driver.parseTableName(table)
6✔
1239

6✔
1240
            if (!parsedTableName.schema) {
6!
1241
                parsedTableName.schema = await this.getCurrentSchema()
×
1242
            }
×
1243

6✔
1244
            const insertQuery = this.insertTypeormMetadataSql({
6✔
1245
                database: parsedTableName.database,
6✔
1246
                schema: parsedTableName.schema,
6✔
1247
                table: parsedTableName.tableName,
6✔
1248
                type: MetadataTableType.GENERATED_COLUMN,
6✔
1249
                name: column.name,
6✔
1250
                value: column.asExpression,
6✔
1251
            })
6✔
1252

6✔
1253
            const deleteQuery = this.deleteTypeormMetadataSql({
6✔
1254
                database: parsedTableName.database,
6✔
1255
                schema: parsedTableName.schema,
6✔
1256
                table: parsedTableName.tableName,
6✔
1257
                type: MetadataTableType.GENERATED_COLUMN,
6✔
1258
                name: column.name,
6✔
1259
            })
6✔
1260

6✔
1261
            upQueries.push(insertQuery)
6✔
1262
            downQueries.push(deleteQuery)
6✔
1263
        }
6✔
1264

74✔
1265
        await this.executeQueries(upQueries, downQueries)
74✔
1266

70✔
1267
        clonedTable.addColumn(column)
70✔
1268
        this.replaceCachedTable(table, clonedTable)
70✔
1269
    }
70✔
1270

28✔
1271
    /**
28✔
1272
     * Creates a new columns from the column in the table.
28✔
1273
     * @param tableOrName
28✔
1274
     * @param columns
28✔
1275
     */
28✔
1276
    async addColumns(
28✔
1277
        tableOrName: Table | string,
12✔
1278
        columns: TableColumn[],
12✔
1279
    ): Promise<void> {
12✔
1280
        for (const column of columns) {
12✔
1281
            await this.addColumn(tableOrName, column)
14✔
1282
        }
12✔
1283
    }
10✔
1284

28✔
1285
    /**
28✔
1286
     * Renames column in the given table.
28✔
1287
     * @param tableOrName
28✔
1288
     * @param oldTableColumnOrName
28✔
1289
     * @param newTableColumnOrName
28✔
1290
     */
28✔
1291
    async renameColumn(
28✔
1292
        tableOrName: Table | string,
28✔
1293
        oldTableColumnOrName: TableColumn | string,
28✔
1294
        newTableColumnOrName: TableColumn | string,
28✔
1295
    ): Promise<void> {
28✔
1296
        const table = InstanceChecker.isTable(tableOrName)
28✔
1297
            ? tableOrName
28✔
1298
            : await this.getCachedTable(tableOrName)
28✔
1299
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
4✔
1300
            ? oldTableColumnOrName
28✔
1301
            : table.columns.find((c) => c.name === oldTableColumnOrName)
28✔
1302
        if (!oldColumn)
28✔
1303
            throw new TypeORMError(
28!
1304
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
×
1305
            )
×
1306

28✔
1307
        let newColumn: TableColumn
28✔
1308
        if (InstanceChecker.isTableColumn(newTableColumnOrName)) {
28✔
1309
            newColumn = newTableColumnOrName
18✔
1310
        } else {
28✔
1311
            newColumn = oldColumn.clone()
10✔
1312
            newColumn.name = newTableColumnOrName
10✔
1313
        }
10✔
1314

28✔
1315
        await this.changeColumn(table, oldColumn, newColumn)
28✔
1316
    }
28✔
1317

28✔
1318
    /**
28✔
1319
     * Changes a column in the table.
28✔
1320
     * @param tableOrName
28✔
1321
     * @param oldTableColumnOrName
28✔
1322
     * @param newColumn
28✔
1323
     */
28✔
1324
    async changeColumn(
28✔
1325
        tableOrName: Table | string,
104✔
1326
        oldTableColumnOrName: TableColumn | string,
104✔
1327
        newColumn: TableColumn,
104✔
1328
    ): Promise<void> {
104✔
1329
        const table = InstanceChecker.isTable(tableOrName)
104✔
1330
            ? tableOrName
104✔
1331
            : await this.getCachedTable(tableOrName)
104!
1332
        let clonedTable = table.clone()
×
1333
        const upQueries: Query[] = []
×
1334
        const downQueries: Query[] = []
×
1335

×
1336
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
×
1337
            ? oldTableColumnOrName
104✔
1338
            : table.columns.find(
104!
1339
                  (column) => column.name === oldTableColumnOrName,
×
1340
              )
104✔
1341
        if (!oldColumn)
104✔
1342
            throw new TypeORMError(
104!
1343
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
×
1344
            )
×
1345

104✔
1346
        if (
104✔
1347
            (newColumn.isGenerated !== oldColumn.isGenerated &&
104✔
1348
                newColumn.generationStrategy !== "uuid") ||
104✔
1349
            newColumn.type !== oldColumn.type ||
104✔
1350
            newColumn.length !== oldColumn.length ||
104✔
1351
            newColumn.asExpression !== oldColumn.asExpression ||
104✔
1352
            newColumn.generatedType !== oldColumn.generatedType
54✔
1353
        ) {
104✔
1354
            // SQL Server does not support changing of IDENTITY column, so we must drop column and recreate it again.
50✔
1355
            // Also, we recreate column if column type changed
50✔
1356
            await this.dropColumn(table, oldColumn)
50✔
1357
            await this.addColumn(table, newColumn)
50✔
1358

50✔
1359
            // update cloned table
50✔
1360
            clonedTable = table.clone()
50✔
1361
        } else {
104✔
1362
            if (newColumn.name !== oldColumn.name) {
54✔
1363
                // we need database name and schema name to rename FK constraints
28✔
1364
                let dbName: string | undefined = undefined
28✔
1365
                let schemaName: string | undefined = undefined
28✔
1366
                const splittedName = table.name.split(".")
28✔
1367
                if (splittedName.length === 3) {
28✔
1368
                    dbName = splittedName[0]
4✔
1369
                    if (splittedName[1] !== "") schemaName = splittedName[1]
4✔
1370
                } else if (splittedName.length === 2) {
28!
1371
                    schemaName = splittedName[0]
×
1372
                }
×
1373

28✔
1374
                // if we have tables with database which differs from database specified in config, we must change currently used database.
28✔
1375
                // This need because we can not rename objects from another database.
28✔
1376
                const currentDB = await this.getCurrentDatabase()
28✔
1377
                if (dbName && dbName !== currentDB) {
28✔
1378
                    upQueries.push(new Query(`USE "${dbName}"`))
4✔
1379
                    downQueries.push(new Query(`USE "${currentDB}"`))
4✔
1380
                }
4✔
1381

28✔
1382
                // rename the column
28✔
1383
                upQueries.push(
28✔
1384
                    new Query(
28✔
1385
                        `EXEC sp_rename "${this.getTablePath(table)}.${
28✔
1386
                            oldColumn.name
28✔
1387
                        }", "${newColumn.name}"`,
28✔
1388
                    ),
28✔
1389
                )
28✔
1390
                downQueries.push(
28✔
1391
                    new Query(
28✔
1392
                        `EXEC sp_rename "${this.getTablePath(table)}.${
28✔
1393
                            newColumn.name
28✔
1394
                        }", "${oldColumn.name}"`,
28✔
1395
                    ),
28✔
1396
                )
28✔
1397

28✔
1398
                // rename column primary key constraint
28✔
1399
                if (
28✔
1400
                    oldColumn.isPrimary === true &&
28✔
1401
                    !oldColumn.primaryKeyConstraintName
6✔
1402
                ) {
28✔
1403
                    const primaryColumns = clonedTable.primaryColumns
2✔
1404

2✔
1405
                    // build old primary constraint name
2✔
1406
                    const columnNames = primaryColumns.map(
2✔
1407
                        (column) => column.name,
2✔
1408
                    )
2✔
1409
                    const oldPkName =
2✔
1410
                        this.connection.namingStrategy.primaryKeyName(
2✔
1411
                            clonedTable,
2✔
1412
                            columnNames,
2✔
1413
                        )
2✔
1414

2✔
1415
                    // replace old column name with new column name
2✔
1416
                    columnNames.splice(columnNames.indexOf(oldColumn.name), 1)
2✔
1417
                    columnNames.push(newColumn.name)
2✔
1418

2✔
1419
                    // build new primary constraint name
2✔
1420
                    const newPkName =
2✔
1421
                        this.connection.namingStrategy.primaryKeyName(
2✔
1422
                            clonedTable,
2✔
1423
                            columnNames,
2✔
1424
                        )
2✔
1425

2✔
1426
                    // rename primary constraint
2✔
1427
                    upQueries.push(
2✔
1428
                        new Query(
2✔
1429
                            `EXEC sp_rename "${this.getTablePath(
2✔
1430
                                clonedTable,
2✔
1431
                            )}.${oldPkName}", "${newPkName}"`,
2✔
1432
                        ),
2✔
1433
                    )
2✔
1434
                    downQueries.push(
2✔
1435
                        new Query(
2✔
1436
                            `EXEC sp_rename "${this.getTablePath(
2✔
1437
                                clonedTable,
2✔
1438
                            )}.${newPkName}", "${oldPkName}"`,
2✔
1439
                        ),
2✔
1440
                    )
2✔
1441
                }
2✔
1442

28✔
1443
                // rename index constraints
28✔
1444
                clonedTable.findColumnIndices(oldColumn).forEach((index) => {
28✔
1445
                    const oldIndexName =
6✔
1446
                        this.connection.namingStrategy.indexName(
6✔
1447
                            clonedTable,
6✔
1448
                            index.columnNames,
6✔
1449
                            index.where,
6✔
1450
                        )
6✔
1451

6✔
1452
                    // Skip renaming if Index has user defined constraint name
6✔
1453
                    if (index.name !== oldIndexName) return
6✔
1454

2✔
1455
                    // build new constraint name
2✔
1456
                    index.columnNames.splice(
2✔
1457
                        index.columnNames.indexOf(oldColumn.name),
2✔
1458
                        1,
2✔
1459
                    )
2✔
1460
                    index.columnNames.push(newColumn.name)
2✔
1461
                    const newIndexName =
2✔
1462
                        this.connection.namingStrategy.indexName(
2✔
1463
                            clonedTable,
2✔
1464
                            index.columnNames,
2✔
1465
                            index.where,
2✔
1466
                        )
2✔
1467

2✔
1468
                    // build queries
2✔
1469
                    upQueries.push(
2✔
1470
                        new Query(
2✔
1471
                            `EXEC sp_rename "${this.getTablePath(
2✔
1472
                                clonedTable,
2✔
1473
                            )}.${index.name}", "${newIndexName}", "INDEX"`,
2✔
1474
                        ),
2✔
1475
                    )
2✔
1476
                    downQueries.push(
2✔
1477
                        new Query(
2✔
1478
                            `EXEC sp_rename "${this.getTablePath(
2✔
1479
                                clonedTable,
2✔
1480
                            )}.${newIndexName}", "${index.name}", "INDEX"`,
2✔
1481
                        ),
2✔
1482
                    )
2✔
1483

2✔
1484
                    // replace constraint name
2✔
1485
                    index.name = newIndexName
2✔
1486
                })
28✔
1487

28✔
1488
                // rename foreign key constraints
28✔
1489
                clonedTable
28✔
1490
                    .findColumnForeignKeys(oldColumn)
28✔
1491
                    .forEach((foreignKey) => {
28✔
1492
                        const foreignKeyName =
2✔
1493
                            this.connection.namingStrategy.foreignKeyName(
2✔
1494
                                clonedTable,
2✔
1495
                                foreignKey.columnNames,
2✔
1496
                                this.getTablePath(foreignKey),
2✔
1497
                                foreignKey.referencedColumnNames,
2✔
1498
                            )
2✔
1499

2✔
1500
                        // Skip renaming if foreign key has user defined constraint name
2✔
1501
                        if (foreignKey.name !== foreignKeyName) return
2!
1502

2✔
1503
                        // build new constraint name
2✔
1504
                        foreignKey.columnNames.splice(
2✔
1505
                            foreignKey.columnNames.indexOf(oldColumn.name),
2✔
1506
                            1,
2✔
1507
                        )
2✔
1508
                        foreignKey.columnNames.push(newColumn.name)
2✔
1509
                        const newForeignKeyName =
2✔
1510
                            this.connection.namingStrategy.foreignKeyName(
2✔
1511
                                clonedTable,
2✔
1512
                                foreignKey.columnNames,
2✔
1513
                                this.getTablePath(foreignKey),
2✔
1514
                                foreignKey.referencedColumnNames,
2✔
1515
                            )
2✔
1516

2✔
1517
                        // build queries
2✔
1518
                        upQueries.push(
2✔
1519
                            new Query(
2✔
1520
                                `EXEC sp_rename "${this.buildForeignKeyName(
2✔
1521
                                    foreignKey.name!,
2✔
1522
                                    schemaName,
2✔
1523
                                    dbName,
2✔
1524
                                )}", "${newForeignKeyName}"`,
2✔
1525
                            ),
2✔
1526
                        )
2✔
1527
                        downQueries.push(
2✔
1528
                            new Query(
2✔
1529
                                `EXEC sp_rename "${this.buildForeignKeyName(
2✔
1530
                                    newForeignKeyName,
2✔
1531
                                    schemaName,
2✔
1532
                                    dbName,
2✔
1533
                                )}", "${foreignKey.name}"`,
2✔
1534
                            ),
2✔
1535
                        )
2✔
1536

2✔
1537
                        // replace constraint name
2✔
1538
                        foreignKey.name = newForeignKeyName
2✔
1539
                    })
28✔
1540

28✔
1541
                // rename check constraints
28✔
1542
                clonedTable.findColumnChecks(oldColumn).forEach((check) => {
28✔
1543
                    // build new constraint name
×
1544
                    check.columnNames!.splice(
×
1545
                        check.columnNames!.indexOf(oldColumn.name),
×
1546
                        1,
×
1547
                    )
×
1548
                    check.columnNames!.push(newColumn.name)
×
1549
                    const newCheckName =
×
1550
                        this.connection.namingStrategy.checkConstraintName(
×
1551
                            clonedTable,
×
1552
                            check.expression!,
×
1553
                        )
×
1554

×
1555
                    // build queries
×
1556
                    upQueries.push(
×
1557
                        new Query(
×
1558
                            `EXEC sp_rename "${this.getTablePath(
×
1559
                                clonedTable,
×
1560
                            )}.${check.name}", "${newCheckName}"`,
×
1561
                        ),
×
1562
                    )
×
1563
                    downQueries.push(
×
1564
                        new Query(
×
1565
                            `EXEC sp_rename "${this.getTablePath(
×
1566
                                clonedTable,
×
1567
                            )}.${newCheckName}", "${check.name}"`,
×
1568
                        ),
×
1569
                    )
×
1570

×
1571
                    // replace constraint name
×
1572
                    check.name = newCheckName
×
1573
                })
28✔
1574

28✔
1575
                // rename unique constraints
28✔
1576
                clonedTable.findColumnUniques(oldColumn).forEach((unique) => {
28✔
1577
                    const oldUniqueName =
12✔
1578
                        this.connection.namingStrategy.uniqueConstraintName(
12✔
1579
                            clonedTable,
12✔
1580
                            unique.columnNames,
12✔
1581
                        )
12✔
1582

12✔
1583
                    // Skip renaming if Unique has user defined constraint name
12✔
1584
                    if (unique.name !== oldUniqueName) return
12✔
1585

8✔
1586
                    // build new constraint name
8✔
1587
                    unique.columnNames.splice(
8✔
1588
                        unique.columnNames.indexOf(oldColumn.name),
8✔
1589
                        1,
8✔
1590
                    )
8✔
1591
                    unique.columnNames.push(newColumn.name)
8✔
1592
                    const newUniqueName =
8✔
1593
                        this.connection.namingStrategy.uniqueConstraintName(
8✔
1594
                            clonedTable,
8✔
1595
                            unique.columnNames,
8✔
1596
                        )
8✔
1597

8✔
1598
                    // build queries
8✔
1599
                    upQueries.push(
8✔
1600
                        new Query(
8✔
1601
                            `EXEC sp_rename "${this.getTablePath(
8✔
1602
                                clonedTable,
8✔
1603
                            )}.${unique.name}", "${newUniqueName}"`,
8✔
1604
                        ),
8✔
1605
                    )
8✔
1606
                    downQueries.push(
8✔
1607
                        new Query(
8✔
1608
                            `EXEC sp_rename "${this.getTablePath(
8✔
1609
                                clonedTable,
8✔
1610
                            )}.${newUniqueName}", "${unique.name}"`,
8✔
1611
                        ),
8✔
1612
                    )
8✔
1613

8✔
1614
                    // replace constraint name
8✔
1615
                    unique.name = newUniqueName
8✔
1616
                })
28✔
1617

28✔
1618
                // rename default constraints
28✔
1619
                if (
28✔
1620
                    oldColumn.default !== null &&
28✔
1621
                    oldColumn.default !== undefined
28✔
1622
                ) {
28✔
1623
                    const oldDefaultName =
4✔
1624
                        this.connection.namingStrategy.defaultConstraintName(
4✔
1625
                            table,
4✔
1626
                            oldColumn.name,
4✔
1627
                        )
4✔
1628
                    const newDefaultName =
4✔
1629
                        this.connection.namingStrategy.defaultConstraintName(
4✔
1630
                            table,
4✔
1631
                            newColumn.name,
4✔
1632
                        )
4✔
1633

4✔
1634
                    upQueries.push(
4✔
1635
                        new Query(
4✔
1636
                            `ALTER TABLE ${this.escapePath(
4✔
1637
                                table,
4✔
1638
                            )} DROP CONSTRAINT "${oldDefaultName}"`,
4✔
1639
                        ),
4✔
1640
                    )
4✔
1641
                    downQueries.push(
4✔
1642
                        new Query(
4✔
1643
                            `ALTER TABLE ${this.escapePath(
4✔
1644
                                table,
4✔
1645
                            )} ADD CONSTRAINT "${oldDefaultName}" DEFAULT ${
4✔
1646
                                oldColumn.default
4✔
1647
                            } FOR "${newColumn.name}"`,
4✔
1648
                        ),
4✔
1649
                    )
4✔
1650

4✔
1651
                    upQueries.push(
4✔
1652
                        new Query(
4✔
1653
                            `ALTER TABLE ${this.escapePath(
4✔
1654
                                table,
4✔
1655
                            )} ADD CONSTRAINT "${newDefaultName}" DEFAULT ${
4✔
1656
                                oldColumn.default
4✔
1657
                            } FOR "${newColumn.name}"`,
4✔
1658
                        ),
4✔
1659
                    )
4✔
1660
                    downQueries.push(
4✔
1661
                        new Query(
4✔
1662
                            `ALTER TABLE ${this.escapePath(
4✔
1663
                                table,
4✔
1664
                            )} DROP CONSTRAINT "${newDefaultName}"`,
4✔
1665
                        ),
4✔
1666
                    )
4✔
1667
                }
4✔
1668

28✔
1669
                // change currently used database back to default db.
28✔
1670
                if (dbName && dbName !== currentDB) {
28✔
1671
                    upQueries.push(new Query(`USE "${currentDB}"`))
4✔
1672
                    downQueries.push(new Query(`USE "${dbName}"`))
4✔
1673
                }
4✔
1674

28✔
1675
                // rename old column in the Table object
28✔
1676
                const oldTableColumn = clonedTable.columns.find(
28✔
1677
                    (column) => column.name === oldColumn.name,
28✔
1678
                )
28✔
1679
                clonedTable.columns[
28✔
1680
                    clonedTable.columns.indexOf(oldTableColumn!)
28✔
1681
                ].name = newColumn.name
28✔
1682
                oldColumn.name = newColumn.name
28✔
1683
            }
28✔
1684

54✔
1685
            if (
54✔
1686
                this.isColumnChanged(oldColumn, newColumn, false, false, false)
54✔
1687
            ) {
54!
1688
                upQueries.push(
×
1689
                    new Query(
×
1690
                        `ALTER TABLE ${this.escapePath(
×
1691
                            table,
×
1692
                        )} ALTER COLUMN ${this.buildCreateColumnSql(
×
1693
                            table,
×
1694
                            newColumn,
×
1695
                            true,
×
1696
                            false,
×
1697
                            true,
×
1698
                        )}`,
×
1699
                    ),
×
1700
                )
×
1701
                downQueries.push(
×
1702
                    new Query(
×
1703
                        `ALTER TABLE ${this.escapePath(
×
1704
                            table,
×
1705
                        )} ALTER COLUMN ${this.buildCreateColumnSql(
×
1706
                            table,
×
1707
                            oldColumn,
×
1708
                            true,
×
1709
                            false,
×
1710
                            true,
×
1711
                        )}`,
×
1712
                    ),
×
1713
                )
×
1714
            }
×
1715

54✔
1716
            if (this.isEnumChanged(oldColumn, newColumn)) {
54✔
1717
                const oldExpression = this.getEnumExpression(oldColumn)
2✔
1718
                const oldCheck = new TableCheck({
2✔
1719
                    name: this.connection.namingStrategy.checkConstraintName(
2✔
1720
                        table,
2✔
1721
                        oldExpression,
2✔
1722
                        true,
2✔
1723
                    ),
2✔
1724
                    expression: oldExpression,
2✔
1725
                })
2✔
1726

2✔
1727
                const newExpression = this.getEnumExpression(newColumn)
2✔
1728
                const newCheck = new TableCheck({
2✔
1729
                    name: this.connection.namingStrategy.checkConstraintName(
2✔
1730
                        table,
2✔
1731
                        newExpression,
2✔
1732
                        true,
2✔
1733
                    ),
2✔
1734
                    expression: newExpression,
2✔
1735
                })
2✔
1736

2✔
1737
                upQueries.push(this.dropCheckConstraintSql(table, oldCheck))
2✔
1738
                upQueries.push(this.createCheckConstraintSql(table, newCheck))
2✔
1739

2✔
1740
                downQueries.push(this.dropCheckConstraintSql(table, newCheck))
2✔
1741
                downQueries.push(this.createCheckConstraintSql(table, oldCheck))
2✔
1742
            }
2✔
1743

54✔
1744
            if (newColumn.isPrimary !== oldColumn.isPrimary) {
54✔
1745
                const primaryColumns = clonedTable.primaryColumns
8✔
1746

8✔
1747
                // if primary column state changed, we must always drop existed constraint.
8✔
1748
                if (primaryColumns.length > 0) {
8✔
1749
                    const pkName = primaryColumns[0].primaryKeyConstraintName
6✔
1750
                        ? primaryColumns[0].primaryKeyConstraintName
6!
1751
                        : this.connection.namingStrategy.primaryKeyName(
6✔
1752
                              clonedTable,
6✔
1753
                              primaryColumns.map((column) => column.name),
6✔
1754
                          )
6✔
1755

6✔
1756
                    const columnNames = primaryColumns
6✔
1757
                        .map((column) => `"${column.name}"`)
6✔
1758
                        .join(", ")
6✔
1759
                    upQueries.push(
6✔
1760
                        new Query(
6✔
1761
                            `ALTER TABLE ${this.escapePath(
6✔
1762
                                table,
6✔
1763
                            )} DROP CONSTRAINT "${pkName}"`,
6✔
1764
                        ),
6✔
1765
                    )
6✔
1766
                    downQueries.push(
6✔
1767
                        new Query(
6✔
1768
                            `ALTER TABLE ${this.escapePath(
6✔
1769
                                table,
6✔
1770
                            )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
6✔
1771
                        ),
6✔
1772
                    )
6✔
1773
                }
6✔
1774

8✔
1775
                if (newColumn.isPrimary === true) {
8✔
1776
                    primaryColumns.push(newColumn)
4✔
1777
                    // update column in table
4✔
1778
                    const column = clonedTable.columns.find(
4✔
1779
                        (column) => column.name === newColumn.name,
4✔
1780
                    )
4✔
1781
                    column!.isPrimary = true
4✔
1782
                    const pkName = primaryColumns[0].primaryKeyConstraintName
4✔
1783
                        ? primaryColumns[0].primaryKeyConstraintName
4!
1784
                        : this.connection.namingStrategy.primaryKeyName(
4✔
1785
                              clonedTable,
4✔
1786
                              primaryColumns.map((column) => column.name),
4✔
1787
                          )
4✔
1788

4✔
1789
                    const columnNames = primaryColumns
4✔
1790
                        .map((column) => `"${column.name}"`)
4✔
1791
                        .join(", ")
4✔
1792
                    upQueries.push(
4✔
1793
                        new Query(
4✔
1794
                            `ALTER TABLE ${this.escapePath(
4✔
1795
                                table,
4✔
1796
                            )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
4✔
1797
                        ),
4✔
1798
                    )
4✔
1799
                    downQueries.push(
4✔
1800
                        new Query(
4✔
1801
                            `ALTER TABLE ${this.escapePath(
4✔
1802
                                table,
4✔
1803
                            )} DROP CONSTRAINT "${pkName}"`,
4✔
1804
                        ),
4✔
1805
                    )
4✔
1806
                } else {
4✔
1807
                    const primaryColumn = primaryColumns.find(
4✔
1808
                        (c) => c.name === newColumn.name,
4✔
1809
                    )
4✔
1810
                    primaryColumns.splice(
4✔
1811
                        primaryColumns.indexOf(primaryColumn!),
4✔
1812
                        1,
4✔
1813
                    )
4✔
1814

4✔
1815
                    // update column in table
4✔
1816
                    const column = clonedTable.columns.find(
4✔
1817
                        (column) => column.name === newColumn.name,
4✔
1818
                    )
4✔
1819
                    column!.isPrimary = false
4✔
1820

4✔
1821
                    // if we have another primary keys, we must recreate constraint.
4✔
1822
                    if (primaryColumns.length > 0) {
4✔
1823
                        const pkName = primaryColumns[0]
2✔
1824
                            .primaryKeyConstraintName
2✔
1825
                            ? primaryColumns[0].primaryKeyConstraintName
2!
1826
                            : this.connection.namingStrategy.primaryKeyName(
2✔
1827
                                  clonedTable,
2✔
1828
                                  primaryColumns.map((column) => column.name),
2✔
1829
                              )
2✔
1830

2✔
1831
                        const columnNames = primaryColumns
2✔
1832
                            .map((column) => `"${column.name}"`)
2✔
1833
                            .join(", ")
2✔
1834
                        upQueries.push(
2✔
1835
                            new Query(
2✔
1836
                                `ALTER TABLE ${this.escapePath(
2✔
1837
                                    table,
2✔
1838
                                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
2✔
1839
                            ),
2✔
1840
                        )
2✔
1841
                        downQueries.push(
2✔
1842
                            new Query(
2✔
1843
                                `ALTER TABLE ${this.escapePath(
2✔
1844
                                    table,
2✔
1845
                                )} DROP CONSTRAINT "${pkName}"`,
2✔
1846
                            ),
2✔
1847
                        )
2✔
1848
                    }
2✔
1849
                }
4✔
1850
            }
8✔
1851

54✔
1852
            if (newColumn.isUnique !== oldColumn.isUnique) {
54✔
1853
                if (newColumn.isUnique === true) {
2✔
1854
                    const uniqueConstraint = new TableUnique({
2✔
1855
                        name: this.connection.namingStrategy.uniqueConstraintName(
2✔
1856
                            table,
2✔
1857
                            [newColumn.name],
2✔
1858
                        ),
2✔
1859
                        columnNames: [newColumn.name],
2✔
1860
                    })
2✔
1861
                    clonedTable.uniques.push(uniqueConstraint)
2✔
1862
                    upQueries.push(
2✔
1863
                        new Query(
2✔
1864
                            `ALTER TABLE ${this.escapePath(
2✔
1865
                                table,
2✔
1866
                            )} ADD CONSTRAINT "${
2✔
1867
                                uniqueConstraint.name
2✔
1868
                            }" UNIQUE ("${newColumn.name}")`,
2✔
1869
                        ),
2✔
1870
                    )
2✔
1871
                    downQueries.push(
2✔
1872
                        new Query(
2✔
1873
                            `ALTER TABLE ${this.escapePath(
2✔
1874
                                table,
2✔
1875
                            )} DROP CONSTRAINT "${uniqueConstraint.name}"`,
2✔
1876
                        ),
2✔
1877
                    )
2✔
1878
                } else {
2!
1879
                    const uniqueConstraint = clonedTable.uniques.find(
×
1880
                        (unique) => {
×
1881
                            return (
×
1882
                                unique.columnNames.length === 1 &&
×
1883
                                !!unique.columnNames.find(
×
1884
                                    (columnName) =>
×
1885
                                        columnName === newColumn.name,
×
1886
                                )
×
1887
                            )
×
1888
                        },
×
1889
                    )
×
1890
                    clonedTable.uniques.splice(
×
1891
                        clonedTable.uniques.indexOf(uniqueConstraint!),
×
1892
                        1,
×
1893
                    )
×
1894
                    upQueries.push(
×
1895
                        new Query(
×
1896
                            `ALTER TABLE ${this.escapePath(
×
1897
                                table,
×
1898
                            )} DROP CONSTRAINT "${uniqueConstraint!.name}"`,
×
1899
                        ),
×
1900
                    )
×
1901
                    downQueries.push(
×
1902
                        new Query(
×
1903
                            `ALTER TABLE ${this.escapePath(
×
1904
                                table,
×
1905
                            )} ADD CONSTRAINT "${
×
1906
                                uniqueConstraint!.name
×
1907
                            }" UNIQUE ("${newColumn.name}")`,
×
1908
                        ),
×
1909
                    )
×
1910
                }
×
1911
            }
2✔
1912

54✔
1913
            if (newColumn.default !== oldColumn.default) {
54✔
1914
                // (note) if there is a previous default, we need to drop its constraint first
4✔
1915
                if (
4✔
1916
                    oldColumn.default !== null &&
4✔
1917
                    oldColumn.default !== undefined
4✔
1918
                ) {
4✔
1919
                    const defaultName =
2✔
1920
                        this.connection.namingStrategy.defaultConstraintName(
2✔
1921
                            table,
2✔
1922
                            oldColumn.name,
2✔
1923
                        )
2✔
1924
                    upQueries.push(
2✔
1925
                        new Query(
2✔
1926
                            `ALTER TABLE ${this.escapePath(
2✔
1927
                                table,
2✔
1928
                            )} DROP CONSTRAINT "${defaultName}"`,
2✔
1929
                        ),
2✔
1930
                    )
2✔
1931
                    downQueries.push(
2✔
1932
                        new Query(
2✔
1933
                            `ALTER TABLE ${this.escapePath(
2✔
1934
                                table,
2✔
1935
                            )} ADD CONSTRAINT "${defaultName}" DEFAULT ${
2✔
1936
                                oldColumn.default
2✔
1937
                            } FOR "${oldColumn.name}"`,
2✔
1938
                        ),
2✔
1939
                    )
2✔
1940
                }
2✔
1941

4✔
1942
                if (
4✔
1943
                    newColumn.default !== null &&
4✔
1944
                    newColumn.default !== undefined
4✔
1945
                ) {
4✔
1946
                    const defaultName =
4✔
1947
                        this.connection.namingStrategy.defaultConstraintName(
4✔
1948
                            table,
4✔
1949
                            newColumn.name,
4✔
1950
                        )
4✔
1951
                    upQueries.push(
4✔
1952
                        new Query(
4✔
1953
                            `ALTER TABLE ${this.escapePath(
4✔
1954
                                table,
4✔
1955
                            )} ADD CONSTRAINT "${defaultName}" DEFAULT ${
4✔
1956
                                newColumn.default
4✔
1957
                            } FOR "${newColumn.name}"`,
4✔
1958
                        ),
4✔
1959
                    )
4✔
1960
                    downQueries.push(
4✔
1961
                        new Query(
4✔
1962
                            `ALTER TABLE ${this.escapePath(
4✔
1963
                                table,
4✔
1964
                            )} DROP CONSTRAINT "${defaultName}"`,
4✔
1965
                        ),
4✔
1966
                    )
4✔
1967
                }
4✔
1968
            }
4✔
1969

54✔
1970
            await this.executeQueries(upQueries, downQueries)
54✔
1971
            this.replaceCachedTable(table, clonedTable)
54✔
1972
        }
54✔
1973
    }
104✔
1974

28✔
1975
    /**
28✔
1976
     * Changes a column in the table.
28✔
1977
     * @param tableOrName
28✔
1978
     * @param changedColumns
28✔
1979
     */
28✔
1980
    async changeColumns(
28✔
1981
        tableOrName: Table | string,
46✔
1982
        changedColumns: { newColumn: TableColumn; oldColumn: TableColumn }[],
46✔
1983
    ): Promise<void> {
46✔
1984
        for (const { oldColumn, newColumn } of changedColumns) {
46✔
1985
            await this.changeColumn(tableOrName, oldColumn, newColumn)
64✔
1986
        }
64✔
1987
    }
46✔
1988

28✔
1989
    /**
28✔
1990
     * Drops column in the table.
28✔
1991
     * @param tableOrName
28✔
1992
     * @param columnOrName
28✔
1993
     * @param ifExists
28✔
1994
     */
28✔
1995
    async dropColumn(
28✔
1996
        tableOrName: Table | string,
88✔
1997
        columnOrName: TableColumn | string,
88✔
1998
        ifExists?: boolean,
88✔
1999
    ): Promise<void> {
88✔
2000
        const table = InstanceChecker.isTable(tableOrName)
88✔
2001
            ? tableOrName
88✔
2002
            : await this.getCachedTable(tableOrName)
88✔
2003
        const column = InstanceChecker.isTableColumn(columnOrName)
8✔
2004
            ? columnOrName
88✔
2005
            : table.findColumnByName(columnOrName)
88✔
2006
        if (!column) {
88✔
2007
            if (ifExists) return
4✔
2008
            throw new TypeORMError(
2✔
2009
                `Column "${columnOrName}" was not found in table "${table.name}"`,
2✔
2010
            )
2✔
2011
        }
2✔
2012

84✔
2013
        const clonedTable = table.clone()
84✔
2014
        const upQueries: Query[] = []
84✔
2015
        const downQueries: Query[] = []
84✔
2016

84✔
2017
        // drop primary key constraint
84✔
2018
        if (column.isPrimary) {
88✔
2019
            const pkName = column.primaryKeyConstraintName
14✔
2020
                ? column.primaryKeyConstraintName
14!
2021
                : this.connection.namingStrategy.primaryKeyName(
14✔
2022
                      clonedTable,
14✔
2023
                      clonedTable.primaryColumns.map((column) => column.name),
14✔
2024
                  )
14✔
2025

14✔
2026
            const columnNames = clonedTable.primaryColumns
14✔
2027
                .map((primaryColumn) => `"${primaryColumn.name}"`)
14✔
2028
                .join(", ")
14✔
2029

14✔
2030
            upQueries.push(
14✔
2031
                new Query(
14✔
2032
                    `ALTER TABLE ${this.escapePath(
14✔
2033
                        clonedTable,
14✔
2034
                    )} DROP CONSTRAINT "${pkName}"`,
14✔
2035
                ),
14✔
2036
            )
14✔
2037
            downQueries.push(
14✔
2038
                new Query(
14✔
2039
                    `ALTER TABLE ${this.escapePath(
14✔
2040
                        clonedTable,
14✔
2041
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
14✔
2042
                ),
14✔
2043
            )
14✔
2044

14✔
2045
            // update column in table
14✔
2046
            const tableColumn = clonedTable.findColumnByName(column.name)
14✔
2047
            tableColumn!.isPrimary = false
14✔
2048

14✔
2049
            // if primary key have multiple columns, we must recreate it without dropped column
14✔
2050
            if (clonedTable.primaryColumns.length > 0) {
14✔
2051
                const pkName = clonedTable.primaryColumns[0]
2✔
2052
                    .primaryKeyConstraintName
2✔
2053
                    ? clonedTable.primaryColumns[0].primaryKeyConstraintName
2!
2054
                    : this.connection.namingStrategy.primaryKeyName(
2✔
2055
                          clonedTable,
2✔
2056
                          clonedTable.primaryColumns.map(
2✔
2057
                              (column) => column.name,
2✔
2058
                          ),
2✔
2059
                      )
2✔
2060

2✔
2061
                const columnNames = clonedTable.primaryColumns
2✔
2062
                    .map((primaryColumn) => `"${primaryColumn.name}"`)
2✔
2063
                    .join(", ")
2✔
2064
                upQueries.push(
2✔
2065
                    new Query(
2✔
2066
                        `ALTER TABLE ${this.escapePath(
2✔
2067
                            clonedTable,
2✔
2068
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
2✔
2069
                    ),
2✔
2070
                )
2✔
2071
                downQueries.push(
2✔
2072
                    new Query(
2✔
2073
                        `ALTER TABLE ${this.escapePath(
2✔
2074
                            clonedTable,
2✔
2075
                        )} DROP CONSTRAINT "${pkName}"`,
2✔
2076
                    ),
2✔
2077
                )
2✔
2078
            }
2✔
2079
        }
14✔
2080

84✔
2081
        // drop column index
84✔
2082
        const columnIndex = clonedTable.indices.find(
84✔
2083
            (index) =>
84✔
2084
                index.columnNames.length === 1 &&
2✔
2085
                index.columnNames[0] === column.name,
84✔
2086
        )
84✔
2087
        if (columnIndex) {
88!
2088
            clonedTable.indices.splice(
×
2089
                clonedTable.indices.indexOf(columnIndex),
×
2090
                1,
×
2091
            )
×
2092
            upQueries.push(this.dropIndexSql(table, columnIndex))
×
2093
            downQueries.push(this.createIndexSql(table, columnIndex))
×
2094
        }
×
2095

84✔
2096
        // drop column check
84✔
2097
        const columnCheck = clonedTable.checks.find(
84✔
2098
            (check) =>
84✔
2099
                !!check.columnNames &&
32✔
2100
                check.columnNames.length === 1 &&
32✔
2101
                check.columnNames[0] === column.name,
84✔
2102
        )
84✔
2103
        if (columnCheck) {
88✔
2104
            clonedTable.checks.splice(
4✔
2105
                clonedTable.checks.indexOf(columnCheck),
4✔
2106
                1,
4✔
2107
            )
4✔
2108
            upQueries.push(this.dropCheckConstraintSql(table, columnCheck))
4✔
2109
            downQueries.push(this.createCheckConstraintSql(table, columnCheck))
4✔
2110
        }
4✔
2111

84✔
2112
        // drop column unique
84✔
2113
        const columnUnique = clonedTable.uniques.find(
84✔
2114
            (unique) =>
84✔
2115
                unique.columnNames.length === 1 &&
60✔
2116
                unique.columnNames[0] === column.name,
84✔
2117
        )
84✔
2118
        if (columnUnique) {
88✔
2119
            clonedTable.uniques.splice(
6✔
2120
                clonedTable.uniques.indexOf(columnUnique),
6✔
2121
                1,
6✔
2122
            )
6✔
2123
            upQueries.push(this.dropUniqueConstraintSql(table, columnUnique))
6✔
2124
            downQueries.push(
6✔
2125
                this.createUniqueConstraintSql(table, columnUnique),
6✔
2126
            )
6✔
2127
        }
6✔
2128

84✔
2129
        // drop default constraint
84✔
2130
        if (column.default !== null && column.default !== undefined) {
88✔
2131
            const defaultName =
12✔
2132
                this.connection.namingStrategy.defaultConstraintName(
12✔
2133
                    table,
12✔
2134
                    column.name,
12✔
2135
                )
12✔
2136
            upQueries.push(
12✔
2137
                new Query(
12✔
2138
                    `ALTER TABLE ${this.escapePath(
12✔
2139
                        table,
12✔
2140
                    )} DROP CONSTRAINT "${defaultName}"`,
12✔
2141
                ),
12✔
2142
            )
12✔
2143
            downQueries.push(
12✔
2144
                new Query(
12✔
2145
                    `ALTER TABLE ${this.escapePath(
12✔
2146
                        table,
12✔
2147
                    )} ADD CONSTRAINT "${defaultName}" DEFAULT ${
12✔
2148
                        column.default
12✔
2149
                    } FOR "${column.name}"`,
12✔
2150
                ),
12✔
2151
            )
12✔
2152
        }
12✔
2153

84✔
2154
        if (column.generatedType && column.asExpression) {
88✔
2155
            const parsedTableName = this.driver.parseTableName(table)
8✔
2156

8✔
2157
            if (!parsedTableName.schema) {
8!
2158
                parsedTableName.schema = await this.getCurrentSchema()
×
2159
            }
×
2160

8✔
2161
            const deleteQuery = this.deleteTypeormMetadataSql({
8✔
2162
                database: parsedTableName.database,
8✔
2163
                schema: parsedTableName.schema,
8✔
2164
                table: parsedTableName.tableName,
8✔
2165
                type: MetadataTableType.GENERATED_COLUMN,
8✔
2166
                name: column.name,
8✔
2167
            })
8✔
2168
            const insertQuery = this.insertTypeormMetadataSql({
8✔
2169
                database: parsedTableName.database,
8✔
2170
                schema: parsedTableName.schema,
8✔
2171
                table: parsedTableName.tableName,
8✔
2172
                type: MetadataTableType.GENERATED_COLUMN,
8✔
2173
                name: column.name,
8✔
2174
                value: column.asExpression,
8✔
2175
            })
8✔
2176

8✔
2177
            upQueries.push(deleteQuery)
8✔
2178
            downQueries.push(insertQuery)
8✔
2179
        }
8✔
2180

84✔
2181
        upQueries.push(
84✔
2182
            new Query(
84✔
2183
                `ALTER TABLE ${this.escapePath(table)} DROP COLUMN "${
84✔
2184
                    column.name
84✔
2185
                }"`,
84✔
2186
            ),
84✔
2187
        )
84✔
2188
        downQueries.push(
84✔
2189
            new Query(
84✔
2190
                `ALTER TABLE ${this.escapePath(
84✔
2191
                    table,
84✔
2192
                )} ADD ${this.buildCreateColumnSql(
84✔
2193
                    table,
84✔
2194
                    column,
84✔
2195
                    false,
84✔
2196
                    false,
84✔
2197
                )}`,
84✔
2198
            ),
84✔
2199
        )
84✔
2200

84✔
2201
        await this.executeQueries(upQueries, downQueries)
84✔
2202

84✔
2203
        clonedTable.removeColumn(column)
84✔
2204
        this.replaceCachedTable(table, clonedTable)
84✔
2205
    }
84✔
2206

28✔
2207
    /**
28✔
2208
     * Drops the columns in the table.
28✔
2209
     * @param tableOrName
28✔
2210
     * @param columns
28✔
2211
     * @param ifExists
28✔
2212
     */
28✔
2213
    async dropColumns(
28✔
2214
        tableOrName: Table | string,
10✔
2215
        columns: TableColumn[] | string[],
10✔
2216
        ifExists?: boolean,
10✔
2217
    ): Promise<void> {
10✔
2218
        for (const column of [...columns]) {
10✔
2219
            await this.dropColumn(tableOrName, column, ifExists)
26✔
2220
        }
26✔
2221
    }
10✔
2222

28✔
2223
    /**
28✔
2224
     * Creates a new primary key.
28✔
2225
     * @param tableOrName
28✔
2226
     * @param columnNames
28✔
2227
     * @param constraintName
28✔
2228
     */
28✔
2229
    async createPrimaryKey(
28✔
2230
        tableOrName: Table | string,
4✔
2231
        columnNames: string[],
4✔
2232
        constraintName?: string,
4✔
2233
    ): Promise<void> {
4✔
2234
        const table = InstanceChecker.isTable(tableOrName)
4✔
2235
            ? tableOrName
4!
2236
            : await this.getCachedTable(tableOrName)
4✔
2237
        const clonedTable = table.clone()
4✔
2238

4✔
2239
        const up = this.createPrimaryKeySql(table, columnNames, constraintName)
4✔
2240

4✔
2241
        // mark columns as primary, because dropPrimaryKeySql build constraint name from table primary column names.
4✔
2242
        clonedTable.columns.forEach((column) => {
4✔
2243
            if (columnNames.find((columnName) => columnName === column.name))
10✔
2244
                column.isPrimary = true
10✔
2245
        })
4✔
2246
        const down = this.dropPrimaryKeySql(clonedTable)
4✔
2247

4✔
2248
        await this.executeQueries(up, down)
4✔
2249
        this.replaceCachedTable(table, clonedTable)
4✔
2250
    }
4✔
2251

28✔
2252
    /**
28✔
2253
     * Updates composite primary keys.
28✔
2254
     * @param tableOrName
28✔
2255
     * @param columns
28✔
2256
     */
28✔
2257
    async updatePrimaryKeys(
28✔
2258
        tableOrName: Table | string,
6✔
2259
        columns: TableColumn[],
6✔
2260
    ): Promise<void> {
6✔
2261
        const table = InstanceChecker.isTable(tableOrName)
6✔
2262
            ? tableOrName
6✔
2263
            : await this.getCachedTable(tableOrName)
6!
2264
        const clonedTable = table.clone()
×
2265
        const columnNames = columns.map((column) => column.name)
✔
2266
        const upQueries: Query[] = []
×
2267
        const downQueries: Query[] = []
×
2268

×
2269
        // if table already have primary columns, we must drop them.
×
2270
        const primaryColumns = clonedTable.primaryColumns
×
2271
        if (primaryColumns.length > 0) {
6✔
2272
            const pkName = primaryColumns[0].primaryKeyConstraintName
6✔
2273
                ? primaryColumns[0].primaryKeyConstraintName
6!
2274
                : this.connection.namingStrategy.primaryKeyName(
6✔
2275
                      clonedTable,
6✔
2276
                      primaryColumns.map((column) => column.name),
6✔
2277
                  )
6✔
2278

6✔
2279
            const columnNamesString = primaryColumns
6✔
2280
                .map((column) => `"${column.name}"`)
6✔
2281
                .join(", ")
6✔
2282

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

6✔
2299
        // update columns in table.
6✔
2300
        clonedTable.columns
6✔
2301
            .filter((column) => columnNames.indexOf(column.name) !== -1)
6✔
2302
            .forEach((column) => {
6✔
2303
                column.isPrimary = true
12✔
2304
            })
6✔
2305

6✔
2306
        const pkName = primaryColumns[0].primaryKeyConstraintName
6✔
2307
            ? primaryColumns[0].primaryKeyConstraintName
6!
2308
            : this.connection.namingStrategy.primaryKeyName(
6✔
2309
                  clonedTable,
6✔
2310
                  columnNames,
6✔
2311
              )
6✔
2312

6✔
2313
        const columnNamesString = columnNames
6✔
2314
            .map((columnName) => `"${columnName}"`)
6✔
2315
            .join(", ")
6✔
2316

6✔
2317
        upQueries.push(
6✔
2318
            new Query(
6✔
2319
                `ALTER TABLE ${this.escapePath(
6✔
2320
                    table,
6✔
2321
                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
6✔
2322
            ),
6✔
2323
        )
6✔
2324
        downQueries.push(
6✔
2325
            new Query(
6✔
2326
                `ALTER TABLE ${this.escapePath(
6✔
2327
                    table,
6✔
2328
                )} DROP CONSTRAINT "${pkName}"`,
6✔
2329
            ),
6✔
2330
        )
6✔
2331

6✔
2332
        await this.executeQueries(upQueries, downQueries)
6✔
2333
        this.replaceCachedTable(table, clonedTable)
6✔
2334
    }
6✔
2335

28✔
2336
    /**
28✔
2337
     * Drops a primary key.
28✔
2338
     * @param tableOrName
28✔
2339
     * @param constraintName
28✔
2340
     * @param ifExists
28✔
2341
     */
28✔
2342
    async dropPrimaryKey(
28✔
2343
        tableOrName: Table | string,
8✔
2344
        constraintName?: string,
8✔
2345
        ifExists?: boolean,
8✔
2346
    ): Promise<void> {
8✔
2347
        const table = InstanceChecker.isTable(tableOrName)
8✔
2348
            ? tableOrName
8✔
2349
            : await this.getCachedTable(tableOrName)
8✔
2350
        if (ifExists && table.primaryColumns.length === 0) return
8!
2351

8✔
2352
        const up = this.dropPrimaryKeySql(table)
8✔
2353
        const down = this.createPrimaryKeySql(
8✔
2354
            table,
8✔
2355
            table.primaryColumns.map((column) => column.name),
8✔
2356
            constraintName,
8✔
2357
        )
8✔
2358
        await this.executeQueries(up, down)
8✔
2359
        table.primaryColumns.forEach((column) => {
8✔
2360
            column.isPrimary = false
8✔
2361
        })
8✔
2362
    }
8✔
2363

28✔
2364
    /**
28✔
2365
     * Creates a new unique constraint.
28✔
2366
     * @param tableOrName
28✔
2367
     * @param uniqueConstraint
28✔
2368
     */
28✔
2369
    async createUniqueConstraint(
28✔
2370
        tableOrName: Table | string,
16✔
2371
        uniqueConstraint: TableUnique,
16✔
2372
    ): Promise<void> {
16✔
2373
        const table = InstanceChecker.isTable(tableOrName)
16✔
2374
            ? tableOrName
16✔
2375
            : await this.getCachedTable(tableOrName)
16✔
2376

12✔
2377
        // new unique constraint may be passed without name. In this case we generate unique name manually.
12✔
2378
        if (!uniqueConstraint.name)
12✔
2379
            uniqueConstraint.name =
16✔
2380
                this.connection.namingStrategy.uniqueConstraintName(
4✔
2381
                    table,
4✔
2382
                    uniqueConstraint.columnNames,
4✔
2383
                )
4✔
2384

16✔
2385
        const up = this.createUniqueConstraintSql(table, uniqueConstraint)
16✔
2386
        const down = this.dropUniqueConstraintSql(table, uniqueConstraint)
16✔
2387
        await this.executeQueries(up, down)
16✔
2388
        table.addUniqueConstraint(uniqueConstraint)
16✔
2389
    }
16✔
2390

28✔
2391
    /**
28✔
2392
     * Creates a new unique constraints.
28✔
2393
     * @param tableOrName
28✔
2394
     * @param uniqueConstraints
28✔
2395
     */
28✔
2396
    async createUniqueConstraints(
28✔
2397
        tableOrName: Table | string,
6✔
2398
        uniqueConstraints: TableUnique[],
6✔
2399
    ): Promise<void> {
6✔
2400
        const promises = uniqueConstraints.map((uniqueConstraint) =>
6✔
2401
            this.createUniqueConstraint(tableOrName, uniqueConstraint),
6✔
2402
        )
6✔
2403
        await Promise.all(promises)
6✔
2404
    }
6✔
2405

28✔
2406
    /**
28✔
2407
     * Drops unique constraint.
28✔
2408
     * @param tableOrName
28✔
2409
     * @param uniqueOrName
28✔
2410
     * @param ifExists
28✔
2411
     */
28✔
2412
    async dropUniqueConstraint(
28✔
2413
        tableOrName: Table | string,
18✔
2414
        uniqueOrName: TableUnique | string,
18✔
2415
        ifExists?: boolean,
18✔
2416
    ): Promise<void> {
18✔
2417
        const table = InstanceChecker.isTable(tableOrName)
18✔
2418
            ? tableOrName
18✔
2419
            : await this.getCachedTable(tableOrName)
18✔
2420
        const uniqueConstraint = InstanceChecker.isTableUnique(uniqueOrName)
10✔
2421
            ? uniqueOrName
18✔
2422
            : table.uniques.find((u) => u.name === uniqueOrName)
18✔
2423
        if (!uniqueConstraint) {
18✔
2424
            if (ifExists) return
2✔
2425
            throw new TypeORMError(
×
2426
                `Supplied unique constraint was not found in table ${table.name}`,
×
2427
            )
×
2428
        }
×
2429

16✔
2430
        const up = this.dropUniqueConstraintSql(table, uniqueConstraint)
16✔
2431
        const down = this.createUniqueConstraintSql(table, uniqueConstraint)
16✔
2432
        await this.executeQueries(up, down)
16✔
2433
        table.removeUniqueConstraint(uniqueConstraint)
16✔
2434
    }
16✔
2435

28✔
2436
    /**
28✔
2437
     * Drops unique constraints.
28✔
2438
     * @param tableOrName
28✔
2439
     * @param uniqueConstraints
28✔
2440
     * @param ifExists
28✔
2441
     */
28✔
2442
    async dropUniqueConstraints(
28✔
2443
        tableOrName: Table | string,
8✔
2444
        uniqueConstraints: TableUnique[],
8✔
2445
        ifExists?: boolean,
8✔
2446
    ): Promise<void> {
8✔
2447
        const promises = uniqueConstraints.map((uniqueConstraint) =>
8✔
2448
            this.dropUniqueConstraint(tableOrName, uniqueConstraint, ifExists),
8✔
2449
        )
8✔
2450
        await Promise.all(promises)
8✔
2451
    }
8✔
2452

28✔
2453
    /**
28✔
2454
     * Creates a new check constraint.
28✔
2455
     * @param tableOrName
28✔
2456
     * @param checkConstraint
28✔
2457
     */
28✔
2458
    async createCheckConstraint(
28✔
2459
        tableOrName: Table | string,
10✔
2460
        checkConstraint: TableCheck,
10✔
2461
    ): Promise<void> {
10✔
2462
        const table = InstanceChecker.isTable(tableOrName)
10✔
2463
            ? tableOrName
10✔
2464
            : await this.getCachedTable(tableOrName)
10✔
2465

6✔
2466
        // new unique constraint may be passed without name. In this case we generate unique name manually.
6✔
2467
        if (!checkConstraint.name)
6✔
2468
            checkConstraint.name =
6✔
2469
                this.connection.namingStrategy.checkConstraintName(
6✔
2470
                    table,
6✔
2471
                    checkConstraint.expression!,
6✔
2472
                )
6✔
2473

10✔
2474
        const up = this.createCheckConstraintSql(table, checkConstraint)
10✔
2475
        const down = this.dropCheckConstraintSql(table, checkConstraint)
10✔
2476
        await this.executeQueries(up, down)
10✔
2477
        table.addCheckConstraint(checkConstraint)
10✔
2478
    }
10✔
2479

28✔
2480
    /**
28✔
2481
     * Creates a new check constraints.
28✔
2482
     * @param tableOrName
28✔
2483
     * @param checkConstraints
28✔
2484
     */
28✔
2485
    async createCheckConstraints(
28✔
2486
        tableOrName: Table | string,
4✔
2487
        checkConstraints: TableCheck[],
4✔
2488
    ): Promise<void> {
4✔
2489
        const promises = checkConstraints.map((checkConstraint) =>
4✔
2490
            this.createCheckConstraint(tableOrName, checkConstraint),
4✔
2491
        )
4✔
2492
        await Promise.all(promises)
4✔
2493
    }
4✔
2494

28✔
2495
    /**
28✔
2496
     * Drops check constraint.
28✔
2497
     * @param tableOrName
28✔
2498
     * @param checkOrName
28✔
2499
     * @param ifExists
28✔
2500
     */
28✔
2501
    async dropCheckConstraint(
28✔
2502
        tableOrName: Table | string,
8✔
2503
        checkOrName: TableCheck | string,
8✔
2504
        ifExists?: boolean,
8✔
2505
    ): Promise<void> {
8✔
2506
        const table = InstanceChecker.isTable(tableOrName)
8✔
2507
            ? tableOrName
8✔
2508
            : await this.getCachedTable(tableOrName)
8✔
2509
        const checkConstraint = InstanceChecker.isTableCheck(checkOrName)
2✔
2510
            ? checkOrName
8✔
2511
            : table.checks.find((c) => c.name === checkOrName)
8✔
2512
        if (!checkConstraint) {
8✔
2513
            if (ifExists) return
2✔
2514
            throw new TypeORMError(
×
2515
                `Supplied check constraint was not found in table ${table.name}`,
×
2516
            )
×
2517
        }
×
2518

6✔
2519
        const up = this.dropCheckConstraintSql(table, checkConstraint)
6✔
2520
        const down = this.createCheckConstraintSql(table, checkConstraint)
6✔
2521
        await this.executeQueries(up, down)
6✔
2522
        table.removeCheckConstraint(checkConstraint)
6✔
2523
    }
6✔
2524

28✔
2525
    /**
28✔
2526
     * Drops check constraints.
28✔
2527
     * @param tableOrName
28✔
2528
     * @param checkConstraints
28✔
2529
     * @param ifExists
28✔
2530
     */
28✔
2531
    async dropCheckConstraints(
28✔
2532
        tableOrName: Table | string,
4✔
2533
        checkConstraints: TableCheck[],
4✔
2534
        ifExists?: boolean,
4✔
2535
    ): Promise<void> {
4✔
2536
        const promises = checkConstraints.map((checkConstraint) =>
4✔
2537
            this.dropCheckConstraint(tableOrName, checkConstraint, ifExists),
4✔
2538
        )
4✔
2539
        await Promise.all(promises)
4✔
2540
    }
4✔
2541

28✔
2542
    /**
28✔
2543
     * Creates a new exclusion constraint.
28✔
2544
     * @param tableOrName
28✔
2545
     * @param exclusionConstraint
28✔
2546
     */
28✔
2547
    async createExclusionConstraint(
28✔
2548
        tableOrName: Table | string,
×
2549
        exclusionConstraint: TableExclusion,
×
2550
    ): Promise<void> {
×
2551
        throw new TypeORMError(
×
2552
            `SqlServer does not support exclusion constraints.`,
×
2553
        )
×
2554
    }
×
2555

28✔
2556
    /**
28✔
2557
     * Creates a new exclusion constraints.
28✔
2558
     * @param tableOrName
28✔
2559
     * @param exclusionConstraints
28✔
2560
     */
28✔
2561
    async createExclusionConstraints(
28✔
2562
        tableOrName: Table | string,
×
2563
        exclusionConstraints: TableExclusion[],
×
2564
    ): Promise<void> {
×
2565
        throw new TypeORMError(
×
2566
            `SqlServer does not support exclusion constraints.`,
×
2567
        )
×
2568
    }
×
2569

28✔
2570
    /**
28✔
2571
     * Drops exclusion constraint.
28✔
2572
     * @param tableOrName
28✔
2573
     * @param exclusionOrName
28✔
2574
     * @param ifExists
28✔
2575
     */
28✔
2576
    async dropExclusionConstraint(
28✔
2577
        tableOrName: Table | string,
×
2578
        exclusionOrName: TableExclusion | string,
×
2579
        ifExists?: boolean,
×
2580
    ): Promise<void> {
×
2581
        throw new TypeORMError(
×
2582
            `SqlServer does not support exclusion constraints.`,
×
2583
        )
×
2584
    }
×
2585

28✔
2586
    /**
28✔
2587
     * Drops exclusion constraints.
28✔
2588
     * @param tableOrName
28✔
2589
     * @param exclusionConstraints
28✔
2590
     * @param ifExists
28✔
2591
     */
28✔
2592
    async dropExclusionConstraints(
28✔
2593
        tableOrName: Table | string,
×
2594
        exclusionConstraints: TableExclusion[],
×
2595
        ifExists?: boolean,
×
2596
    ): Promise<void> {
×
2597
        throw new TypeORMError(
×
2598
            `SqlServer does not support exclusion constraints.`,
×
2599
        )
×
2600
    }
×
2601

28✔
2602
    /**
28✔
2603
     * Creates a new foreign key.
28✔
2604
     * @param tableOrName
28✔
2605
     * @param foreignKey
28✔
2606
     */
28✔
2607
    async createForeignKey(
28✔
2608
        tableOrName: Table | string,
6,784✔
2609
        foreignKey: TableForeignKey,
6,784✔
2610
    ): Promise<void> {
6,784✔
2611
        const table = InstanceChecker.isTable(tableOrName)
6,784✔
2612
            ? tableOrName
6,784✔
2613
            : await this.getCachedTable(tableOrName)
6,784✔
2614
        const metadata = this.connection.hasMetadata(table.name)
10✔
2615
            ? this.connection.getMetadata(table.name)
6,784✔
2616
            : undefined
6,784✔
2617

6,784✔
2618
        if (
6,784✔
2619
            metadata &&
6,784✔
2620
            metadata.treeParentRelation &&
6,784✔
2621
            metadata.treeParentRelation!.isTreeParent &&
6,784✔
2622
            metadata.foreignKeys.find(
778✔
2623
                (foreignKey) => foreignKey.onDelete !== "NO ACTION",
778✔
2624
            )
6,784✔
2625
        )
6,784✔
2626
            throw new TypeORMError(
6,784!
2627
                "SqlServer does not support options in TreeParent.",
×
2628
            )
×
2629

6,784✔
2630
        // new FK may be passed without name. In this case we generate FK name manually.
6,784✔
2631
        if (!foreignKey.name)
6,784✔
2632
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
6,784✔
2633
                table,
2✔
2634
                foreignKey.columnNames,
2✔
2635
                this.getTablePath(foreignKey),
2✔
2636
                foreignKey.referencedColumnNames,
2✔
2637
            )
2✔
2638

6,784✔
2639
        const up = this.createForeignKeySql(table, foreignKey)
6,784✔
2640
        const down = this.dropForeignKeySql(table, foreignKey)
6,784✔
2641
        await this.executeQueries(up, down)
6,784✔
2642
        table.addForeignKey(foreignKey)
6,784✔
2643
    }
6,784✔
2644

28✔
2645
    /**
28✔
2646
     * Creates a new foreign keys.
28✔
2647
     * @param tableOrName
28✔
2648
     * @param foreignKeys
28✔
2649
     */
28✔
2650
    async createForeignKeys(
28✔
2651
        tableOrName: Table | string,
4,228✔
2652
        foreignKeys: TableForeignKey[],
4,228✔
2653
    ): Promise<void> {
4,228✔
2654
        const promises = foreignKeys.map((foreignKey) =>
4,228✔
2655
            this.createForeignKey(tableOrName, foreignKey),
4,228✔
2656
        )
4,228✔
2657
        await Promise.all(promises)
4,228✔
2658
    }
4,228✔
2659

28✔
2660
    /**
28✔
2661
     * Drops a foreign key from the table.
28✔
2662
     * @param tableOrName
28✔
2663
     * @param foreignKeyOrName
28✔
2664
     * @param ifExists
28✔
2665
     */
28✔
2666
    async dropForeignKey(
28✔
2667
        tableOrName: Table | string,
24✔
2668
        foreignKeyOrName: TableForeignKey | string,
24✔
2669
        ifExists?: boolean,
24✔
2670
    ): Promise<void> {
24✔
2671
        const table = InstanceChecker.isTable(tableOrName)
24✔
2672
            ? tableOrName
24✔
2673
            : await this.getCachedTable(tableOrName)
24✔
2674
        const foreignKey = InstanceChecker.isTableForeignKey(foreignKeyOrName)
10✔
2675
            ? foreignKeyOrName
24✔
2676
            : table.foreignKeys.find((fk) => fk.name === foreignKeyOrName)
24✔
2677
        if (!foreignKey) {
24✔
2678
            if (ifExists) return
2✔
2679
            throw new TypeORMError(
×
2680
                `Supplied foreign key was not found in table ${table.name}`,
×
2681
            )
×
2682
        }
×
2683

22✔
2684
        if (!foreignKey.name) {
24!
2685
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
×
2686
                table,
×
2687
                foreignKey.columnNames,
×
2688
                this.getTablePath(foreignKey),
×
2689
                foreignKey.referencedColumnNames,
×
2690
            )
×
2691
        }
×
2692

22✔
2693
        const up = this.dropForeignKeySql(table, foreignKey)
22✔
2694
        const down = this.createForeignKeySql(table, foreignKey)
22✔
2695
        await this.executeQueries(up, down)
22✔
2696
        table.removeForeignKey(foreignKey)
22✔
2697
    }
22✔
2698

28✔
2699
    /**
28✔
2700
     * Drops a foreign keys from the table.
28✔
2701
     * @param tableOrName
28✔
2702
     * @param foreignKeys
28✔
2703
     * @param ifExists
28✔
2704
     */
28✔
2705
    async dropForeignKeys(
28✔
2706
        tableOrName: Table | string,
14✔
2707
        foreignKeys: TableForeignKey[],
14✔
2708
        ifExists?: boolean,
14✔
2709
    ): Promise<void> {
14✔
2710
        const promises = foreignKeys.map((foreignKey) =>
14✔
2711
            this.dropForeignKey(tableOrName, foreignKey, ifExists),
14✔
2712
        )
14✔
2713
        await Promise.all(promises)
14✔
2714
    }
14✔
2715

28✔
2716
    /**
28✔
2717
     * Creates a new index.
28✔
2718
     * @param tableOrName
28✔
2719
     * @param index
28✔
2720
     */
28✔
2721
    async createIndex(
28✔
2722
        tableOrName: Table | string,
28✔
2723
        index: TableIndex,
28✔
2724
    ): Promise<void> {
28✔
2725
        const table = InstanceChecker.isTable(tableOrName)
28✔
2726
            ? tableOrName
28✔
2727
            : await this.getCachedTable(tableOrName)
28✔
2728

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

28✔
2732
        const up = this.createIndexSql(table, index)
28✔
2733
        const down = this.dropIndexSql(table, index)
28✔
2734
        await this.executeQueries(up, down)
28✔
2735
        table.addIndex(index)
28✔
2736
    }
28✔
2737

28✔
2738
    /**
28✔
2739
     * Creates a new indices
28✔
2740
     * @param tableOrName
28✔
2741
     * @param indices
28✔
2742
     */
28✔
2743
    async createIndices(
28✔
2744
        tableOrName: Table | string,
14✔
2745
        indices: TableIndex[],
14✔
2746
    ): Promise<void> {
14✔
2747
        const promises = indices.map((index) =>
14✔
2748
            this.createIndex(tableOrName, index),
14✔
2749
        )
14✔
2750
        await Promise.all(promises)
14✔
2751
    }
14✔
2752

28✔
2753
    /**
28✔
2754
     * Drops an index.
28✔
2755
     * @param tableOrName
28✔
2756
     * @param indexOrName
28✔
2757
     * @param ifExists
28✔
2758
     */
28✔
2759
    async dropIndex(
28✔
2760
        tableOrName: Table | string,
34✔
2761
        indexOrName: TableIndex | string,
34✔
2762
        ifExists?: boolean,
34✔
2763
    ): Promise<void> {
34✔
2764
        const table = InstanceChecker.isTable(tableOrName)
34✔
2765
            ? tableOrName
34✔
2766
            : await this.getCachedTable(tableOrName)
34✔
2767
        const index = InstanceChecker.isTableIndex(indexOrName)
12✔
2768
            ? indexOrName
34✔
2769
            : table.indices.find((i) => i.name === indexOrName)
34✔
2770
        if (!index) {
34✔
2771
            if (ifExists) return
2✔
2772
            throw new TypeORMError(
×
2773
                `Supplied index was not found in table ${table.name}`,
×
2774
            )
×
2775
        }
×
2776

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

32✔
2780
        const up = this.dropIndexSql(table, index)
32✔
2781
        const down = this.createIndexSql(table, index)
32✔
2782
        await this.executeQueries(up, down)
32✔
2783
        table.removeIndex(index)
32✔
2784
    }
32✔
2785

28✔
2786
    /**
28✔
2787
     * Drops an indices from the table.
28✔
2788
     * @param tableOrName
28✔
2789
     * @param indices
28✔
2790
     * @param ifExists
28✔
2791
     */
28✔
2792
    async dropIndices(
28✔
2793
        tableOrName: Table | string,
4✔
2794
        indices: TableIndex[],
4✔
2795
        ifExists?: boolean,
4✔
2796
    ): Promise<void> {
4✔
2797
        const promises = indices.map((index) =>
4✔
2798
            this.dropIndex(tableOrName, index, ifExists),
4✔
2799
        )
4✔
2800
        await Promise.all(promises)
4✔
2801
    }
4✔
2802

28✔
2803
    /**
28✔
2804
     * Clears all table contents.
28✔
2805
     * Note: this operation uses SQL's TRUNCATE query which cannot be reverted in transactions.
28✔
2806
     * @param tablePath
28✔
2807
     * @param options
28✔
2808
     * @param options.cascade
28✔
2809
     */
28✔
2810
    async clearTable(
28✔
2811
        tablePath: string,
10✔
2812
        options?: { cascade?: boolean },
10✔
2813
    ): Promise<void> {
10✔
2814
        if (options?.cascade) {
10✔
2815
            throw new TypeORMError(
2✔
2816
                `SqlServer does not support clearing table with cascade option`,
2✔
2817
            )
2✔
2818
        }
2✔
2819
        await this.query(`TRUNCATE TABLE ${this.escapePath(tablePath)}`)
8✔
2820
    }
6✔
2821

28✔
2822
    /**
28✔
2823
     * Removes all tables from the currently connected database.
28✔
2824
     * @param database
28✔
2825
     */
28✔
2826
    async clearDatabase(database?: string): Promise<void> {
28✔
2827
        if (database) {
2,982✔
2828
            const isDatabaseExist = await this.hasDatabase(database)
2,982✔
2829
            if (!isDatabaseExist) return Promise.resolve()
2,982!
2830
        }
2,982✔
2831

2,982✔
2832
        const isAnotherTransactionActive = this.isTransactionActive
2,982✔
2833
        if (!isAnotherTransactionActive) await this.startTransaction()
2,982✔
2834
        try {
2,982✔
2835
            const allViewsSql = database
2,982✔
2836
                ? `SELECT * FROM "${database}"."INFORMATION_SCHEMA"."VIEWS"`
2,982✔
2837
                : `SELECT * FROM "INFORMATION_SCHEMA"."VIEWS"`
2,982!
2838
            const allViewsResults: ObjectLiteral[] =
2,982✔
2839
                await this.query(allViewsSql)
2,982✔
2840

2,982✔
2841
            await Promise.all(
2,982✔
2842
                allViewsResults.map((viewResult) => {
2,982✔
2843
                    // 'DROP VIEW' does not allow specifying the database name as a prefix to the object name.
16✔
2844
                    const dropTableSql = `DROP VIEW "${viewResult["TABLE_SCHEMA"]}"."${viewResult["TABLE_NAME"]}"`
16✔
2845
                    return this.query(dropTableSql)
16✔
2846
                }),
2,982✔
2847
            )
2,982✔
2848

2,982✔
2849
            const allTablesSql = database
2,982✔
2850
                ? `SELECT * FROM "${database}"."INFORMATION_SCHEMA"."TABLES" WHERE "TABLE_TYPE" = 'BASE TABLE'`
2,982✔
2851
                : `SELECT * FROM "INFORMATION_SCHEMA"."TABLES" WHERE "TABLE_TYPE" = 'BASE TABLE'`
2,982!
2852
            const allTablesResults: ObjectLiteral[] =
2,982✔
2853
                await this.query(allTablesSql)
2,982✔
2854

2,982✔
2855
            if (allTablesResults.length > 0) {
2,982✔
2856
                const tablesByCatalog: {
2,938✔
2857
                    [key: string]: {
2,938✔
2858
                        TABLE_NAME: string
2,938✔
2859
                        TABLE_SCHEMA: string
2,938✔
2860
                    }[]
2,938✔
2861
                } = allTablesResults.reduce(
2,938✔
2862
                    (c, { TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME }) => {
2,938✔
2863
                        c[TABLE_CATALOG] = c[TABLE_CATALOG] || []
10,326✔
2864
                        c[TABLE_CATALOG].push({ TABLE_SCHEMA, TABLE_NAME })
10,326✔
2865
                        return c
10,326✔
2866
                    },
2,938✔
2867
                    {},
2,938✔
2868
                )
2,938✔
2869

2,938✔
2870
                const foreignKeysSql = Object.entries(tablesByCatalog)
2,938✔
2871
                    .map(([TABLE_CATALOG, tables]) => {
2,938✔
2872
                        const conditions = tables
2,938✔
2873
                            .map(({ TABLE_SCHEMA, TABLE_NAME }) => {
2,938✔
2874
                                return `("fk"."referenced_object_id" = OBJECT_ID('"${TABLE_CATALOG}"."${TABLE_SCHEMA}"."${TABLE_NAME}"'))`
10,326✔
2875
                            })
2,938✔
2876
                            .join(" OR ")
2,938✔
2877

2,938✔
2878
                        return `
2,938✔
2879
                        SELECT DISTINCT '${TABLE_CATALOG}' AS                                              "TABLE_CATALOG",
2,938✔
2880
                                        OBJECT_SCHEMA_NAME("fk"."parent_object_id",
2,938✔
2881
                                                           DB_ID('${TABLE_CATALOG}')) AS                   "TABLE_SCHEMA",
2,938✔
2882
                                        OBJECT_NAME("fk"."parent_object_id", DB_ID('${TABLE_CATALOG}')) AS "TABLE_NAME",
2,938✔
2883
                                        "fk"."name" AS                                                     "CONSTRAINT_NAME"
2,938✔
2884
                        FROM "${TABLE_CATALOG}"."sys"."foreign_keys" AS "fk"
2,938✔
2885
                        WHERE (${conditions})
2,938✔
2886
                    `
2,938✔
2887
                    })
2,938✔
2888
                    .join(" UNION ALL ")
2,938✔
2889

2,938✔
2890
                const foreignKeys: {
2,938✔
2891
                    TABLE_CATALOG: string
2,938✔
2892
                    TABLE_SCHEMA: string
2,938✔
2893
                    TABLE_NAME: string
2,938✔
2894
                    CONSTRAINT_NAME: string
2,938✔
2895
                }[] = await this.query(foreignKeysSql)
2,938✔
2896

2,938✔
2897
                await Promise.all(
2,938✔
2898
                    foreignKeys.map(
2,938✔
2899
                        async ({
2,938✔
2900
                            TABLE_CATALOG,
6,750✔
2901
                            TABLE_SCHEMA,
6,750✔
2902
                            TABLE_NAME,
6,750✔
2903
                            CONSTRAINT_NAME,
6,750✔
2904
                        }) => {
6,750✔
2905
                            // Disable the constraint first.
6,750✔
2906
                            await this.query(
6,750✔
2907
                                `ALTER TABLE "${TABLE_CATALOG}"."${TABLE_SCHEMA}"."${TABLE_NAME}" ` +
6,750✔
2908
                                    `NOCHECK CONSTRAINT "${CONSTRAINT_NAME}"`,
6,750✔
2909
                            )
6,750✔
2910

6,750✔
2911
                            await this.query(
6,750✔
2912
                                `ALTER TABLE "${TABLE_CATALOG}"."${TABLE_SCHEMA}"."${TABLE_NAME}" ` +
6,750✔
2913
                                    `DROP CONSTRAINT "${CONSTRAINT_NAME}" -- FROM CLEAR`,
6,750✔
2914
                            )
6,750✔
2915
                        },
2,938✔
2916
                    ),
2,938✔
2917
                )
2,938✔
2918

2,938✔
2919
                await Promise.all(
2,938✔
2920
                    allTablesResults.map(async (tablesResult) => {
2,938✔
2921
                        if (tablesResult["TABLE_NAME"].startsWith("#")) {
10,326!
2922
                            // don't try to drop temporary tables
×
2923
                            return
×
2924
                        }
×
2925

10,326✔
2926
                        const dropTableSql = `DROP TABLE "${tablesResult["TABLE_CATALOG"]}"."${tablesResult["TABLE_SCHEMA"]}"."${tablesResult["TABLE_NAME"]}"`
10,326✔
2927
                        return this.query(dropTableSql)
10,326✔
2928
                    }),
2,938✔
2929
                )
2,938✔
2930
            }
2,938✔
2931

2,982✔
2932
            if (!isAnotherTransactionActive) await this.commitTransaction()
2,982✔
2933
        } catch (error) {
2,982!
2934
            try {
×
2935
                // we throw original error even if rollback thrown an error
×
2936
                if (!isAnotherTransactionActive)
×
2937
                    await this.rollbackTransaction()
×
2938
            } catch (rollbackError) {}
×
2939
            throw error
×
2940
        }
×
2941
    }
2,982✔
2942

28✔
2943
    // -------------------------------------------------------------------------
28✔
2944
    // Protected Methods
28✔
2945
    // -------------------------------------------------------------------------
28✔
2946

28✔
2947
    protected async loadViews(viewPaths?: string[]): Promise<View[]> {
28✔
2948
        const hasTable = await this.hasTable(this.getTypeormMetadataTableName())
3,092✔
2949
        if (!hasTable) {
3,092✔
2950
            return []
3,062✔
2951
        }
3,062✔
2952

30✔
2953
        if (!viewPaths) {
3,092!
2954
            viewPaths = []
×
2955
        }
×
2956

30✔
2957
        const currentSchema = await this.getCurrentSchema()
30✔
2958
        const currentDatabase = await this.getCurrentDatabase()
30✔
2959

30✔
2960
        const dbNames = viewPaths
30✔
2961
            .map((viewPath) => this.driver.parseTableName(viewPath).database)
30✔
2962
            .filter((database) => database)
30✔
2963

30✔
2964
        if (
30✔
2965
            this.driver.database &&
30✔
2966
            !dbNames.find((dbName) => dbName === this.driver.database)
30✔
2967
        )
3,092✔
2968
            dbNames.push(this.driver.database)
3,092✔
2969

30✔
2970
        const viewsCondition = viewPaths
30✔
2971
            .map((viewPath) => {
30✔
2972
                let { schema, tableName: name } =
22✔
2973
                    this.driver.parseTableName(viewPath)
22✔
2974

22✔
2975
                if (!schema) {
22!
2976
                    schema = currentSchema
×
2977
                }
×
2978
                return `("T"."SCHEMA" = '${schema}' AND "T"."NAME" = '${name}')`
22✔
2979
            })
30✔
2980
            .join(" OR ")
30✔
2981

30✔
2982
        const query = dbNames
30✔
2983
            .map((dbName) => {
30✔
2984
                return (
38✔
2985
                    `SELECT "T".*, "V"."CHECK_OPTION" FROM ${this.escapePath(
38✔
2986
                        this.getTypeormMetadataTableName(),
38✔
2987
                    )} "t" ` +
38✔
2988
                    `INNER JOIN "${dbName}"."INFORMATION_SCHEMA"."VIEWS" "V" ON "V"."TABLE_SCHEMA" = "T"."SCHEMA" AND "v"."TABLE_NAME" = "T"."NAME" WHERE "T"."TYPE" = '${
38✔
2989
                        MetadataTableType.VIEW
38✔
2990
                    }' ${viewsCondition ? `AND (${viewsCondition})` : ""}`
38✔
2991
                )
38✔
2992
            })
30✔
2993
            .join(" UNION ALL ")
30✔
2994

30✔
2995
        const dbViews = await this.query(query)
30✔
2996
        return dbViews.map((dbView: any) => {
30✔
2997
            const view = new View()
6✔
2998
            const db =
6✔
2999
                dbView["TABLE_CATALOG"] === currentDatabase
6✔
3000
                    ? undefined
6!
3001
                    : dbView["TABLE_CATALOG"]
6✔
3002
            const schema =
6✔
3003
                dbView["schema"] === currentSchema &&
6✔
3004
                !this.driver.options.schema
6✔
3005
                    ? undefined
6✔
3006
                    : dbView["schema"]
6!
3007
            view.database = dbView["TABLE_CATALOG"]
6✔
3008
            view.schema = dbView["schema"]
6✔
3009
            view.name = this.driver.buildTableName(dbView["name"], schema, db)
6✔
3010
            view.expression = dbView["value"]
6✔
3011
            return view
6✔
3012
        })
30✔
3013
    }
30✔
3014

28✔
3015
    /**
28✔
3016
     * Loads all tables (with given names) from the database and creates a Table from them.
28✔
3017
     * @param tableNames
28✔
3018
     */
28✔
3019
    protected async loadTables(tableNames?: string[]): Promise<Table[]> {
28✔
3020
        // if no tables given then no need to proceed
3,680✔
3021
        if (tableNames && tableNames.length === 0) {
3,680✔
3022
            return []
14✔
3023
        }
14✔
3024

3,666✔
3025
        const currentSchema = await this.getCurrentSchema()
3,666✔
3026
        const currentDatabase = await this.getCurrentDatabase()
3,666✔
3027

3,666✔
3028
        const dbTables: {
3,666✔
3029
            TABLE_CATALOG: string
3,666✔
3030
            TABLE_SCHEMA: string
3,666✔
3031
            TABLE_NAME: string
3,666✔
3032
        }[] = []
3,666✔
3033

3,666✔
3034
        if (!tableNames) {
3,680!
3035
            const databasesSql =
×
3036
                `SELECT DISTINCT "name" ` +
×
3037
                `FROM "master"."dbo"."sysdatabases" ` +
×
3038
                `WHERE "name" NOT IN ('master', 'model', 'msdb')`
×
3039
            const dbDatabases: { name: string }[] =
×
3040
                await this.query(databasesSql)
×
3041

×
3042
            const tablesSql = dbDatabases
×
3043
                .map(({ name }) => {
×
3044
                    return `
×
3045
                    SELECT DISTINCT
×
3046
                        "TABLE_CATALOG", "TABLE_SCHEMA", "TABLE_NAME"
×
3047
                    FROM "${name}"."INFORMATION_SCHEMA"."TABLES"
×
3048
                    WHERE
×
3049
                      "TABLE_TYPE" = 'BASE TABLE'
×
3050
                      AND
×
3051
                      "TABLE_CATALOG" = '${name}'
×
3052
                      AND
×
3053
                      ISNULL(Objectproperty(Object_id("TABLE_CATALOG" + '.' + "TABLE_SCHEMA" + '.' + "TABLE_NAME"), 'IsMSShipped'), 0) = 0
×
3054
                `
×
3055
                })
×
3056
                .join(" UNION ALL ")
×
3057

×
3058
            dbTables.push(...(await this.query(tablesSql)))
×
3059
        } else {
3,680✔
3060
            const tableNamesByCatalog = tableNames
3,666✔
3061
                .map((tableName) => this.driver.parseTableName(tableName))
3,666✔
3062
                .reduce(
3,666✔
3063
                    (c, { database, ...other }) => {
3,666✔
3064
                        database = database || currentDatabase
11,338!
3065
                        c[database] = c[database] || []
11,338✔
3066
                        c[database].push({
11,338✔
3067
                            schema: other.schema || currentSchema,
11,338!
3068
                            tableName: other.tableName,
11,338✔
3069
                        })
11,338✔
3070
                        return c
11,338✔
3071
                    },
3,666✔
3072
                    {} as {
3,666✔
3073
                        [key: string]: { schema: string; tableName: string }[]
3,666✔
3074
                    },
3,666✔
3075
                )
3,666✔
3076

3,666✔
3077
            const tablesSql = Object.entries(tableNamesByCatalog)
3,666✔
3078
                .map(([database, tables]) => {
3,666✔
3079
                    const tablesCondition = tables
3,674✔
3080
                        .map(({ schema, tableName }) => {
3,674✔
3081
                            return `("TABLE_SCHEMA" = '${schema}' AND "TABLE_NAME" = '${tableName}')`
11,338✔
3082
                        })
3,674✔
3083
                        .join(" OR ")
3,674✔
3084

3,674✔
3085
                    return `
3,674✔
3086
                    SELECT DISTINCT
3,674✔
3087
                        "TABLE_CATALOG", "TABLE_SCHEMA", "TABLE_NAME"
3,674✔
3088
                    FROM "${database}"."INFORMATION_SCHEMA"."TABLES"
3,674✔
3089
                    WHERE
3,674✔
3090
                          "TABLE_TYPE" = 'BASE TABLE' AND
3,674✔
3091
                          "TABLE_CATALOG" = '${database}' AND
3,674✔
3092
                          ${tablesCondition}
3,674✔
3093
                `
3,674✔
3094
                })
3,666✔
3095
                .join(" UNION ALL ")
3,666✔
3096

3,666✔
3097
            dbTables.push(...(await this.query(tablesSql)))
3,666✔
3098
        }
3,666✔
3099

3,666✔
3100
        // if tables were not found in the db, no need to proceed
3,666✔
3101
        if (dbTables.length === 0) {
3,680✔
3102
            return []
2,986✔
3103
        }
2,986✔
3104

680✔
3105
        const dbTablesByCatalog = dbTables.reduce(
680✔
3106
            (c, { TABLE_CATALOG, ...other }) => {
680✔
3107
                c[TABLE_CATALOG] = c[TABLE_CATALOG] || []
1,004✔
3108
                c[TABLE_CATALOG].push(other)
1,004✔
3109
                return c
1,004✔
3110
            },
680✔
3111
            {} as {
680✔
3112
                [key: string]: { TABLE_NAME: string; TABLE_SCHEMA: string }[]
680✔
3113
            },
680✔
3114
        )
680✔
3115

680✔
3116
        const columnsSql = Object.entries(dbTablesByCatalog)
680✔
3117
            .map(([TABLE_CATALOG, tables]) => {
680✔
3118
                const condition = tables
680✔
3119
                    .map(
680✔
3120
                        ({ TABLE_SCHEMA, TABLE_NAME }) =>
680✔
3121
                            `("TABLE_SCHEMA" = '${TABLE_SCHEMA}' AND "TABLE_NAME" = '${TABLE_NAME}')`,
680✔
3122
                    )
680✔
3123
                    .join("OR")
680✔
3124

680✔
3125
                return (
680✔
3126
                    `SELECT "COLUMNS".*, "cc"."is_persisted", "cc"."definition" ` +
680✔
3127
                    `FROM "${TABLE_CATALOG}"."INFORMATION_SCHEMA"."COLUMNS" ` +
680✔
3128
                    `LEFT JOIN "sys"."computed_columns" "cc" ON COL_NAME("cc"."object_id", "cc"."column_id") = "column_name" ` +
680✔
3129
                    `WHERE (${condition})`
680✔
3130
                )
680✔
3131
            })
680✔
3132
            .join(" UNION ALL ")
680✔
3133

680✔
3134
        const constraintsSql = Object.entries(dbTablesByCatalog)
680✔
3135
            .map(([TABLE_CATALOG, tables]) => {
680✔
3136
                const conditions = tables
680✔
3137
                    .map(
680✔
3138
                        ({ TABLE_NAME, TABLE_SCHEMA }) =>
680✔
3139
                            `("columnUsages"."TABLE_SCHEMA" = '${TABLE_SCHEMA}' AND "columnUsages"."TABLE_NAME" = '${TABLE_NAME}')`,
680✔
3140
                    )
680✔
3141
                    .join(" OR ")
680✔
3142

680✔
3143
                return (
680✔
3144
                    `SELECT "columnUsages".*, "tableConstraints"."CONSTRAINT_TYPE", "chk"."definition" ` +
680✔
3145
                    `FROM "${TABLE_CATALOG}"."INFORMATION_SCHEMA"."CONSTRAINT_COLUMN_USAGE" "columnUsages" ` +
680✔
3146
                    `INNER JOIN "${TABLE_CATALOG}"."INFORMATION_SCHEMA"."TABLE_CONSTRAINTS" "tableConstraints" ` +
680✔
3147
                    `ON ` +
680✔
3148
                    `"tableConstraints"."CONSTRAINT_NAME" = "columnUsages"."CONSTRAINT_NAME" AND ` +
680✔
3149
                    `"tableConstraints"."TABLE_SCHEMA" = "columnUsages"."TABLE_SCHEMA" AND ` +
680✔
3150
                    `"tableConstraints"."TABLE_NAME" = "columnUsages"."TABLE_NAME" ` +
680✔
3151
                    `LEFT JOIN "${TABLE_CATALOG}"."sys"."check_constraints" "chk" ` +
680✔
3152
                    `ON ` +
680✔
3153
                    `"chk"."object_id" = OBJECT_ID("columnUsages"."TABLE_CATALOG" + '.' + "columnUsages"."TABLE_SCHEMA" + '.' + "columnUsages"."CONSTRAINT_NAME") ` +
680✔
3154
                    `WHERE ` +
680✔
3155
                    `(${conditions}) AND ` +
680✔
3156
                    `"tableConstraints"."CONSTRAINT_TYPE" IN ('PRIMARY KEY', 'UNIQUE', 'CHECK')`
680✔
3157
                )
680✔
3158
            })
680✔
3159
            .join(" UNION ALL ")
680✔
3160

680✔
3161
        const foreignKeysSql = Object.entries(dbTablesByCatalog)
680✔
3162
            .map(([TABLE_CATALOG, tables]) => {
680✔
3163
                const conditions = tables
680✔
3164
                    .map(
680✔
3165
                        ({ TABLE_NAME, TABLE_SCHEMA }) =>
680✔
3166
                            `("s1"."name" = '${TABLE_SCHEMA}' AND "t1"."name" = '${TABLE_NAME}')`,
680✔
3167
                    )
680✔
3168
                    .join(" OR ")
680✔
3169

680✔
3170
                return (
680✔
3171
                    `SELECT "fk"."name" AS "FK_NAME", '${TABLE_CATALOG}' AS "TABLE_CATALOG", "s1"."name" AS "TABLE_SCHEMA", "t1"."name" AS "TABLE_NAME", ` +
680✔
3172
                    `"col1"."name" AS "COLUMN_NAME", "s2"."name" AS "REF_SCHEMA", "t2"."name" AS "REF_TABLE", "col2"."name" AS "REF_COLUMN", ` +
680✔
3173
                    `"fk"."delete_referential_action_desc" AS "ON_DELETE", "fk"."update_referential_action_desc" AS "ON_UPDATE" ` +
680✔
3174
                    `FROM "${TABLE_CATALOG}"."sys"."foreign_keys" "fk" ` +
680✔
3175
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."foreign_key_columns" "fkc" ON "fkc"."constraint_object_id" = "fk"."object_id" ` +
680✔
3176
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."tables" "t1" ON "t1"."object_id" = "fk"."parent_object_id" ` +
680✔
3177
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."schemas" "s1" ON "s1"."schema_id" = "t1"."schema_id" ` +
680✔
3178
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."tables" "t2" ON "t2"."object_id" = "fk"."referenced_object_id" ` +
680✔
3179
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."schemas" "s2" ON "s2"."schema_id" = "t2"."schema_id" ` +
680✔
3180
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."columns" "col1" ON "col1"."column_id" = "fkc"."parent_column_id" AND "col1"."object_id" = "fk"."parent_object_id" ` +
680✔
3181
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."columns" "col2" ON "col2"."column_id" = "fkc"."referenced_column_id" AND "col2"."object_id" = "fk"."referenced_object_id" ` +
680✔
3182
                    `WHERE (${conditions})`
680✔
3183
                )
680✔
3184
            })
680✔
3185
            .join(" UNION ALL ")
680✔
3186

680✔
3187
        const identityColumnsSql = Object.entries(dbTablesByCatalog)
680✔
3188
            .map(([TABLE_CATALOG, tables]) => {
680✔
3189
                const conditions = tables
680✔
3190
                    .map(
680✔
3191
                        ({ TABLE_NAME, TABLE_SCHEMA }) =>
680✔
3192
                            `("TABLE_SCHEMA" = '${TABLE_SCHEMA}' AND "TABLE_NAME" = '${TABLE_NAME}')`,
680✔
3193
                    )
680✔
3194
                    .join(" OR ")
680✔
3195

680✔
3196
                return (
680✔
3197
                    `SELECT "TABLE_CATALOG", "TABLE_SCHEMA", "COLUMN_NAME", "TABLE_NAME" ` +
680✔
3198
                    `FROM "${TABLE_CATALOG}"."INFORMATION_SCHEMA"."COLUMNS" ` +
680✔
3199
                    `WHERE ` +
680✔
3200
                    `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 ` +
680✔
3201
                    `(${conditions})`
680✔
3202
                )
680✔
3203
            })
680✔
3204
            .join(" UNION ALL ")
680✔
3205

680✔
3206
        const dbCollationsSql = `SELECT "NAME", "COLLATION_NAME" FROM "sys"."databases"`
680✔
3207

680✔
3208
        const indicesSql = Object.entries(dbTablesByCatalog)
680✔
3209
            .map(([TABLE_CATALOG, tables]) => {
680✔
3210
                const conditions = tables
680✔
3211
                    .map(
680✔
3212
                        ({ TABLE_NAME, TABLE_SCHEMA }) =>
680✔
3213
                            `("s"."name" = '${TABLE_SCHEMA}' AND "t"."name" = '${TABLE_NAME}')`,
680✔
3214
                    )
680✔
3215
                    .join(" OR ")
680✔
3216

680✔
3217
                return (
680✔
3218
                    `SELECT '${TABLE_CATALOG}' AS "TABLE_CATALOG", "s"."name" AS "TABLE_SCHEMA", "t"."name" AS "TABLE_NAME", ` +
680✔
3219
                    `"ind"."name" AS "INDEX_NAME", "col"."name" AS "COLUMN_NAME", "ind"."is_unique" AS "IS_UNIQUE", "ind"."filter_definition" as "CONDITION" ` +
680✔
3220
                    `FROM "${TABLE_CATALOG}"."sys"."indexes" "ind" ` +
680✔
3221
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."index_columns" "ic" ON "ic"."object_id" = "ind"."object_id" AND "ic"."index_id" = "ind"."index_id" ` +
680✔
3222
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."columns" "col" ON "col"."object_id" = "ic"."object_id" AND "col"."column_id" = "ic"."column_id" ` +
680✔
3223
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."tables" "t" ON "t"."object_id" = "ind"."object_id" ` +
680✔
3224
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."schemas" "s" ON "s"."schema_id" = "t"."schema_id" ` +
680✔
3225
                    `WHERE ` +
680✔
3226
                    `"ind"."is_primary_key" = 0 AND "ind"."is_unique_constraint" = 0 AND "t"."is_ms_shipped" = 0 AND ` +
680✔
3227
                    `(${conditions})`
680✔
3228
                )
680✔
3229
            })
680✔
3230
            .join(" UNION ALL ")
680✔
3231

680✔
3232
        const [
680✔
3233
            dbColumns,
680✔
3234
            dbConstraints,
680✔
3235
            dbForeignKeys,
680✔
3236
            dbIdentityColumns,
680✔
3237
            dbCollations,
680✔
3238
            dbIndices,
680✔
3239
        ]: ObjectLiteral[][] = await Promise.all([
680✔
3240
            this.query(columnsSql),
680✔
3241
            this.query(constraintsSql),
680✔
3242
            this.query(foreignKeysSql),
680✔
3243
            this.query(identityColumnsSql),
680✔
3244
            this.query(dbCollationsSql),
680✔
3245
            this.query(indicesSql),
680✔
3246
        ])
680✔
3247

680✔
3248
        // create table schemas for loaded tables
680✔
3249
        return await Promise.all(
680✔
3250
            dbTables.map(async (dbTable) => {
680✔
3251
                const table = new Table()
1,004✔
3252

1,004✔
3253
                const getSchemaFromKey = (dbObject: any, key: string) => {
1,004✔
3254
                    return dbObject[key] === currentSchema &&
1,342✔
3255
                        (!this.driver.options.schema ||
1,252✔
3256
                            this.driver.options.schema === currentSchema)
1,252✔
3257
                        ? undefined
1,342✔
3258
                        : dbObject[key]
1,342✔
3259
                }
1,342✔
3260

1,004✔
3261
                // We do not need to join schema and database names, when db or schema is by default.
1,004✔
3262
                const db =
1,004✔
3263
                    dbTable["TABLE_CATALOG"] === currentDatabase
1,004✔
3264
                        ? undefined
1,004✔
3265
                        : dbTable["TABLE_CATALOG"]
1,004✔
3266
                const schema = getSchemaFromKey(dbTable, "TABLE_SCHEMA")
1,004✔
3267
                table.database = dbTable["TABLE_CATALOG"]
1,004✔
3268
                table.schema = dbTable["TABLE_SCHEMA"]
1,004✔
3269
                table.name = this.driver.buildTableName(
1,004✔
3270
                    dbTable["TABLE_NAME"],
1,004✔
3271
                    schema,
1,004✔
3272
                    db,
1,004✔
3273
                )
1,004✔
3274

1,004✔
3275
                const defaultCollation = dbCollations.find(
1,004✔
3276
                    (dbCollation) =>
1,004✔
3277
                        dbCollation["NAME"] === dbTable["TABLE_CATALOG"],
1,004✔
3278
                )!
1,004✔
3279

1,004✔
3280
                // create columns from the loaded columns
1,004✔
3281
                table.columns = await Promise.all(
1,004✔
3282
                    dbColumns
1,004✔
3283
                        .filter(
1,004✔
3284
                            (dbColumn) =>
1,004✔
3285
                                dbColumn["TABLE_NAME"] ===
10,764✔
3286
                                    dbTable["TABLE_NAME"] &&
10,764✔
3287
                                dbColumn["TABLE_SCHEMA"] ===
3,610✔
3288
                                    dbTable["TABLE_SCHEMA"] &&
10,764✔
3289
                                dbColumn["TABLE_CATALOG"] ===
3,610✔
3290
                                    dbTable["TABLE_CATALOG"],
1,004✔
3291
                        )
1,004✔
3292
                        .map(async (dbColumn) => {
1,004✔
3293
                            const columnConstraints = dbConstraints.filter(
3,610✔
3294
                                (dbConstraint) =>
3,610✔
3295
                                    dbConstraint["TABLE_NAME"] ===
18,984✔
3296
                                        dbColumn["TABLE_NAME"] &&
18,984✔
3297
                                    dbConstraint["TABLE_SCHEMA"] ===
8,130✔
3298
                                        dbColumn["TABLE_SCHEMA"] &&
18,984✔
3299
                                    dbConstraint["TABLE_CATALOG"] ===
8,130✔
3300
                                        dbColumn["TABLE_CATALOG"] &&
18,984✔
3301
                                    dbConstraint["COLUMN_NAME"] ===
8,130✔
3302
                                        dbColumn["COLUMN_NAME"],
3,610✔
3303
                            )
3,610✔
3304

3,610✔
3305
                            const uniqueConstraints = columnConstraints.filter(
3,610✔
3306
                                (constraint) =>
3,610✔
3307
                                    constraint["CONSTRAINT_TYPE"] === "UNIQUE",
3,610✔
3308
                            )
3,610✔
3309
                            const isConstraintComposite =
3,610✔
3310
                                uniqueConstraints.every((uniqueConstraint) => {
3,610✔
3311
                                    return dbConstraints.some(
626✔
3312
                                        (dbConstraint) =>
626✔
3313
                                            dbConstraint["CONSTRAINT_TYPE"] ===
2,800✔
3314
                                                "UNIQUE" &&
2,800✔
3315
                                            dbConstraint["CONSTRAINT_NAME"] ===
1,284✔
3316
                                                uniqueConstraint[
1,284✔
3317
                                                    "CONSTRAINT_NAME"
1,284✔
3318
                                                ] &&
2,800✔
3319
                                            dbConstraint["TABLE_SCHEMA"] ===
810✔
3320
                                                dbColumn["TABLE_SCHEMA"] &&
2,800✔
3321
                                            dbConstraint["TABLE_CATALOG"] ===
810✔
3322
                                                dbColumn["TABLE_CATALOG"] &&
2,800✔
3323
                                            dbConstraint["COLUMN_NAME"] !==
810✔
3324
                                                dbColumn["COLUMN_NAME"],
626✔
3325
                                    )
626✔
3326
                                })
3,610✔
3327

3,610✔
3328
                            const isGenerated = !!dbIdentityColumns.find(
3,610✔
3329
                                (column) =>
3,610✔
3330
                                    column["TABLE_NAME"] ===
5,700✔
3331
                                        dbColumn["TABLE_NAME"] &&
5,700✔
3332
                                    column["TABLE_SCHEMA"] ===
1,774✔
3333
                                        dbColumn["TABLE_SCHEMA"] &&
5,700✔
3334
                                    column["TABLE_CATALOG"] ===
1,774✔
3335
                                        dbColumn["TABLE_CATALOG"] &&
5,700✔
3336
                                    column["COLUMN_NAME"] ===
1,774✔
3337
                                        dbColumn["COLUMN_NAME"],
3,610✔
3338
                            )
3,610✔
3339

3,610✔
3340
                            const tableColumn = new TableColumn()
3,610✔
3341
                            tableColumn.name = dbColumn["COLUMN_NAME"]
3,610✔
3342
                            tableColumn.type =
3,610✔
3343
                                dbColumn["DATA_TYPE"].toLowerCase()
3,610✔
3344

3,610✔
3345
                            // check only columns that have length property
3,610✔
3346
                            if (
3,610✔
3347
                                this.driver.withLengthColumnTypes.indexOf(
3,610✔
3348
                                    tableColumn.type as ColumnType,
3,610✔
3349
                                ) !== -1 &&
3,610✔
3350
                                dbColumn["CHARACTER_MAXIMUM_LENGTH"]
1,966✔
3351
                            ) {
3,610✔
3352
                                const length =
1,966✔
3353
                                    dbColumn[
1,966✔
3354
                                        "CHARACTER_MAXIMUM_LENGTH"
1,966✔
3355
                                    ].toString()
1,966✔
3356
                                if (length === "-1") {
1,966✔
3357
                                    tableColumn.length = "MAX"
8✔
3358
                                } else {
1,966✔
3359
                                    if (tableColumn.type === "vector") {
1,958✔
3360
                                        const len = +length
2✔
3361
                                        // NOTE: real returned length is (N*4 + 8) where N is desired dimensions
2✔
3362
                                        if (!Number.isNaN(len)) {
2✔
3363
                                            tableColumn.length = String(
2✔
3364
                                                (len - 8) / 4,
2✔
3365
                                            )
2✔
3366
                                        }
2✔
3367
                                    } else {
1,958✔
3368
                                        tableColumn.length =
1,956✔
3369
                                            !this.isDefaultColumnLength(
1,956✔
3370
                                                table,
1,956✔
3371
                                                tableColumn,
1,956✔
3372
                                                length,
1,956✔
3373
                                            )
1,956✔
3374
                                                ? length
1,956✔
3375
                                                : ""
1,956✔
3376
                                    }
1,956✔
3377
                                }
1,958✔
3378
                            }
1,966✔
3379

3,610✔
3380
                            if (
3,610✔
3381
                                tableColumn.type === "decimal" ||
3,610✔
3382
                                tableColumn.type === "numeric"
3,600✔
3383
                            ) {
3,610✔
3384
                                if (
14✔
3385
                                    dbColumn["NUMERIC_PRECISION"] !== null &&
14✔
3386
                                    !this.isDefaultColumnPrecision(
14✔
3387
                                        table,
14✔
3388
                                        tableColumn,
14✔
3389
                                        dbColumn["NUMERIC_PRECISION"],
14✔
3390
                                    )
14✔
3391
                                )
14✔
3392
                                    tableColumn.precision =
14✔
3393
                                        dbColumn["NUMERIC_PRECISION"]
8✔
3394
                                if (
14✔
3395
                                    dbColumn["NUMERIC_SCALE"] !== null &&
14✔
3396
                                    !this.isDefaultColumnScale(
14✔
3397
                                        table,
14✔
3398
                                        tableColumn,
14✔
3399
                                        dbColumn["NUMERIC_SCALE"],
14✔
3400
                                    )
14✔
3401
                                )
14✔
3402
                                    tableColumn.scale =
14✔
3403
                                        dbColumn["NUMERIC_SCALE"]
8✔
3404
                            }
14✔
3405

3,610✔
3406
                            if (tableColumn.type === "nvarchar") {
3,610✔
3407
                                // Check if this is an enum
1,632✔
3408
                                const columnCheckConstraints =
1,632✔
3409
                                    columnConstraints.filter(
1,632✔
3410
                                        (constraint) =>
1,632✔
3411
                                            constraint["CONSTRAINT_TYPE"] ===
542✔
3412
                                            "CHECK",
1,632✔
3413
                                    )
1,632✔
3414
                                if (columnCheckConstraints.length) {
1,632✔
3415
                                    // const isEnumRegexp = new RegExp("^\\(\\[" + tableColumn.name + "\\]='[^']+'(?: OR \\[" + tableColumn.name + "\\]='[^']+')*\\)$");
22✔
3416
                                    for (const checkConstraint of columnCheckConstraints) {
22✔
3417
                                        if (
22✔
3418
                                            this.isEnumCheckConstraint(
22✔
3419
                                                checkConstraint[
22✔
3420
                                                    "CONSTRAINT_NAME"
22✔
3421
                                                ],
22✔
3422
                                            )
22✔
3423
                                        ) {
22✔
3424
                                            // This is an enum constraint, make column into an enum
10✔
3425
                                            tableColumn.enum = []
10✔
3426
                                            const enumValueRegexp = new RegExp(
10✔
3427
                                                "\\[" +
10✔
3428
                                                    tableColumn.name +
10✔
3429
                                                    "\\]='([^']+)'",
10✔
3430
                                                "g",
10✔
3431
                                            )
10✔
3432
                                            let result
10✔
3433
                                            while (
10✔
3434
                                                (result = enumValueRegexp.exec(
10✔
3435
                                                    checkConstraint[
10✔
3436
                                                        "definition"
10✔
3437
                                                    ],
10✔
3438
                                                )) !== null
10✔
3439
                                            ) {
10✔
3440
                                                tableColumn.enum.unshift(
26✔
3441
                                                    result[1],
26✔
3442
                                                )
26✔
3443
                                            }
26✔
3444
                                            // Skip other column constraints
10✔
3445
                                            break
10✔
3446
                                        }
10✔
3447
                                    }
22✔
3448
                                }
22✔
3449
                            }
1,632✔
3450

3,610✔
3451
                            const primaryConstraint = columnConstraints.find(
3,610✔
3452
                                (constraint) =>
3,610✔
3453
                                    constraint["CONSTRAINT_TYPE"] ===
1,856✔
3454
                                    "PRIMARY KEY",
3,610✔
3455
                            )
3,610✔
3456
                            if (primaryConstraint) {
3,610✔
3457
                                tableColumn.isPrimary = true
1,040✔
3458
                                // find another columns involved in primary key constraint
1,040✔
3459
                                const anotherPrimaryConstraints =
1,040✔
3460
                                    dbConstraints.filter(
1,040✔
3461
                                        (constraint) =>
1,040✔
3462
                                            constraint["TABLE_NAME"] ===
5,484✔
3463
                                                dbColumn["TABLE_NAME"] &&
5,484✔
3464
                                            constraint["TABLE_SCHEMA"] ===
1,982✔
3465
                                                dbColumn["TABLE_SCHEMA"] &&
5,484✔
3466
                                            constraint["TABLE_CATALOG"] ===
1,982✔
3467
                                                dbColumn["TABLE_CATALOG"] &&
5,484✔
3468
                                            constraint["COLUMN_NAME"] !==
1,982✔
3469
                                                dbColumn["COLUMN_NAME"] &&
5,484✔
3470
                                            constraint["CONSTRAINT_TYPE"] ===
922✔
3471
                                                "PRIMARY KEY",
1,040✔
3472
                                    )
1,040✔
3473

1,040✔
3474
                                // collect all column names
1,040✔
3475
                                const columnNames =
1,040✔
3476
                                    anotherPrimaryConstraints.map(
1,040✔
3477
                                        (constraint) =>
1,040✔
3478
                                            constraint["COLUMN_NAME"],
1,040✔
3479
                                    )
1,040✔
3480
                                columnNames.push(dbColumn["COLUMN_NAME"])
1,040✔
3481

1,040✔
3482
                                // build default primary key constraint name
1,040✔
3483
                                const pkName =
1,040✔
3484
                                    this.connection.namingStrategy.primaryKeyName(
1,040✔
3485
                                        table,
1,040✔
3486
                                        columnNames,
1,040✔
3487
                                    )
1,040✔
3488

1,040✔
3489
                                // if primary key has user-defined constraint name, write it in table column
1,040✔
3490
                                if (
1,040✔
3491
                                    primaryConstraint["CONSTRAINT_NAME"] !==
1,040✔
3492
                                    pkName
1,040✔
3493
                                ) {
1,040✔
3494
                                    tableColumn.primaryKeyConstraintName =
52✔
3495
                                        primaryConstraint["CONSTRAINT_NAME"]
52✔
3496
                                }
52✔
3497
                            }
1,040✔
3498

3,610✔
3499
                            tableColumn.default =
3,610✔
3500
                                dbColumn["COLUMN_DEFAULT"] !== null &&
3,610✔
3501
                                dbColumn["COLUMN_DEFAULT"] !== undefined
224✔
3502
                                    ? this.removeParenthesisFromDefault(
3,610✔
3503
                                          dbColumn["COLUMN_DEFAULT"],
224✔
3504
                                      )
3,610✔
3505
                                    : undefined
3,610✔
3506
                            tableColumn.isNullable =
3,610✔
3507
                                dbColumn["IS_NULLABLE"] === "YES"
3,610✔
3508
                            tableColumn.isUnique =
3,610✔
3509
                                uniqueConstraints.length > 0 &&
3,610✔
3510
                                !isConstraintComposite
626✔
3511
                            tableColumn.isGenerated = isGenerated
3,610✔
3512
                            if (isGenerated)
3,610✔
3513
                                tableColumn.generationStrategy = "increment"
3,610✔
3514
                            if (tableColumn.default === "newsequentialid()") {
3,610✔
3515
                                tableColumn.isGenerated = true
14✔
3516
                                tableColumn.generationStrategy = "uuid"
14✔
3517
                                tableColumn.default = undefined
14✔
3518
                            }
14✔
3519

3,610✔
3520
                            // todo: unable to get default charset
3,610✔
3521
                            // tableColumn.charset = dbColumn["CHARACTER_SET_NAME"];
3,610✔
3522
                            if (dbColumn["COLLATION_NAME"])
3,610✔
3523
                                tableColumn.collation =
3,610✔
3524
                                    dbColumn["COLLATION_NAME"] ===
1,918✔
3525
                                    defaultCollation["COLLATION_NAME"]
1,918✔
3526
                                        ? undefined
1,918✔
3527
                                        : dbColumn["COLLATION_NAME"]
1,918✔
3528

3,610✔
3529
                            if (
3,610✔
3530
                                tableColumn.type === "datetime2" ||
3,610✔
3531
                                tableColumn.type === "time" ||
3,610✔
3532
                                tableColumn.type === "datetimeoffset"
3,582✔
3533
                            ) {
3,610✔
3534
                                tableColumn.precision =
32✔
3535
                                    !this.isDefaultColumnPrecision(
32✔
3536
                                        table,
32✔
3537
                                        tableColumn,
32✔
3538
                                        dbColumn["DATETIME_PRECISION"],
32✔
3539
                                    )
32✔
3540
                                        ? dbColumn["DATETIME_PRECISION"]
32✔
3541
                                        : undefined
32✔
3542
                            }
32✔
3543

3,610✔
3544
                            if (
3,610✔
3545
                                dbColumn["is_persisted"] !== null &&
3,610✔
3546
                                dbColumn["is_persisted"] !== undefined &&
3,610✔
3547
                                dbColumn["definition"]
94✔
3548
                            ) {
3,610✔
3549
                                tableColumn.generatedType =
94✔
3550
                                    dbColumn["is_persisted"] === true
94✔
3551
                                        ? "STORED"
94✔
3552
                                        : "VIRTUAL"
94✔
3553
                                // We cannot relay on information_schema.columns.generation_expression, because it is formatted different.
94✔
3554
                                const asExpressionQuery =
94✔
3555
                                    this.selectTypeormMetadataSql({
94✔
3556
                                        database: dbTable["TABLE_CATALOG"],
94✔
3557
                                        schema: dbTable["TABLE_SCHEMA"],
94✔
3558
                                        table: dbTable["TABLE_NAME"],
94✔
3559
                                        type: MetadataTableType.GENERATED_COLUMN,
94✔
3560
                                        name: tableColumn.name,
94✔
3561
                                    })
94✔
3562

94✔
3563
                                const results = await this.query(
94✔
3564
                                    asExpressionQuery.query,
94✔
3565
                                    asExpressionQuery.parameters,
94✔
3566
                                )
94✔
3567
                                if (results[0] && results[0].value) {
94✔
3568
                                    tableColumn.asExpression = results[0].value
94✔
3569
                                } else {
94!
3570
                                    tableColumn.asExpression = ""
×
3571
                                }
×
3572
                            }
94✔
3573

3,610✔
3574
                            return tableColumn
3,610✔
3575
                        }),
1,004✔
3576
                )
1,004✔
3577

1,004✔
3578
                // find unique constraints of table, group them by constraint name and build TableUnique.
1,004✔
3579
                const tableUniqueConstraints = OrmUtils.uniq(
1,004✔
3580
                    dbConstraints.filter(
1,004✔
3581
                        (dbConstraint) =>
1,004✔
3582
                            dbConstraint["TABLE_NAME"] ===
5,374✔
3583
                                dbTable["TABLE_NAME"] &&
5,374✔
3584
                            dbConstraint["TABLE_SCHEMA"] ===
1,876✔
3585
                                dbTable["TABLE_SCHEMA"] &&
5,374✔
3586
                            dbConstraint["TABLE_CATALOG"] ===
1,876✔
3587
                                dbTable["TABLE_CATALOG"] &&
5,374✔
3588
                            dbConstraint["CONSTRAINT_TYPE"] === "UNIQUE",
1,004✔
3589
                    ),
1,004✔
3590
                    (dbConstraint) => dbConstraint["CONSTRAINT_NAME"],
1,004✔
3591
                )
1,004✔
3592

1,004✔
3593
                table.uniques = tableUniqueConstraints.map((constraint) => {
1,004✔
3594
                    const uniques = dbConstraints.filter(
438✔
3595
                        (dbC) =>
438✔
3596
                            dbC["CONSTRAINT_NAME"] ===
2,714✔
3597
                            constraint["CONSTRAINT_NAME"],
438✔
3598
                    )
438✔
3599
                    return new TableUnique({
438✔
3600
                        name: constraint["CONSTRAINT_NAME"],
438✔
3601
                        columnNames: uniques.map((u) => u["COLUMN_NAME"]),
438✔
3602
                    })
438✔
3603
                })
1,004✔
3604

1,004✔
3605
                // find check constraints of table, group them by constraint name and build TableCheck.
1,004✔
3606
                const tableCheckConstraints = OrmUtils.uniq(
1,004✔
3607
                    dbConstraints.filter(
1,004✔
3608
                        (dbConstraint) =>
1,004✔
3609
                            dbConstraint["TABLE_NAME"] ===
5,374✔
3610
                                dbTable["TABLE_NAME"] &&
5,374✔
3611
                            dbConstraint["TABLE_SCHEMA"] ===
1,876✔
3612
                                dbTable["TABLE_SCHEMA"] &&
5,374✔
3613
                            dbConstraint["TABLE_CATALOG"] ===
1,876✔
3614
                                dbTable["TABLE_CATALOG"] &&
5,374✔
3615
                            dbConstraint["CONSTRAINT_TYPE"] === "CHECK",
1,004✔
3616
                    ),
1,004✔
3617
                    (dbConstraint) => dbConstraint["CONSTRAINT_NAME"],
1,004✔
3618
                )
1,004✔
3619

1,004✔
3620
                table.checks = tableCheckConstraints
1,004✔
3621
                    .filter(
1,004✔
3622
                        (constraint) =>
1,004✔
3623
                            !this.isEnumCheckConstraint(
196✔
3624
                                constraint["CONSTRAINT_NAME"],
196✔
3625
                            ),
1,004✔
3626
                    )
1,004✔
3627
                    .map((constraint) => {
1,004✔
3628
                        const checks = dbConstraints.filter(
186✔
3629
                            (dbC) =>
186✔
3630
                                dbC["CONSTRAINT_NAME"] ===
1,278✔
3631
                                constraint["CONSTRAINT_NAME"],
186✔
3632
                        )
186✔
3633
                        return new TableCheck({
186✔
3634
                            name: constraint["CONSTRAINT_NAME"],
186✔
3635
                            columnNames: checks.map((c) => c["COLUMN_NAME"]),
186✔
3636
                            expression: constraint["definition"],
186✔
3637
                        })
186✔
3638
                    })
1,004✔
3639

1,004✔
3640
                // find foreign key constraints of table, group them by constraint name and build TableForeignKey.
1,004✔
3641
                const tableForeignKeyConstraints = OrmUtils.uniq(
1,004✔
3642
                    dbForeignKeys.filter(
1,004✔
3643
                        (dbForeignKey) =>
1,004✔
3644
                            dbForeignKey["TABLE_NAME"] ===
1,280✔
3645
                                dbTable["TABLE_NAME"] &&
1,280✔
3646
                            dbForeignKey["TABLE_SCHEMA"] ===
346✔
3647
                                dbTable["TABLE_SCHEMA"] &&
1,280✔
3648
                            dbForeignKey["TABLE_CATALOG"] ===
346✔
3649
                                dbTable["TABLE_CATALOG"],
1,004✔
3650
                    ),
1,004✔
3651
                    (dbForeignKey) => dbForeignKey["FK_NAME"],
1,004✔
3652
                )
1,004✔
3653

1,004✔
3654
                table.foreignKeys = tableForeignKeyConstraints.map(
1,004✔
3655
                    (dbForeignKey) => {
1,004✔
3656
                        const foreignKeys = dbForeignKeys.filter(
338✔
3657
                            (dbFk) =>
338✔
3658
                                dbFk["FK_NAME"] === dbForeignKey["FK_NAME"],
338✔
3659
                        )
338✔
3660

338✔
3661
                        // if referenced table located in currently used db and schema, we don't need to concat db and schema names to table name.
338✔
3662
                        const db =
338✔
3663
                            dbForeignKey["TABLE_CATALOG"] === currentDatabase
338✔
3664
                                ? undefined
338✔
3665
                                : dbForeignKey["TABLE_CATALOG"]
338✔
3666
                        const schema = getSchemaFromKey(
338✔
3667
                            dbForeignKey,
338✔
3668
                            "REF_SCHEMA",
338✔
3669
                        )
338✔
3670
                        const referencedTableName = this.driver.buildTableName(
338✔
3671
                            dbForeignKey["REF_TABLE"],
338✔
3672
                            schema,
338✔
3673
                            db,
338✔
3674
                        )
338✔
3675

338✔
3676
                        return new TableForeignKey({
338✔
3677
                            name: dbForeignKey["FK_NAME"],
338✔
3678
                            columnNames: foreignKeys.map(
338✔
3679
                                (dbFk) => dbFk["COLUMN_NAME"],
338✔
3680
                            ),
338✔
3681
                            referencedDatabase: dbForeignKey["TABLE_CATALOG"],
338✔
3682
                            referencedSchema: dbForeignKey["REF_SCHEMA"],
338✔
3683
                            referencedTableName: referencedTableName,
338✔
3684
                            referencedColumnNames: foreignKeys.map(
338✔
3685
                                (dbFk) => dbFk["REF_COLUMN"],
338✔
3686
                            ),
338✔
3687
                            onDelete: dbForeignKey["ON_DELETE"].replace(
338✔
3688
                                "_",
338✔
3689
                                " ",
338✔
3690
                            ), // SqlServer returns NO_ACTION, instead of NO ACTION
338✔
3691
                            onUpdate: dbForeignKey["ON_UPDATE"].replace(
338✔
3692
                                "_",
338✔
3693
                                " ",
338✔
3694
                            ), // SqlServer returns NO_ACTION, instead of NO ACTION
338✔
3695
                        })
338✔
3696
                    },
1,004✔
3697
                )
1,004✔
3698

1,004✔
3699
                // find index constraints of table, group them by constraint name and build TableIndex.
1,004✔
3700
                const tableIndexConstraints = OrmUtils.uniq(
1,004✔
3701
                    dbIndices.filter(
1,004✔
3702
                        (dbIndex) =>
1,004✔
3703
                            dbIndex["TABLE_NAME"] === dbTable["TABLE_NAME"] &&
722✔
3704
                            dbIndex["TABLE_SCHEMA"] ===
332✔
3705
                                dbTable["TABLE_SCHEMA"] &&
722✔
3706
                            dbIndex["TABLE_CATALOG"] ===
332✔
3707
                                dbTable["TABLE_CATALOG"],
1,004✔
3708
                    ),
1,004✔
3709
                    (dbIndex) => dbIndex["INDEX_NAME"],
1,004✔
3710
                )
1,004✔
3711

1,004✔
3712
                table.indices = tableIndexConstraints.map((constraint) => {
1,004✔
3713
                    const indices = dbIndices.filter((index) => {
288✔
3714
                        return (
964✔
3715
                            index["TABLE_CATALOG"] ===
964✔
3716
                                constraint["TABLE_CATALOG"] &&
964✔
3717
                            index["TABLE_SCHEMA"] ===
964✔
3718
                                constraint["TABLE_SCHEMA"] &&
964✔
3719
                            index["TABLE_NAME"] === constraint["TABLE_NAME"] &&
964✔
3720
                            index["INDEX_NAME"] === constraint["INDEX_NAME"]
694✔
3721
                        )
964✔
3722
                    })
288✔
3723
                    return new TableIndex(<TableIndexOptions>{
288✔
3724
                        table: table,
288✔
3725
                        name: constraint["INDEX_NAME"],
288✔
3726
                        columnNames: indices.map((i) => i["COLUMN_NAME"]),
288✔
3727
                        isUnique: constraint["IS_UNIQUE"],
288✔
3728
                        where: constraint["CONDITION"],
288✔
3729
                    })
288✔
3730
                })
1,004✔
3731

1,004✔
3732
                return table
1,004✔
3733
            }),
680✔
3734
        )
680✔
3735
    }
680✔
3736

28✔
3737
    /**
28✔
3738
     * Builds and returns SQL for create table.
28✔
3739
     * @param table
28✔
3740
     * @param createForeignKeys
28✔
3741
     */
28✔
3742
    protected createTableSql(table: Table, createForeignKeys?: boolean): Query {
28✔
3743
        const columnDefinitions = table.columns
10,418✔
3744
            .map((column) =>
10,418✔
3745
                this.buildCreateColumnSql(table, column, false, true),
10,418✔
3746
            )
10,418✔
3747
            .join(", ")
10,418✔
3748
        let sql = `CREATE TABLE ${this.escapePath(table)} (${columnDefinitions}`
10,418✔
3749

10,418✔
3750
        table.columns
10,418✔
3751
            .filter((column) => column.isUnique)
10,418✔
3752
            .forEach((column) => {
10,418✔
3753
                const isUniqueExist = table.uniques.some(
728✔
3754
                    (unique) =>
728✔
3755
                        unique.columnNames.length === 1 &&
882✔
3756
                        unique.columnNames[0] === column.name,
728✔
3757
                )
728✔
3758
                if (!isUniqueExist)
728✔
3759
                    table.uniques.push(
728✔
3760
                        new TableUnique({
8✔
3761
                            name: this.connection.namingStrategy.uniqueConstraintName(
8✔
3762
                                table,
8✔
3763
                                [column.name],
8✔
3764
                            ),
8✔
3765
                            columnNames: [column.name],
8✔
3766
                        }),
8✔
3767
                    )
8✔
3768
            })
10,418✔
3769

10,418✔
3770
        if (table.uniques.length > 0) {
10,418✔
3771
            const uniquesSql = table.uniques
662✔
3772
                .map((unique) => {
662✔
3773
                    const uniqueName = unique.name
972✔
3774
                        ? unique.name
972✔
3775
                        : this.connection.namingStrategy.uniqueConstraintName(
972✔
3776
                              table,
4✔
3777
                              unique.columnNames,
4✔
3778
                          )
972✔
3779
                    const columnNames = unique.columnNames
972✔
3780
                        .map((columnName) => `"${columnName}"`)
972✔
3781
                        .join(", ")
972✔
3782
                    return `CONSTRAINT "${uniqueName}" UNIQUE (${columnNames})`
972✔
3783
                })
662✔
3784
                .join(", ")
662✔
3785

662✔
3786
            sql += `, ${uniquesSql}`
662✔
3787
        }
662✔
3788

10,418✔
3789
        if (table.checks.length > 0) {
10,418✔
3790
            const checksSql = table.checks
164✔
3791
                .map((check) => {
164✔
3792
                    const checkName = check.name
166✔
3793
                        ? check.name
166✔
3794
                        : this.connection.namingStrategy.checkConstraintName(
166✔
3795
                              table,
2✔
3796
                              check.expression!,
2✔
3797
                          )
166✔
3798
                    return `CONSTRAINT "${checkName}" CHECK (${check.expression})`
166✔
3799
                })
164✔
3800
                .join(", ")
164✔
3801

164✔
3802
            sql += `, ${checksSql}`
164✔
3803
        }
164✔
3804

10,418✔
3805
        if (table.foreignKeys.length > 0 && createForeignKeys) {
10,418✔
3806
            const foreignKeysSql = table.foreignKeys
8✔
3807
                .map((fk) => {
8✔
3808
                    const columnNames = fk.columnNames
10✔
3809
                        .map((columnName) => `"${columnName}"`)
10✔
3810
                        .join(", ")
10✔
3811
                    if (!fk.name)
10✔
3812
                        fk.name = this.connection.namingStrategy.foreignKeyName(
10✔
3813
                            table,
6✔
3814
                            fk.columnNames,
6✔
3815
                            this.getTablePath(fk),
6✔
3816
                            fk.referencedColumnNames,
6✔
3817
                        )
6✔
3818
                    const referencedColumnNames = fk.referencedColumnNames
10✔
3819
                        .map((columnName) => `"${columnName}"`)
10✔
3820
                        .join(", ")
10✔
3821

10✔
3822
                    let constraint = `CONSTRAINT "${
10✔
3823
                        fk.name
10✔
3824
                    }" FOREIGN KEY (${columnNames}) REFERENCES ${this.escapePath(
10✔
3825
                        this.getTablePath(fk),
10✔
3826
                    )} (${referencedColumnNames})`
10✔
3827
                    if (fk.onDelete) constraint += ` ON DELETE ${fk.onDelete}`
10✔
3828
                    if (fk.onUpdate) constraint += ` ON UPDATE ${fk.onUpdate}`
10✔
3829

10✔
3830
                    return constraint
10✔
3831
                })
8✔
3832
                .join(", ")
8✔
3833

8✔
3834
            sql += `, ${foreignKeysSql}`
8✔
3835
        }
8✔
3836

10,418✔
3837
        const primaryColumns = table.columns.filter(
10,418✔
3838
            (column) => column.isPrimary,
10,418✔
3839
        )
10,418✔
3840
        if (primaryColumns.length > 0) {
10,418✔
3841
            const primaryKeyName = primaryColumns[0].primaryKeyConstraintName
10,392✔
3842
                ? primaryColumns[0].primaryKeyConstraintName
10,392✔
3843
                : this.connection.namingStrategy.primaryKeyName(
10,392✔
3844
                      table,
10,360✔
3845
                      primaryColumns.map((column) => column.name),
10,360✔
3846
                  )
10,392✔
3847

10,392✔
3848
            const columnNames = primaryColumns
10,392✔
3849
                .map((column) => `"${column.name}"`)
10,392✔
3850
                .join(", ")
10,392✔
3851
            sql += `, CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNames})`
10,392✔
3852
        }
10,392✔
3853

10,418✔
3854
        sql += `)`
10,418✔
3855

10,418✔
3856
        return new Query(sql)
10,418✔
3857
    }
10,418✔
3858

28✔
3859
    /**
28✔
3860
     * Builds drop table sql.
28✔
3861
     * @param tableOrName
28✔
3862
     * @param ifExists
28✔
3863
     */
28✔
3864
    protected dropTableSql(
28✔
3865
        tableOrName: Table | string,
10,418✔
3866
        ifExists?: boolean,
10,418✔
3867
    ): Query {
10,418✔
3868
        const query = ifExists
10,418✔
3869
            ? `DROP TABLE IF EXISTS ${this.escapePath(tableOrName)}`
10,418!
3870
            : `DROP TABLE ${this.escapePath(tableOrName)}`
10,418✔
3871
        return new Query(query)
10,418✔
3872
    }
10,418✔
3873

28✔
3874
    protected createViewSql(view: View): Query {
28✔
3875
        const parsedName = this.driver.parseTableName(view)
16✔
3876

16✔
3877
        // Can't use `escapePath` here because `CREATE VIEW` does not accept database names.
16✔
3878
        const viewIdentifier = parsedName.schema
16✔
3879
            ? `"${parsedName.schema}"."${parsedName.tableName}"`
16✔
3880
            : `"${parsedName.tableName}"`
16!
3881

16✔
3882
        if (typeof view.expression === "string") {
16✔
3883
            return new Query(
4✔
3884
                `CREATE VIEW ${viewIdentifier} AS ${view.expression}`,
4✔
3885
            )
4✔
3886
        } else {
16✔
3887
            return new Query(
12✔
3888
                `CREATE VIEW ${viewIdentifier} AS ${view
12✔
3889
                    .expression(this.connection)
12✔
3890
                    .getQuery()}`,
12✔
3891
            )
12✔
3892
        }
12✔
3893
    }
16✔
3894

28✔
3895
    protected async insertViewDefinitionSql(view: View): Promise<Query> {
28✔
3896
        const parsedTableName = this.driver.parseTableName(view)
16✔
3897

16✔
3898
        if (!parsedTableName.schema) {
16!
3899
            parsedTableName.schema = await this.getCurrentSchema()
×
3900
        }
×
3901

16✔
3902
        const expression =
16✔
3903
            typeof view.expression === "string"
16✔
3904
                ? view.expression.trim()
16✔
3905
                : view.expression(this.connection).getQuery()
16✔
3906
        return this.insertTypeormMetadataSql({
16✔
3907
            type: MetadataTableType.VIEW,
16✔
3908
            database: parsedTableName.database,
16✔
3909
            schema: parsedTableName.schema,
16✔
3910
            name: parsedTableName.tableName,
16✔
3911
            value: expression,
16✔
3912
        })
16✔
3913
    }
16✔
3914

28✔
3915
    /**
28✔
3916
     * Builds drop view sql.
28✔
3917
     * @param viewOrPath
28✔
3918
     * @param ifExists
28✔
3919
     */
28✔
3920
    protected dropViewSql(
28✔
3921
        viewOrPath: View | string,
16✔
3922
        ifExists?: boolean,
16✔
3923
    ): Query {
16✔
3924
        const query = ifExists
16✔
3925
            ? `DROP VIEW IF EXISTS ${this.escapePath(viewOrPath)}`
16!
3926
            : `DROP VIEW ${this.escapePath(viewOrPath)}`
16✔
3927
        return new Query(query)
16✔
3928
    }
16✔
3929

28✔
3930
    /**
28✔
3931
     * Builds remove view sql.
28✔
3932
     * @param viewOrPath
28✔
3933
     */
28✔
3934
    protected async deleteViewDefinitionSql(
28✔
3935
        viewOrPath: View | string,
16✔
3936
    ): Promise<Query> {
16✔
3937
        const parsedTableName = this.driver.parseTableName(viewOrPath)
16✔
3938

16✔
3939
        if (!parsedTableName.schema) {
16!
3940
            parsedTableName.schema = await this.getCurrentSchema()
×
3941
        }
×
3942

16✔
3943
        return this.deleteTypeormMetadataSql({
16✔
3944
            type: MetadataTableType.VIEW,
16✔
3945
            database: parsedTableName.database,
16✔
3946
            schema: parsedTableName.schema,
16✔
3947
            name: parsedTableName.tableName,
16✔
3948
        })
16✔
3949
    }
16✔
3950

28✔
3951
    /**
28✔
3952
     * Builds create index sql.
28✔
3953
     * @param table
28✔
3954
     * @param index
28✔
3955
     */
28✔
3956
    protected createIndexSql(table: Table, index: TableIndex): Query {
28✔
3957
        const columns = index.columnNames
4,392✔
3958
            .map((columnName) => `"${columnName}"`)
4,392✔
3959
            .join(", ")
4,392✔
3960
        return new Query(
4,392✔
3961
            `CREATE ${index.isUnique ? "UNIQUE " : ""}INDEX "${
4,392✔
3962
                index.name
4,392✔
3963
            }" ON ${this.escapePath(table)} (${columns}) ${
4,392✔
3964
                index.where ? "WHERE " + index.where : ""
4,392✔
3965
            }`,
4,392✔
3966
        )
4,392✔
3967
    }
4,392✔
3968

28✔
3969
    /**
28✔
3970
     * Builds drop index sql.
28✔
3971
     * @param table
28✔
3972
     * @param indexOrName
28✔
3973
     */
28✔
3974
    protected dropIndexSql(
28✔
3975
        table: Table,
4,392✔
3976
        indexOrName: TableIndex | string,
4,392✔
3977
    ): Query {
4,392✔
3978
        const indexName = InstanceChecker.isTableIndex(indexOrName)
4,392✔
3979
            ? indexOrName.name
4,392✔
3980
            : indexOrName
4,392!
3981
        return new Query(
4,392✔
3982
            `DROP INDEX "${indexName}" ON ${this.escapePath(table)}`,
4,392✔
3983
        )
4,392✔
3984
    }
4,392✔
3985

28✔
3986
    /**
28✔
3987
     * Builds create primary key sql.
28✔
3988
     * @param table
28✔
3989
     * @param columnNames
28✔
3990
     * @param constraintName
28✔
3991
     */
28✔
3992
    protected createPrimaryKeySql(
28✔
3993
        table: Table,
12✔
3994
        columnNames: string[],
12✔
3995
        constraintName?: string,
12✔
3996
    ): Query {
12✔
3997
        const primaryKeyName = constraintName
12✔
3998
            ? constraintName
12✔
3999
            : this.connection.namingStrategy.primaryKeyName(table, columnNames)
12✔
4000

12✔
4001
        const columnNamesString = columnNames
12✔
4002
            .map((columnName) => `"${columnName}"`)
12✔
4003
            .join(", ")
12✔
4004
        return new Query(
12✔
4005
            `ALTER TABLE ${this.escapePath(
12✔
4006
                table,
12✔
4007
            )} ADD CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNamesString})`,
12✔
4008
        )
12✔
4009
    }
12✔
4010

28✔
4011
    /**
28✔
4012
     * Builds drop primary key sql.
28✔
4013
     * @param table
28✔
4014
     */
28✔
4015
    protected dropPrimaryKeySql(table: Table): Query {
28✔
4016
        const columnNames = table.primaryColumns.map((column) => column.name)
12✔
4017
        const constraintName = table.primaryColumns[0].primaryKeyConstraintName
12✔
4018
        const primaryKeyName = constraintName
12✔
4019
            ? constraintName
12!
4020
            : this.connection.namingStrategy.primaryKeyName(table, columnNames)
12✔
4021

12✔
4022
        return new Query(
12✔
4023
            `ALTER TABLE ${this.escapePath(
12✔
4024
                table,
12✔
4025
            )} DROP CONSTRAINT "${primaryKeyName}"`,
12✔
4026
        )
12✔
4027
    }
12✔
4028

28✔
4029
    /**
28✔
4030
     * Builds create unique constraint sql.
28✔
4031
     * @param table
28✔
4032
     * @param uniqueConstraint
28✔
4033
     */
28✔
4034
    protected createUniqueConstraintSql(
28✔
4035
        table: Table,
38✔
4036
        uniqueConstraint: TableUnique,
38✔
4037
    ): Query {
38✔
4038
        const columnNames = uniqueConstraint.columnNames
38✔
4039
            .map((column) => `"` + column + `"`)
38✔
4040
            .join(", ")
38✔
4041
        return new Query(
38✔
4042
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
38✔
4043
                uniqueConstraint.name
38✔
4044
            }" UNIQUE (${columnNames})`,
38✔
4045
        )
38✔
4046
    }
38✔
4047

28✔
4048
    /**
28✔
4049
     * Builds drop unique constraint sql.
28✔
4050
     * @param table
28✔
4051
     * @param uniqueOrName
28✔
4052
     */
28✔
4053
    protected dropUniqueConstraintSql(
28✔
4054
        table: Table,
38✔
4055
        uniqueOrName: TableUnique | string,
38✔
4056
    ): Query {
38✔
4057
        const uniqueName = InstanceChecker.isTableUnique(uniqueOrName)
38✔
4058
            ? uniqueOrName.name
38✔
4059
            : uniqueOrName
38!
4060
        return new Query(
38✔
4061
            `ALTER TABLE ${this.escapePath(
38✔
4062
                table,
38✔
4063
            )} DROP CONSTRAINT "${uniqueName}"`,
38✔
4064
        )
38✔
4065
    }
38✔
4066

28✔
4067
    /**
28✔
4068
     * Builds create check constraint sql.
28✔
4069
     * @param table
28✔
4070
     * @param checkConstraint
28✔
4071
     */
28✔
4072
    protected createCheckConstraintSql(
28✔
4073
        table: Table,
24✔
4074
        checkConstraint: TableCheck,
24✔
4075
    ): Query {
24✔
4076
        return new Query(
24✔
4077
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
24✔
4078
                checkConstraint.name
24✔
4079
            }" CHECK (${checkConstraint.expression})`,
24✔
4080
        )
24✔
4081
    }
24✔
4082

28✔
4083
    /**
28✔
4084
     * Builds drop check constraint sql.
28✔
4085
     * @param table
28✔
4086
     * @param checkOrName
28✔
4087
     */
28✔
4088
    protected dropCheckConstraintSql(
28✔
4089
        table: Table,
24✔
4090
        checkOrName: TableCheck | string,
24✔
4091
    ): Query {
24✔
4092
        const checkName = InstanceChecker.isTableCheck(checkOrName)
24✔
4093
            ? checkOrName.name
24✔
4094
            : checkOrName
24!
4095
        return new Query(
24✔
4096
            `ALTER TABLE ${this.escapePath(
24✔
4097
                table,
24✔
4098
            )} DROP CONSTRAINT "${checkName}"`,
24✔
4099
        )
24✔
4100
    }
24✔
4101

28✔
4102
    /**
28✔
4103
     * Builds create foreign key sql.
28✔
4104
     * @param table
28✔
4105
     * @param foreignKey
28✔
4106
     */
28✔
4107
    protected createForeignKeySql(
28✔
4108
        table: Table,
6,806✔
4109
        foreignKey: TableForeignKey,
6,806✔
4110
    ): Query {
6,806✔
4111
        const columnNames = foreignKey.columnNames
6,806✔
4112
            .map((column) => `"` + column + `"`)
6,806✔
4113
            .join(", ")
6,806✔
4114
        const referencedColumnNames = foreignKey.referencedColumnNames
6,806✔
4115
            .map((column) => `"` + column + `"`)
6,806✔
4116
            .join(",")
6,806✔
4117
        let sql =
6,806✔
4118
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
6,806✔
4119
                foreignKey.name
6,806✔
4120
            }" FOREIGN KEY (${columnNames}) ` +
6,806✔
4121
            `REFERENCES ${this.escapePath(
6,806✔
4122
                this.getTablePath(foreignKey),
6,806✔
4123
            )}(${referencedColumnNames})`
6,806✔
4124
        if (foreignKey.onDelete) sql += ` ON DELETE ${foreignKey.onDelete}`
6,806✔
4125
        if (foreignKey.onUpdate) sql += ` ON UPDATE ${foreignKey.onUpdate}`
6,806✔
4126

6,806✔
4127
        return new Query(sql)
6,806✔
4128
    }
6,806✔
4129

28✔
4130
    /**
28✔
4131
     * Builds drop foreign key sql.
28✔
4132
     * @param table
28✔
4133
     * @param foreignKeyOrName
28✔
4134
     */
28✔
4135
    protected dropForeignKeySql(
28✔
4136
        table: Table,
6,816✔
4137
        foreignKeyOrName: TableForeignKey | string,
6,816✔
4138
    ): Query {
6,816✔
4139
        const foreignKeyName = InstanceChecker.isTableForeignKey(
6,816✔
4140
            foreignKeyOrName,
6,816✔
4141
        )
6,816✔
4142
            ? foreignKeyOrName.name
6,816✔
4143
            : foreignKeyOrName
6,816!
4144
        return new Query(
6,816✔
4145
            `ALTER TABLE ${this.escapePath(
6,816✔
4146
                table,
6,816✔
4147
            )} DROP CONSTRAINT "${foreignKeyName}"`,
6,816✔
4148
        )
6,816✔
4149
    }
6,816✔
4150

28✔
4151
    /**
28✔
4152
     * Escapes given table or View path.
28✔
4153
     * @param target
28✔
4154
     */
28✔
4155
    protected escapePath(target: Table | View | string): string {
28✔
4156
        const { database, schema, tableName } =
50,778✔
4157
            this.driver.parseTableName(target)
50,778✔
4158

50,778✔
4159
        if (database && database !== this.driver.database) {
50,778✔
4160
            if (schema && schema !== this.driver.searchSchema) {
96✔
4161
                return `"${database}"."${schema}"."${tableName}"`
84✔
4162
            }
84✔
4163

12✔
4164
            return `"${database}".."${tableName}"`
12✔
4165
        }
12✔
4166

50,682✔
4167
        if (schema && schema !== this.driver.searchSchema) {
50,778✔
4168
            return `"${schema}"."${tableName}"`
148✔
4169
        }
148✔
4170

50,534✔
4171
        return `"${tableName}"`
50,534✔
4172
    }
50,534✔
4173

28✔
4174
    /**
28✔
4175
     * Concat database name and schema name to the foreign key name.
28✔
4176
     * Needs because FK name is relevant to the schema and database.
28✔
4177
     * @param fkName
28✔
4178
     * @param schemaName
28✔
4179
     * @param dbName
28✔
4180
     */
28✔
4181
    protected buildForeignKeyName(
28✔
4182
        fkName: string,
8✔
4183
        schemaName: string | undefined,
8✔
4184
        dbName: string | undefined,
8✔
4185
    ): string {
8✔
4186
        let joinedFkName = fkName
8✔
4187
        if (schemaName && schemaName !== this.driver.searchSchema)
8✔
4188
            joinedFkName = schemaName + "." + joinedFkName
8✔
4189
        if (dbName && dbName !== this.driver.database)
8✔
4190
            joinedFkName = dbName + "." + joinedFkName
8✔
4191

8✔
4192
        return joinedFkName
8✔
4193
    }
8✔
4194

28✔
4195
    /**
28✔
4196
     * Removes parenthesis around default value.
28✔
4197
     * Sql server returns default value with parenthesis around, e.g.
28✔
4198
     *  ('My text') - for string
28✔
4199
     *  ((1)) - for number
28✔
4200
     *  (newsequentialId()) - for function
28✔
4201
     * @param defaultValue
28✔
4202
     */
28✔
4203
    protected removeParenthesisFromDefault(defaultValue: string): any {
28✔
4204
        if (defaultValue.substr(0, 1) !== "(") return defaultValue
452✔
4205
        const normalizedDefault = defaultValue.substr(
228✔
4206
            1,
228✔
4207
            defaultValue.lastIndexOf(")") - 1,
228✔
4208
        )
228✔
4209
        return this.removeParenthesisFromDefault(normalizedDefault)
228✔
4210
    }
228✔
4211

28✔
4212
    /**
28✔
4213
     * Builds a query for create column.
28✔
4214
     * @param table
28✔
4215
     * @param column
28✔
4216
     * @param skipIdentity
28✔
4217
     * @param createDefault
28✔
4218
     * @param skipEnum
28✔
4219
     */
28✔
4220
    protected buildCreateColumnSql(
28✔
4221
        table: Table,
33,880✔
4222
        column: TableColumn,
33,880✔
4223
        skipIdentity: boolean,
33,880✔
4224
        createDefault: boolean,
33,880✔
4225
        skipEnum?: boolean,
33,880✔
4226
    ) {
33,880✔
4227
        let c = `"${column.name}" ${this.connection.driver.createFullType(
33,880✔
4228
            column,
33,880✔
4229
        )}`
33,880✔
4230

33,880✔
4231
        if (!skipEnum && column.enum) {
33,880✔
4232
            const expression = this.getEnumExpression(column)
48✔
4233
            const checkName =
48✔
4234
                this.connection.namingStrategy.checkConstraintName(
48✔
4235
                    table,
48✔
4236
                    expression,
48✔
4237
                    true,
48✔
4238
                )
48✔
4239
            c += ` CONSTRAINT ${checkName} CHECK(${expression})`
48✔
4240
        }
48✔
4241

33,880✔
4242
        if (column.collation) c += " COLLATE " + column.collation
33,880✔
4243

33,880✔
4244
        if (column.asExpression) {
33,880✔
4245
            c += ` AS (${column.asExpression})`
78✔
4246
            if (column.generatedType === "STORED") {
78✔
4247
                c += ` PERSISTED`
42✔
4248

42✔
4249
                // NOT NULL can be specified for computed columns only if PERSISTED is also specified
42✔
4250
                if (column.isNullable !== true) c += " NOT NULL"
42✔
4251
            }
42✔
4252
        } else {
33,880✔
4253
            if (column.isNullable !== true) c += " NOT NULL"
33,802✔
4254
        }
33,802✔
4255

33,880✔
4256
        if (
33,880✔
4257
            column.isGenerated === true &&
33,880✔
4258
            column.generationStrategy === "increment" &&
33,880✔
4259
            !skipIdentity
6,276✔
4260
        )
33,880✔
4261
            // don't use skipPrimary here since updates can update already exist primary without auto inc.
33,880✔
4262
            c += " IDENTITY(1,1)"
33,880✔
4263

33,880✔
4264
        if (
33,880✔
4265
            column.default !== undefined &&
33,880✔
4266
            column.default !== null &&
33,880✔
4267
            createDefault
2,022✔
4268
        ) {
33,880✔
4269
            // we create named constraint to be able to delete this constraint when column been dropped
2,010✔
4270
            const defaultName =
2,010✔
4271
                this.connection.namingStrategy.defaultConstraintName(
2,010✔
4272
                    table,
2,010✔
4273
                    column.name,
2,010✔
4274
                )
2,010✔
4275
            c += ` CONSTRAINT "${defaultName}" DEFAULT ${column.default}`
2,010✔
4276
        }
2,010✔
4277

33,880✔
4278
        if (
33,880✔
4279
            column.isGenerated &&
33,880✔
4280
            column.generationStrategy === "uuid" &&
33,880✔
4281
            !column.default
240✔
4282
        ) {
33,880✔
4283
            // we create named constraint to be able to delete this constraint when column been dropped
240✔
4284
            const defaultName =
240✔
4285
                this.connection.namingStrategy.defaultConstraintName(
240✔
4286
                    table,
240✔
4287
                    column.name,
240✔
4288
                )
240✔
4289
            c += ` CONSTRAINT "${defaultName}" DEFAULT NEWSEQUENTIALID()`
240✔
4290
        }
240✔
4291
        return c
33,880✔
4292
    }
33,880✔
4293

28✔
4294
    private getEnumExpression(column: TableColumn) {
28✔
4295
        if (!column.enum) {
52!
4296
            throw new Error(`Enum is not defined in column ${column.name}`)
×
4297
        }
×
4298
        return (
52✔
4299
            column.name +
52✔
4300
            " IN (" +
52✔
4301
            column.enum.map((val) => "'" + val + "'").join(",") +
52✔
4302
            ")"
52✔
4303
        )
52✔
4304
    }
52✔
4305

28✔
4306
    protected isEnumCheckConstraint(name: string): boolean {
28✔
4307
        return name.indexOf("CHK_") !== -1 && name.indexOf("_ENUM") !== -1
218✔
4308
    }
218✔
4309

28✔
4310
    /**
28✔
4311
     * Converts MssqlParameter into real mssql parameter type.
28✔
4312
     * @param parameter
28✔
4313
     */
28✔
4314
    protected mssqlParameterToNativeParameter(parameter: MssqlParameter): any {
28✔
4315
        switch (this.driver.normalizeType({ type: parameter.type as any })) {
53,014✔
4316
            case "bit":
53,014✔
4317
                return this.driver.mssql.Bit
3,744✔
4318
            case "bigint":
53,014✔
4319
                return this.driver.mssql.BigInt
64✔
4320
            case "decimal":
53,014✔
4321
                return this.driver.mssql.Decimal(...parameter.params)
8✔
4322
            case "float":
53,014✔
4323
                return this.driver.mssql.Float
60✔
4324
            case "int":
53,014✔
4325
                return this.driver.mssql.Int
24,226✔
4326
            case "money":
53,014✔
4327
                return this.driver.mssql.Money
2✔
4328
            case "numeric":
53,014✔
4329
                return this.driver.mssql.Numeric(...parameter.params)
4✔
4330
            case "smallint":
53,014✔
4331
                return this.driver.mssql.SmallInt
2✔
4332
            case "smallmoney":
53,014✔
4333
                return this.driver.mssql.SmallMoney
2✔
4334
            case "real":
53,014✔
4335
                return this.driver.mssql.Real
2✔
4336
            case "tinyint":
53,014✔
4337
                return this.driver.mssql.TinyInt
2✔
4338
            case "char":
53,014✔
4339
                if (
10✔
4340
                    this.driver.options.options
10✔
4341
                        ?.disableAsciiToUnicodeParamConversion
10✔
4342
                ) {
10✔
4343
                    return this.driver.mssql.Char(...parameter.params)
2✔
4344
                }
2✔
4345
                return this.driver.mssql.NChar(...parameter.params)
8✔
4346
            case "nchar":
53,014✔
4347
                return this.driver.mssql.NChar(...parameter.params)
4✔
4348
            case "text":
53,014✔
4349
                if (
10✔
4350
                    this.driver.options.options
10✔
4351
                        ?.disableAsciiToUnicodeParamConversion
10!
4352
                ) {
10!
4353
                    return this.driver.mssql.Text
×
4354
                }
×
4355
                return this.driver.mssql.Ntext
10✔
4356
            case "ntext":
53,014✔
4357
                return this.driver.mssql.Ntext
14✔
4358
            case "varchar":
53,014✔
4359
                if (
240✔
4360
                    this.driver.options.options
240✔
4361
                        ?.disableAsciiToUnicodeParamConversion
240✔
4362
                ) {
240✔
4363
                    return this.driver.mssql.VarChar(...parameter.params)
2✔
4364
                }
2✔
4365
                return this.driver.mssql.NVarChar(...parameter.params)
238✔
4366
            case "nvarchar":
53,014✔
4367
                return this.driver.mssql.NVarChar(...parameter.params)
24,174✔
4368
            case "xml":
53,014!
4369
                return this.driver.mssql.Xml
×
4370
            case "time":
53,014✔
4371
                return this.driver.mssql.Time(...parameter.params)
10✔
4372
            case "date":
53,014✔
4373
                return this.driver.mssql.Date
10✔
4374
            case "datetime":
53,014✔
4375
                return this.driver.mssql.DateTime
48✔
4376
            case "datetime2":
53,014✔
4377
                return this.driver.mssql.DateTime2(...parameter.params)
24✔
4378
            case "datetimeoffset":
53,014✔
4379
                return this.driver.mssql.DateTimeOffset(...parameter.params)
8✔
4380
            case "smalldatetime":
53,014✔
4381
                return this.driver.mssql.SmallDateTime
2✔
4382
            case "uniqueidentifier":
53,014✔
4383
                return this.driver.mssql.UniqueIdentifier
272✔
4384
            case "variant":
53,014!
4385
                return this.driver.mssql.Variant
×
4386
            case "binary":
53,014✔
4387
                return this.driver.mssql.Binary
26✔
4388
            case "varbinary":
53,014✔
4389
                return this.driver.mssql.VarBinary(...parameter.params)
4✔
4390
            case "image":
53,014✔
4391
                return this.driver.mssql.Image
2✔
4392
            case "udt":
53,014!
4393
                return this.driver.mssql.UDT
×
4394
            case "rowversion":
53,014!
4395
                return this.driver.mssql.RowVersion
×
4396
            case "vector":
53,014✔
4397
                return this.driver.mssql.Ntext
20✔
4398
        }
53,014✔
4399
    }
53,014✔
4400

28✔
4401
    /**
28✔
4402
     * Converts string literal of isolation level to enum.
28✔
4403
     * The underlying mssql driver requires an enum for the isolation level.
28✔
4404
     * @param isolation
28✔
4405
     */
28✔
4406
    convertIsolationLevel(isolation: IsolationLevel) {
28✔
4407
        const ISOLATION_LEVEL = this.driver.mssql.ISOLATION_LEVEL
12✔
4408
        switch (isolation) {
12✔
4409
            case "READ UNCOMMITTED":
12✔
4410
                return ISOLATION_LEVEL.READ_UNCOMMITTED
4✔
4411
            case "REPEATABLE READ":
12✔
4412
                return ISOLATION_LEVEL.REPEATABLE_READ
2✔
4413
            case "SERIALIZABLE":
12✔
4414
                return ISOLATION_LEVEL.SERIALIZABLE
4✔
4415

12✔
4416
            case "READ COMMITTED":
12✔
4417
            default:
12✔
4418
                return ISOLATION_LEVEL.READ_COMMITTED
2✔
4419
        }
12✔
4420
    }
12✔
4421

28✔
4422
    /**
28✔
4423
     * Change table comment.
28✔
4424
     * @param tableOrName
28✔
4425
     * @param comment
28✔
4426
     */
28✔
4427
    changeTableComment(
28✔
4428
        tableOrName: Table | string,
×
4429
        comment?: string,
×
4430
    ): Promise<void> {
×
4431
        throw new TypeORMError(
×
4432
            `sqlserver driver does not support change table comment.`,
×
4433
        )
×
4434
    }
×
4435
}
28✔
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