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

typeorm / typeorm / 23110275338

15 Mar 2026 12:22PM UTC coverage: 73.186% (-0.04%) from 73.225%
23110275338

push

github

web-flow
feat(sqlite): add support for jsonb column type in SQLite (#11933)

37717 of 48103 branches covered (78.41%)

Branch coverage included in aggregate %.

235 of 317 new or added lines in 8 files covered. (74.13%)

5 existing lines in 2 files now uncovered.

83703 of 117804 relevant lines covered (71.05%)

64163.66 hits per line

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

91.68
/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()
25,510✔
60
        this.driver = driver
25,510✔
61
        this.connection = driver.connection
25,510✔
62
        this.broadcaster = new Broadcaster(this)
25,510✔
63
        this.mode = mode
25,510✔
64
    }
25,510✔
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
25,510✔
84
        return Promise.resolve()
25,510✔
85
    }
25,510✔
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,824!
93

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

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

18,824✔
136
        await this.broadcaster.broadcast("AfterTransactionStart")
18,824✔
137
    }
18,824✔
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,774!
145

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

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

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

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

18,770✔
159
                    ok()
18,770✔
160
                    this.connection.logger.logQuery("COMMIT")
18,770✔
161
                    this.transactionDepth -= 1
18,770✔
162
                })
18,770✔
163
            })
18,770✔
164
        }
18,770✔
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,074✔
209
        parameters?: any[],
109,074✔
210
        useStructuredResult = false,
109,074✔
211
    ): Promise<any> {
109,074✔
212
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
109,074!
213

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

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

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

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

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

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

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

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

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

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

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

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

109,032✔
298
            const queryType = query.slice(0, query.indexOf(" "))
109,032✔
299
            switch (queryType) {
109,032✔
300
                case "DELETE":
109,074✔
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,074✔
305
                    result.raw = raw.recordset
108,742✔
306
            }
109,074✔
307

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

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

109,074✔
334
            release()
109,074✔
335
        }
109,074✔
336
    }
109,074✔
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"`, [
2,976✔
433
            database,
2,976✔
434
        ])
2,976✔
435
        const dbId = result[0]["db_id"]
2,976✔
436
        return !!dbId
2,976✔
437
    }
2,976✔
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,760✔
444
        return currentDBQuery[0]["db_name"]
3,760✔
445
    }
3,760✔
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"`, [
6✔
453
            schema,
6✔
454
        ])
6✔
455
        const schemaId = result[0]["schema_id"]
6✔
456
        return !!schemaId
6✔
457
    }
6✔
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,662✔
464
            `SELECT SCHEMA_NAME() AS "schema_name"`,
4,662✔
465
        )
4,662✔
466
        return currentSchemaQuery[0]["schema_name"]
4,662✔
467
    }
4,662✔
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,208✔
475

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

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

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

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

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

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

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

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

28✔
538
    /**
28✔
539
     * Drops database.
28✔
540
     * @param database
28✔
541
     * @param ifExists
28✔
542
     */
28✔
543
    async dropDatabase(database: string, ifExists?: boolean): Promise<void> {
28✔
544
        const up = ifExists
6✔
545
            ? `IF DB_ID('${database}') IS NOT NULL DROP DATABASE "${database}"`
6✔
546
            : `DROP DATABASE "${database}"`
6✔
547
        const down = `CREATE DATABASE "${database}"`
6✔
548
        await this.executeQueries(new Query(up), new Query(down))
6✔
549
    }
6✔
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,
30✔
559
        ifNotExists?: boolean,
30✔
560
    ): Promise<void> {
30✔
561
        const upQueries: Query[] = []
30✔
562
        const downQueries: Query[] = []
30✔
563

30✔
564
        if (schemaPath.indexOf(".") === -1) {
30✔
565
            const upQuery = ifNotExists
16✔
566
                ? `IF SCHEMA_ID('${schemaPath}') IS NULL BEGIN EXEC ('CREATE SCHEMA "${schemaPath}"') END`
16✔
567
                : `CREATE SCHEMA "${schemaPath}"`
16!
568
            upQueries.push(new Query(upQuery))
16✔
569
            downQueries.push(new Query(`DROP SCHEMA "${schemaPath}"`))
16✔
570
        } else {
30✔
571
            const dbName = schemaPath.split(".")[0]
14✔
572
            const schema = schemaPath.split(".")[1]
14✔
573
            const currentDB = await this.getCurrentDatabase()
14✔
574
            upQueries.push(new Query(`USE "${dbName}"`))
14✔
575
            downQueries.push(new Query(`USE "${currentDB}"`))
14✔
576

14✔
577
            const upQuery = ifNotExists
14✔
578
                ? `IF SCHEMA_ID('${schema}') IS NULL BEGIN EXEC ('CREATE SCHEMA "${schema}"') END`
14✔
579
                : `CREATE SCHEMA "${schema}"`
14!
580
            upQueries.push(new Query(upQuery))
14✔
581
            downQueries.push(new Query(`DROP SCHEMA "${schema}"`))
14✔
582

14✔
583
            upQueries.push(new Query(`USE "${currentDB}"`))
14✔
584
            downQueries.push(new Query(`USE "${dbName}"`))
14✔
585
        }
14✔
586

30✔
587
        await this.executeQueries(upQueries, downQueries)
30✔
588
    }
30✔
589

28✔
590
    /**
28✔
591
     * Drops table schema.
28✔
592
     * If database name also specified (e.g. 'dbName.schemaName') schema will be dropped in specified database.
28✔
593
     * @param schemaPath
28✔
594
     * @param ifExists
28✔
595
     */
28✔
596
    async dropSchema(schemaPath: string, ifExists?: boolean): Promise<void> {
28✔
597
        const upQueries: Query[] = []
4✔
598
        const downQueries: Query[] = []
4✔
599

4✔
600
        if (schemaPath.indexOf(".") === -1) {
4✔
601
            const upQuery = ifExists
4✔
602
                ? `IF SCHEMA_ID('${schemaPath}') IS NOT NULL BEGIN EXEC ('DROP SCHEMA "${schemaPath}"') END`
4✔
603
                : `DROP SCHEMA "${schemaPath}"`
4✔
604
            upQueries.push(new Query(upQuery))
4✔
605
            downQueries.push(new Query(`CREATE SCHEMA "${schemaPath}"`))
4✔
606
        } else {
4!
607
            const dbName = schemaPath.split(".")[0]
×
608
            const schema = schemaPath.split(".")[1]
×
609
            const currentDB = await this.getCurrentDatabase()
×
610
            upQueries.push(new Query(`USE "${dbName}"`))
×
611
            downQueries.push(new Query(`USE "${currentDB}"`))
×
612

×
613
            const upQuery = ifExists
×
614
                ? `IF SCHEMA_ID('${schema}') IS NOT NULL BEGIN EXEC ('DROP SCHEMA "${schema}"') END`
×
615
                : `DROP SCHEMA "${schema}"`
×
616
            upQueries.push(new Query(upQuery))
×
617
            downQueries.push(new Query(`CREATE SCHEMA "${schema}"`))
×
618

×
619
            upQueries.push(new Query(`USE "${currentDB}"`))
×
620
            downQueries.push(new Query(`USE "${dbName}"`))
×
621
        }
×
622

4✔
623
        await this.executeQueries(upQueries, downQueries)
4✔
624
    }
4✔
625

28✔
626
    /**
28✔
627
     * Creates a new table.
28✔
628
     * @param table
28✔
629
     * @param ifNotExists
28✔
630
     * @param createForeignKeys
28✔
631
     * @param createIndices
28✔
632
     */
28✔
633
    async createTable(
28✔
634
        table: Table,
10,360✔
635
        ifNotExists: boolean = false,
10,360✔
636
        createForeignKeys: boolean = true,
10,360✔
637
        createIndices: boolean = true,
10,360✔
638
    ): Promise<void> {
10,360✔
639
        if (ifNotExists) {
10,360✔
640
            const isTableExist = await this.hasTable(table)
66✔
641
            if (isTableExist) return Promise.resolve()
66✔
642
        }
66✔
643
        const upQueries: Query[] = []
10,356✔
644
        const downQueries: Query[] = []
10,356✔
645

10,356✔
646
        upQueries.push(this.createTableSql(table, createForeignKeys))
10,356✔
647
        downQueries.push(this.dropTableSql(table))
10,356✔
648

10,356✔
649
        // if createForeignKeys is true, we must drop created foreign keys in down query.
10,356✔
650
        // createTable does not need separate method to create foreign keys, because it create fk's in the same query with table creation.
10,356✔
651
        if (createForeignKeys)
10,356✔
652
            table.foreignKeys.forEach((foreignKey) =>
10,360✔
653
                downQueries.push(this.dropForeignKeySql(table, foreignKey)),
110✔
654
            )
110✔
655

10,356✔
656
        if (createIndices) {
10,356✔
657
            table.indices.forEach((index) => {
10,356✔
658
                // new index may be passed without name. In this case we generate index name manually.
4,314✔
659
                if (!index.name)
4,314✔
660
                    index.name = this.connection.namingStrategy.indexName(
4,314✔
661
                        table,
6✔
662
                        index.columnNames,
6✔
663
                        index.where,
6✔
664
                    )
6✔
665
                upQueries.push(this.createIndexSql(table, index))
4,314✔
666
                downQueries.push(this.dropIndexSql(table, index))
4,314✔
667
            })
10,356✔
668
        }
10,356✔
669

10,356✔
670
        // if table have column with generated type, we must add the expression to the metadata table
10,356✔
671
        const generatedColumns = table.columns.filter(
10,356✔
672
            (column) => column.generatedType && column.asExpression,
10,356✔
673
        )
10,356✔
674

10,356✔
675
        for (const column of generatedColumns) {
10,360✔
676
            const parsedTableName = this.driver.parseTableName(table)
56✔
677

56✔
678
            if (!parsedTableName.schema) {
56!
679
                parsedTableName.schema = await this.getCurrentSchema()
×
680
            }
×
681

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

56✔
691
            const deleteQuery = this.deleteTypeormMetadataSql({
56✔
692
                database: parsedTableName.database,
56✔
693
                schema: parsedTableName.schema,
56✔
694
                table: parsedTableName.tableName,
56✔
695
                type: MetadataTableType.GENERATED_COLUMN,
56✔
696
                name: column.name,
56✔
697
            })
56✔
698

56✔
699
            upQueries.push(insertQuery)
56✔
700
            downQueries.push(deleteQuery)
56✔
701
        }
56✔
702

10,356✔
703
        await this.executeQueries(upQueries, downQueries)
10,356✔
704
    }
10,356✔
705

28✔
706
    /**
28✔
707
     * Drops the table.
28✔
708
     * @param tableOrName
28✔
709
     * @param ifExists
28✔
710
     * @param dropForeignKeys
28✔
711
     * @param dropIndices
28✔
712
     */
28✔
713
    async dropTable(
28✔
714
        tableOrName: Table | string,
26✔
715
        ifExists?: boolean,
26✔
716
        dropForeignKeys: boolean = true,
26✔
717
        dropIndices: boolean = true,
26✔
718
    ): Promise<void> {
26✔
719
        if (ifExists) {
26✔
720
            const isTableExist = await this.hasTable(tableOrName)
6✔
721
            if (!isTableExist) return Promise.resolve()
6✔
722
        }
6✔
723

24✔
724
        // if dropTable called with dropForeignKeys = true, we must create foreign keys in down query.
24✔
725
        const createForeignKeys: boolean = dropForeignKeys
24✔
726
        const table = InstanceChecker.isTable(tableOrName)
24✔
727
            ? tableOrName
26✔
728
            : await this.getCachedTable(tableOrName)
26✔
729
        const upQueries: Query[] = []
16✔
730
        const downQueries: Query[] = []
16✔
731

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

16✔
735
        if (dropIndices) {
26✔
736
            table.indices.forEach((index) => {
24✔
737
                upQueries.push(this.dropIndexSql(table, index))
2✔
738
                downQueries.push(this.createIndexSql(table, index))
2✔
739
            })
24✔
740
        }
24✔
741

24✔
742
        // if dropForeignKeys is true, we just drop the table, otherwise we also drop table foreign keys.
24✔
743
        // createTable does not need separate method to create foreign keys, because it create fk's in the same query with table creation.
24✔
744
        if (dropForeignKeys)
24✔
745
            table.foreignKeys.forEach((foreignKey) =>
26✔
746
                upQueries.push(this.dropForeignKeySql(table, foreignKey)),
20✔
747
            )
20✔
748

24✔
749
        upQueries.push(this.dropTableSql(table))
24✔
750
        downQueries.push(this.createTableSql(table, createForeignKeys))
24✔
751

24✔
752
        // if table had columns with generated type, we must remove the expression from the metadata table
24✔
753
        const generatedColumns = table.columns.filter(
24✔
754
            (column) => column.generatedType && column.asExpression,
24✔
755
        )
24✔
756

24✔
757
        for (const column of generatedColumns) {
26✔
758
            const parsedTableName = this.driver.parseTableName(table)
8✔
759

8✔
760
            if (!parsedTableName.schema) {
8!
761
                parsedTableName.schema = await this.getCurrentSchema()
×
762
            }
×
763

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

8✔
772
            const insertQuery = this.insertTypeormMetadataSql({
8✔
773
                database: parsedTableName.database,
8✔
774
                schema: parsedTableName.schema,
8✔
775
                table: parsedTableName.tableName,
8✔
776
                type: MetadataTableType.GENERATED_COLUMN,
8✔
777
                name: column.name,
8✔
778
                value: column.asExpression,
8✔
779
            })
8✔
780

8✔
781
            upQueries.push(deleteQuery)
8✔
782
            downQueries.push(insertQuery)
8✔
783
        }
8✔
784

24✔
785
        await this.executeQueries(upQueries, downQueries)
24✔
786
    }
24✔
787

28✔
788
    /**
28✔
789
     * Creates a new view.
28✔
790
     * @param view
28✔
791
     * @param syncWithMetadata
28✔
792
     */
28✔
793
    async createView(
28✔
794
        view: View,
16✔
795
        syncWithMetadata: boolean = false,
16✔
796
    ): Promise<void> {
16✔
797
        const upQueries: Query[] = []
16✔
798
        const downQueries: Query[] = []
16✔
799
        upQueries.push(this.createViewSql(view))
16✔
800
        if (syncWithMetadata)
16✔
801
            upQueries.push(await this.insertViewDefinitionSql(view))
16✔
802
        downQueries.push(this.dropViewSql(view))
16✔
803
        if (syncWithMetadata)
16✔
804
            downQueries.push(await this.deleteViewDefinitionSql(view))
16✔
805
        await this.executeQueries(upQueries, downQueries)
16✔
806
    }
16✔
807

28✔
808
    /**
28✔
809
     * Drops the view.
28✔
810
     * @param target
28✔
811
     * @param ifExists
28✔
812
     */
28✔
813
    async dropView(target: View | string, ifExists?: boolean): Promise<void> {
28✔
814
        const viewName = InstanceChecker.isView(target) ? target.name : target
×
815
        let view: View
×
816
        try {
×
817
            view = await this.getCachedView(viewName)
×
818
        } catch {
×
819
            if (ifExists) return
×
820
            throw new TypeORMError(`View "${viewName}" does not exist.`)
×
821
        }
×
822

×
823
        await this.executeQueries(
×
824
            [
×
825
                await this.deleteViewDefinitionSql(view),
×
826
                this.dropViewSql(view, ifExists),
×
827
            ],
×
828
            [
×
829
                await this.insertViewDefinitionSql(view),
×
830
                this.createViewSql(view),
×
831
            ],
×
832
        )
×
833
    }
×
834

28✔
835
    /**
28✔
836
     * Renames a table.
28✔
837
     * @param oldTableOrName
28✔
838
     * @param newTableName
28✔
839
     */
28✔
840
    async renameTable(
28✔
841
        oldTableOrName: Table | string,
34✔
842
        newTableName: string,
34✔
843
    ): Promise<void> {
34✔
844
        const upQueries: Query[] = []
34✔
845
        const downQueries: Query[] = []
34✔
846
        const oldTable = InstanceChecker.isTable(oldTableOrName)
34✔
847
            ? oldTableOrName
34✔
848
            : await this.getCachedTable(oldTableOrName)
34✔
849
        const newTable = oldTable.clone()
30✔
850

30✔
851
        // we need database name and schema name to rename FK constraints
30✔
852
        let dbName: string | undefined = undefined
30✔
853
        let schemaName: string | undefined = undefined
30✔
854
        let oldTableName: string = oldTable.name
30✔
855
        const splittedName = oldTable.name.split(".")
30✔
856
        if (splittedName.length === 3) {
34✔
857
            dbName = splittedName[0]
4✔
858
            oldTableName = splittedName[2]
4✔
859
            if (splittedName[1] !== "") schemaName = splittedName[1]
4✔
860
        } else if (splittedName.length === 2) {
34!
861
            schemaName = splittedName[0]
×
862
            oldTableName = splittedName[1]
×
863
        }
×
864

34✔
865
        newTable.name = this.driver.buildTableName(
34✔
866
            newTableName,
34✔
867
            schemaName,
34✔
868
            dbName,
34✔
869
        )
34✔
870

34✔
871
        // if we have tables with database which differs from database specified in config, we must change currently used database.
34✔
872
        // This need because we can not rename objects from another database.
34✔
873
        const currentDB = await this.getCurrentDatabase()
34✔
874
        if (dbName && dbName !== currentDB) {
34✔
875
            upQueries.push(new Query(`USE "${dbName}"`))
4✔
876
            downQueries.push(new Query(`USE "${currentDB}"`))
4✔
877
        }
4✔
878

34✔
879
        // rename table
34✔
880
        upQueries.push(
34✔
881
            new Query(
34✔
882
                `EXEC sp_rename "${this.getTablePath(
34✔
883
                    oldTable,
34✔
884
                )}", "${newTableName}"`,
34✔
885
            ),
34✔
886
        )
34✔
887
        downQueries.push(
34✔
888
            new Query(
34✔
889
                `EXEC sp_rename "${this.getTablePath(
34✔
890
                    newTable,
34✔
891
                )}", "${oldTableName}"`,
34✔
892
            ),
34✔
893
        )
34✔
894

34✔
895
        // rename primary key constraint
34✔
896
        if (
34✔
897
            newTable.primaryColumns.length > 0 &&
34✔
898
            !newTable.primaryColumns[0].primaryKeyConstraintName
34✔
899
        ) {
34✔
900
            const columnNames = newTable.primaryColumns.map(
26✔
901
                (column) => column.name,
26✔
902
            )
26✔
903

26✔
904
            const oldPkName = this.connection.namingStrategy.primaryKeyName(
26✔
905
                oldTable,
26✔
906
                columnNames,
26✔
907
            )
26✔
908
            const newPkName = this.connection.namingStrategy.primaryKeyName(
26✔
909
                newTable,
26✔
910
                columnNames,
26✔
911
            )
26✔
912

26✔
913
            // rename primary constraint
26✔
914
            upQueries.push(
26✔
915
                new Query(
26✔
916
                    `EXEC sp_rename "${this.getTablePath(
26✔
917
                        newTable,
26✔
918
                    )}.${oldPkName}", "${newPkName}"`,
26✔
919
                ),
26✔
920
            )
26✔
921
            downQueries.push(
26✔
922
                new Query(
26✔
923
                    `EXEC sp_rename "${this.getTablePath(
26✔
924
                        newTable,
26✔
925
                    )}.${newPkName}", "${oldPkName}"`,
26✔
926
                ),
26✔
927
            )
26✔
928
        }
26✔
929

34✔
930
        // rename unique constraints
34✔
931
        newTable.uniques.forEach((unique) => {
34✔
932
            const oldUniqueName =
10✔
933
                this.connection.namingStrategy.uniqueConstraintName(
10✔
934
                    oldTable,
10✔
935
                    unique.columnNames,
10✔
936
                )
10✔
937

10✔
938
            // Skip renaming if Unique has user defined constraint name
10✔
939
            if (unique.name !== oldUniqueName) return
10✔
940

6✔
941
            // build new constraint name
6✔
942
            const newUniqueName =
6✔
943
                this.connection.namingStrategy.uniqueConstraintName(
6✔
944
                    newTable,
6✔
945
                    unique.columnNames,
6✔
946
                )
6✔
947

6✔
948
            // build queries
6✔
949
            upQueries.push(
6✔
950
                new Query(
6✔
951
                    `EXEC sp_rename "${this.getTablePath(newTable)}.${
6✔
952
                        unique.name
6✔
953
                    }", "${newUniqueName}"`,
6✔
954
                ),
6✔
955
            )
6✔
956
            downQueries.push(
6✔
957
                new Query(
6✔
958
                    `EXEC sp_rename "${this.getTablePath(
6✔
959
                        newTable,
6✔
960
                    )}.${newUniqueName}", "${unique.name}"`,
6✔
961
                ),
6✔
962
            )
6✔
963

6✔
964
            // replace constraint name
6✔
965
            unique.name = newUniqueName
6✔
966
        })
34✔
967

34✔
968
        // rename index constraints
34✔
969
        newTable.indices.forEach((index) => {
34✔
970
            const oldIndexName = this.connection.namingStrategy.indexName(
22✔
971
                oldTable,
22✔
972
                index.columnNames,
22✔
973
                index.where,
22✔
974
            )
22✔
975

22✔
976
            // Skip renaming if Index has user defined constraint name
22✔
977
            if (index.name !== oldIndexName) return
22✔
978

10✔
979
            // build new constraint name
10✔
980
            const newIndexName = this.connection.namingStrategy.indexName(
10✔
981
                newTable,
10✔
982
                index.columnNames,
10✔
983
                index.where,
10✔
984
            )
10✔
985

10✔
986
            // build queries
10✔
987
            upQueries.push(
10✔
988
                new Query(
10✔
989
                    `EXEC sp_rename "${this.getTablePath(newTable)}.${
10✔
990
                        index.name
10✔
991
                    }", "${newIndexName}", "INDEX"`,
10✔
992
                ),
10✔
993
            )
10✔
994
            downQueries.push(
10✔
995
                new Query(
10✔
996
                    `EXEC sp_rename "${this.getTablePath(
10✔
997
                        newTable,
10✔
998
                    )}.${newIndexName}", "${index.name}", "INDEX"`,
10✔
999
                ),
10✔
1000
            )
10✔
1001

10✔
1002
            // replace constraint name
10✔
1003
            index.name = newIndexName
10✔
1004
        })
34✔
1005

34✔
1006
        // rename foreign key constraints
34✔
1007
        newTable.foreignKeys.forEach((foreignKey) => {
34✔
1008
            const oldForeignKeyName =
18✔
1009
                this.connection.namingStrategy.foreignKeyName(
18✔
1010
                    oldTable,
18✔
1011
                    foreignKey.columnNames,
18✔
1012
                    this.getTablePath(foreignKey),
18✔
1013
                    foreignKey.referencedColumnNames,
18✔
1014
                )
18✔
1015

18✔
1016
            // Skip renaming if foreign key has user defined constraint name
18✔
1017
            if (foreignKey.name !== oldForeignKeyName) return
18✔
1018

2✔
1019
            // build new constraint name
2✔
1020
            const newForeignKeyName =
2✔
1021
                this.connection.namingStrategy.foreignKeyName(
2✔
1022
                    newTable,
2✔
1023
                    foreignKey.columnNames,
2✔
1024
                    this.getTablePath(foreignKey),
2✔
1025
                    foreignKey.referencedColumnNames,
2✔
1026
                )
2✔
1027

2✔
1028
            // build queries
2✔
1029
            upQueries.push(
2✔
1030
                new Query(
2✔
1031
                    `EXEC sp_rename "${this.buildForeignKeyName(
2✔
1032
                        foreignKey.name!,
2✔
1033
                        schemaName,
2✔
1034
                        dbName,
2✔
1035
                    )}", "${newForeignKeyName}"`,
2✔
1036
                ),
2✔
1037
            )
2✔
1038
            downQueries.push(
2✔
1039
                new Query(
2✔
1040
                    `EXEC sp_rename "${this.buildForeignKeyName(
2✔
1041
                        newForeignKeyName,
2✔
1042
                        schemaName,
2✔
1043
                        dbName,
2✔
1044
                    )}", "${foreignKey.name}"`,
2✔
1045
                ),
2✔
1046
            )
2✔
1047

2✔
1048
            // replace constraint name
2✔
1049
            foreignKey.name = newForeignKeyName
2✔
1050
        })
34✔
1051

34✔
1052
        // change currently used database back to default db.
34✔
1053
        if (dbName && dbName !== currentDB) {
34✔
1054
            upQueries.push(new Query(`USE "${currentDB}"`))
4✔
1055
            downQueries.push(new Query(`USE "${dbName}"`))
4✔
1056
        }
4✔
1057

34✔
1058
        await this.executeQueries(upQueries, downQueries)
34✔
1059

34✔
1060
        // rename old table and replace it in cached tabled;
34✔
1061
        oldTable.name = newTable.name
34✔
1062
        this.replaceCachedTable(oldTable, newTable)
34✔
1063
    }
34✔
1064

28✔
1065
    /**
28✔
1066
     * Creates a new column from the column in the table.
28✔
1067
     * @param tableOrName
28✔
1068
     * @param column
28✔
1069
     */
28✔
1070
    async addColumn(
28✔
1071
        tableOrName: Table | string,
74✔
1072
        column: TableColumn,
74✔
1073
    ): Promise<void> {
74✔
1074
        const table = InstanceChecker.isTable(tableOrName)
74✔
1075
            ? tableOrName
74✔
1076
            : await this.getCachedTable(tableOrName)
74✔
1077
        const clonedTable = table.clone()
4✔
1078
        const upQueries: Query[] = []
4✔
1079
        const downQueries: Query[] = []
4✔
1080

4✔
1081
        upQueries.push(
4✔
1082
            new Query(
4✔
1083
                `ALTER TABLE ${this.escapePath(
4✔
1084
                    table,
4✔
1085
                )} ADD ${this.buildCreateColumnSql(
4✔
1086
                    table,
4✔
1087
                    column,
4✔
1088
                    false,
4✔
1089
                    true,
4✔
1090
                )}`,
4✔
1091
            ),
4✔
1092
        )
4✔
1093
        downQueries.push(
4✔
1094
            new Query(
4✔
1095
                `ALTER TABLE ${this.escapePath(table)} DROP COLUMN "${
4✔
1096
                    column.name
4✔
1097
                }"`,
4✔
1098
            ),
4✔
1099
        )
4✔
1100

4✔
1101
        // create or update primary key constraint
4✔
1102
        if (column.isPrimary) {
74✔
1103
            const primaryColumns = clonedTable.primaryColumns
14✔
1104
            // if table already have primary key, me must drop it and recreate again
14✔
1105
            if (primaryColumns.length > 0) {
14✔
1106
                const pkName = primaryColumns[0].primaryKeyConstraintName
4✔
1107
                    ? primaryColumns[0].primaryKeyConstraintName
4!
1108
                    : this.connection.namingStrategy.primaryKeyName(
4✔
1109
                          clonedTable,
4✔
1110
                          primaryColumns.map((column) => column.name),
4✔
1111
                      )
4✔
1112

4✔
1113
                const columnNames = primaryColumns
4✔
1114
                    .map((column) => `"${column.name}"`)
4✔
1115
                    .join(", ")
4✔
1116

4✔
1117
                upQueries.push(
4✔
1118
                    new Query(
4✔
1119
                        `ALTER TABLE ${this.escapePath(
4✔
1120
                            table,
4✔
1121
                        )} DROP CONSTRAINT "${pkName}"`,
4✔
1122
                    ),
4✔
1123
                )
4✔
1124
                downQueries.push(
4✔
1125
                    new Query(
4✔
1126
                        `ALTER TABLE ${this.escapePath(
4✔
1127
                            table,
4✔
1128
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
4✔
1129
                    ),
4✔
1130
                )
4✔
1131
            }
4✔
1132

14✔
1133
            primaryColumns.push(column)
14✔
1134
            const pkName = primaryColumns[0].primaryKeyConstraintName
14✔
1135
                ? primaryColumns[0].primaryKeyConstraintName
14!
1136
                : this.connection.namingStrategy.primaryKeyName(
14✔
1137
                      clonedTable,
14✔
1138
                      primaryColumns.map((column) => column.name),
14✔
1139
                  )
14✔
1140

14✔
1141
            const columnNames = primaryColumns
14✔
1142
                .map((column) => `"${column.name}"`)
14✔
1143
                .join(", ")
14✔
1144
            upQueries.push(
14✔
1145
                new Query(
14✔
1146
                    `ALTER TABLE ${this.escapePath(
14✔
1147
                        table,
14✔
1148
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
14✔
1149
                ),
14✔
1150
            )
14✔
1151
            downQueries.push(
14✔
1152
                new Query(
14✔
1153
                    `ALTER TABLE ${this.escapePath(
14✔
1154
                        table,
14✔
1155
                    )} DROP CONSTRAINT "${pkName}"`,
14✔
1156
                ),
14✔
1157
            )
14✔
1158
        }
14✔
1159

74✔
1160
        // create column index
74✔
1161
        const columnIndex = clonedTable.indices.find(
74✔
1162
            (index) =>
74✔
1163
                index.columnNames.length === 1 &&
2✔
1164
                index.columnNames[0] === column.name,
74✔
1165
        )
74✔
1166
        if (columnIndex) {
74!
1167
            upQueries.push(this.createIndexSql(table, columnIndex))
×
1168
            downQueries.push(this.dropIndexSql(table, columnIndex))
×
1169
        }
×
1170

74✔
1171
        // create unique constraint
74✔
1172
        if (column.isUnique) {
74✔
1173
            const uniqueConstraint = new TableUnique({
6✔
1174
                name: this.connection.namingStrategy.uniqueConstraintName(
6✔
1175
                    table,
6✔
1176
                    [column.name],
6✔
1177
                ),
6✔
1178
                columnNames: [column.name],
6✔
1179
            })
6✔
1180
            clonedTable.uniques.push(uniqueConstraint)
6✔
1181
            upQueries.push(
6✔
1182
                new Query(
6✔
1183
                    `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
6✔
1184
                        uniqueConstraint.name
6✔
1185
                    }" UNIQUE ("${column.name}")`,
6✔
1186
                ),
6✔
1187
            )
6✔
1188
            downQueries.push(
6✔
1189
                new Query(
6✔
1190
                    `ALTER TABLE ${this.escapePath(table)} DROP CONSTRAINT "${
6✔
1191
                        uniqueConstraint.name
6✔
1192
                    }"`,
6✔
1193
                ),
6✔
1194
            )
6✔
1195
        }
6✔
1196

74✔
1197
        // remove default constraint
74✔
1198
        if (column.default !== null && column.default !== undefined) {
74✔
1199
            const defaultName =
10✔
1200
                this.connection.namingStrategy.defaultConstraintName(
10✔
1201
                    table,
10✔
1202
                    column.name,
10✔
1203
                )
10✔
1204
            downQueries.push(
10✔
1205
                new Query(
10✔
1206
                    `ALTER TABLE ${this.escapePath(
10✔
1207
                        table,
10✔
1208
                    )} DROP CONSTRAINT "${defaultName}"`,
10✔
1209
                ),
10✔
1210
            )
10✔
1211
        }
10✔
1212

74✔
1213
        if (column.generatedType && column.asExpression) {
74✔
1214
            const parsedTableName = this.driver.parseTableName(table)
6✔
1215

6✔
1216
            if (!parsedTableName.schema) {
6!
1217
                parsedTableName.schema = await this.getCurrentSchema()
×
1218
            }
×
1219

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

6✔
1229
            const deleteQuery = this.deleteTypeormMetadataSql({
6✔
1230
                database: parsedTableName.database,
6✔
1231
                schema: parsedTableName.schema,
6✔
1232
                table: parsedTableName.tableName,
6✔
1233
                type: MetadataTableType.GENERATED_COLUMN,
6✔
1234
                name: column.name,
6✔
1235
            })
6✔
1236

6✔
1237
            upQueries.push(insertQuery)
6✔
1238
            downQueries.push(deleteQuery)
6✔
1239
        }
6✔
1240

74✔
1241
        await this.executeQueries(upQueries, downQueries)
74✔
1242

70✔
1243
        clonedTable.addColumn(column)
70✔
1244
        this.replaceCachedTable(table, clonedTable)
70✔
1245
    }
70✔
1246

28✔
1247
    /**
28✔
1248
     * Creates a new columns from the column in the table.
28✔
1249
     * @param tableOrName
28✔
1250
     * @param columns
28✔
1251
     */
28✔
1252
    async addColumns(
28✔
1253
        tableOrName: Table | string,
12✔
1254
        columns: TableColumn[],
12✔
1255
    ): Promise<void> {
12✔
1256
        for (const column of columns) {
12✔
1257
            await this.addColumn(tableOrName, column)
14✔
1258
        }
12✔
1259
    }
10✔
1260

28✔
1261
    /**
28✔
1262
     * Renames column in the given table.
28✔
1263
     * @param tableOrName
28✔
1264
     * @param oldTableColumnOrName
28✔
1265
     * @param newTableColumnOrName
28✔
1266
     */
28✔
1267
    async renameColumn(
28✔
1268
        tableOrName: Table | string,
28✔
1269
        oldTableColumnOrName: TableColumn | string,
28✔
1270
        newTableColumnOrName: TableColumn | string,
28✔
1271
    ): Promise<void> {
28✔
1272
        const table = InstanceChecker.isTable(tableOrName)
28✔
1273
            ? tableOrName
28✔
1274
            : await this.getCachedTable(tableOrName)
28✔
1275
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
4✔
1276
            ? oldTableColumnOrName
28✔
1277
            : table.columns.find((c) => c.name === oldTableColumnOrName)
28✔
1278
        if (!oldColumn)
28✔
1279
            throw new TypeORMError(
28!
1280
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
×
1281
            )
×
1282

28✔
1283
        let newColumn: TableColumn
28✔
1284
        if (InstanceChecker.isTableColumn(newTableColumnOrName)) {
28✔
1285
            newColumn = newTableColumnOrName
18✔
1286
        } else {
28✔
1287
            newColumn = oldColumn.clone()
10✔
1288
            newColumn.name = newTableColumnOrName
10✔
1289
        }
10✔
1290

28✔
1291
        await this.changeColumn(table, oldColumn, newColumn)
28✔
1292
    }
28✔
1293

28✔
1294
    /**
28✔
1295
     * Changes a column in the table.
28✔
1296
     * @param tableOrName
28✔
1297
     * @param oldTableColumnOrName
28✔
1298
     * @param newColumn
28✔
1299
     */
28✔
1300
    async changeColumn(
28✔
1301
        tableOrName: Table | string,
104✔
1302
        oldTableColumnOrName: TableColumn | string,
104✔
1303
        newColumn: TableColumn,
104✔
1304
    ): Promise<void> {
104✔
1305
        const table = InstanceChecker.isTable(tableOrName)
104✔
1306
            ? tableOrName
104✔
1307
            : await this.getCachedTable(tableOrName)
104!
1308
        let clonedTable = table.clone()
×
1309
        const upQueries: Query[] = []
×
1310
        const downQueries: Query[] = []
×
1311

×
1312
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
×
1313
            ? oldTableColumnOrName
104✔
1314
            : table.columns.find(
104!
1315
                  (column) => column.name === oldTableColumnOrName,
×
1316
              )
104✔
1317
        if (!oldColumn)
104✔
1318
            throw new TypeORMError(
104!
1319
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
×
1320
            )
×
1321

104✔
1322
        if (
104✔
1323
            (newColumn.isGenerated !== oldColumn.isGenerated &&
104✔
1324
                newColumn.generationStrategy !== "uuid") ||
104✔
1325
            newColumn.type !== oldColumn.type ||
104✔
1326
            newColumn.length !== oldColumn.length ||
104✔
1327
            newColumn.asExpression !== oldColumn.asExpression ||
104✔
1328
            newColumn.generatedType !== oldColumn.generatedType
54✔
1329
        ) {
104✔
1330
            // SQL Server does not support changing of IDENTITY column, so we must drop column and recreate it again.
50✔
1331
            // Also, we recreate column if column type changed
50✔
1332
            await this.dropColumn(table, oldColumn)
50✔
1333
            await this.addColumn(table, newColumn)
50✔
1334

50✔
1335
            // update cloned table
50✔
1336
            clonedTable = table.clone()
50✔
1337
        } else {
104✔
1338
            if (newColumn.name !== oldColumn.name) {
54✔
1339
                // we need database name and schema name to rename FK constraints
28✔
1340
                let dbName: string | undefined = undefined
28✔
1341
                let schemaName: string | undefined = undefined
28✔
1342
                const splittedName = table.name.split(".")
28✔
1343
                if (splittedName.length === 3) {
28✔
1344
                    dbName = splittedName[0]
4✔
1345
                    if (splittedName[1] !== "") schemaName = splittedName[1]
4✔
1346
                } else if (splittedName.length === 2) {
28!
1347
                    schemaName = splittedName[0]
×
1348
                }
×
1349

28✔
1350
                // if we have tables with database which differs from database specified in config, we must change currently used database.
28✔
1351
                // This need because we can not rename objects from another database.
28✔
1352
                const currentDB = await this.getCurrentDatabase()
28✔
1353
                if (dbName && dbName !== currentDB) {
28✔
1354
                    upQueries.push(new Query(`USE "${dbName}"`))
4✔
1355
                    downQueries.push(new Query(`USE "${currentDB}"`))
4✔
1356
                }
4✔
1357

28✔
1358
                // rename the column
28✔
1359
                upQueries.push(
28✔
1360
                    new Query(
28✔
1361
                        `EXEC sp_rename "${this.getTablePath(table)}.${
28✔
1362
                            oldColumn.name
28✔
1363
                        }", "${newColumn.name}"`,
28✔
1364
                    ),
28✔
1365
                )
28✔
1366
                downQueries.push(
28✔
1367
                    new Query(
28✔
1368
                        `EXEC sp_rename "${this.getTablePath(table)}.${
28✔
1369
                            newColumn.name
28✔
1370
                        }", "${oldColumn.name}"`,
28✔
1371
                    ),
28✔
1372
                )
28✔
1373

28✔
1374
                // rename column primary key constraint
28✔
1375
                if (
28✔
1376
                    oldColumn.isPrimary === true &&
28✔
1377
                    !oldColumn.primaryKeyConstraintName
6✔
1378
                ) {
28✔
1379
                    const primaryColumns = clonedTable.primaryColumns
2✔
1380

2✔
1381
                    // build old primary constraint name
2✔
1382
                    const columnNames = primaryColumns.map(
2✔
1383
                        (column) => column.name,
2✔
1384
                    )
2✔
1385
                    const oldPkName =
2✔
1386
                        this.connection.namingStrategy.primaryKeyName(
2✔
1387
                            clonedTable,
2✔
1388
                            columnNames,
2✔
1389
                        )
2✔
1390

2✔
1391
                    // replace old column name with new column name
2✔
1392
                    columnNames.splice(columnNames.indexOf(oldColumn.name), 1)
2✔
1393
                    columnNames.push(newColumn.name)
2✔
1394

2✔
1395
                    // build new primary constraint name
2✔
1396
                    const newPkName =
2✔
1397
                        this.connection.namingStrategy.primaryKeyName(
2✔
1398
                            clonedTable,
2✔
1399
                            columnNames,
2✔
1400
                        )
2✔
1401

2✔
1402
                    // rename primary constraint
2✔
1403
                    upQueries.push(
2✔
1404
                        new Query(
2✔
1405
                            `EXEC sp_rename "${this.getTablePath(
2✔
1406
                                clonedTable,
2✔
1407
                            )}.${oldPkName}", "${newPkName}"`,
2✔
1408
                        ),
2✔
1409
                    )
2✔
1410
                    downQueries.push(
2✔
1411
                        new Query(
2✔
1412
                            `EXEC sp_rename "${this.getTablePath(
2✔
1413
                                clonedTable,
2✔
1414
                            )}.${newPkName}", "${oldPkName}"`,
2✔
1415
                        ),
2✔
1416
                    )
2✔
1417
                }
2✔
1418

28✔
1419
                // rename index constraints
28✔
1420
                clonedTable.findColumnIndices(oldColumn).forEach((index) => {
28✔
1421
                    const oldIndexName =
6✔
1422
                        this.connection.namingStrategy.indexName(
6✔
1423
                            clonedTable,
6✔
1424
                            index.columnNames,
6✔
1425
                            index.where,
6✔
1426
                        )
6✔
1427

6✔
1428
                    // Skip renaming if Index has user defined constraint name
6✔
1429
                    if (index.name !== oldIndexName) return
6✔
1430

2✔
1431
                    // build new constraint name
2✔
1432
                    index.columnNames.splice(
2✔
1433
                        index.columnNames.indexOf(oldColumn.name),
2✔
1434
                        1,
2✔
1435
                    )
2✔
1436
                    index.columnNames.push(newColumn.name)
2✔
1437
                    const newIndexName =
2✔
1438
                        this.connection.namingStrategy.indexName(
2✔
1439
                            clonedTable,
2✔
1440
                            index.columnNames,
2✔
1441
                            index.where,
2✔
1442
                        )
2✔
1443

2✔
1444
                    // build queries
2✔
1445
                    upQueries.push(
2✔
1446
                        new Query(
2✔
1447
                            `EXEC sp_rename "${this.getTablePath(
2✔
1448
                                clonedTable,
2✔
1449
                            )}.${index.name}", "${newIndexName}", "INDEX"`,
2✔
1450
                        ),
2✔
1451
                    )
2✔
1452
                    downQueries.push(
2✔
1453
                        new Query(
2✔
1454
                            `EXEC sp_rename "${this.getTablePath(
2✔
1455
                                clonedTable,
2✔
1456
                            )}.${newIndexName}", "${index.name}", "INDEX"`,
2✔
1457
                        ),
2✔
1458
                    )
2✔
1459

2✔
1460
                    // replace constraint name
2✔
1461
                    index.name = newIndexName
2✔
1462
                })
28✔
1463

28✔
1464
                // rename foreign key constraints
28✔
1465
                clonedTable
28✔
1466
                    .findColumnForeignKeys(oldColumn)
28✔
1467
                    .forEach((foreignKey) => {
28✔
1468
                        const foreignKeyName =
2✔
1469
                            this.connection.namingStrategy.foreignKeyName(
2✔
1470
                                clonedTable,
2✔
1471
                                foreignKey.columnNames,
2✔
1472
                                this.getTablePath(foreignKey),
2✔
1473
                                foreignKey.referencedColumnNames,
2✔
1474
                            )
2✔
1475

2✔
1476
                        // Skip renaming if foreign key has user defined constraint name
2✔
1477
                        if (foreignKey.name !== foreignKeyName) return
2!
1478

2✔
1479
                        // build new constraint name
2✔
1480
                        foreignKey.columnNames.splice(
2✔
1481
                            foreignKey.columnNames.indexOf(oldColumn.name),
2✔
1482
                            1,
2✔
1483
                        )
2✔
1484
                        foreignKey.columnNames.push(newColumn.name)
2✔
1485
                        const newForeignKeyName =
2✔
1486
                            this.connection.namingStrategy.foreignKeyName(
2✔
1487
                                clonedTable,
2✔
1488
                                foreignKey.columnNames,
2✔
1489
                                this.getTablePath(foreignKey),
2✔
1490
                                foreignKey.referencedColumnNames,
2✔
1491
                            )
2✔
1492

2✔
1493
                        // build queries
2✔
1494
                        upQueries.push(
2✔
1495
                            new Query(
2✔
1496
                                `EXEC sp_rename "${this.buildForeignKeyName(
2✔
1497
                                    foreignKey.name!,
2✔
1498
                                    schemaName,
2✔
1499
                                    dbName,
2✔
1500
                                )}", "${newForeignKeyName}"`,
2✔
1501
                            ),
2✔
1502
                        )
2✔
1503
                        downQueries.push(
2✔
1504
                            new Query(
2✔
1505
                                `EXEC sp_rename "${this.buildForeignKeyName(
2✔
1506
                                    newForeignKeyName,
2✔
1507
                                    schemaName,
2✔
1508
                                    dbName,
2✔
1509
                                )}", "${foreignKey.name}"`,
2✔
1510
                            ),
2✔
1511
                        )
2✔
1512

2✔
1513
                        // replace constraint name
2✔
1514
                        foreignKey.name = newForeignKeyName
2✔
1515
                    })
28✔
1516

28✔
1517
                // rename check constraints
28✔
1518
                clonedTable.findColumnChecks(oldColumn).forEach((check) => {
28✔
1519
                    // build new constraint name
×
1520
                    check.columnNames!.splice(
×
1521
                        check.columnNames!.indexOf(oldColumn.name),
×
1522
                        1,
×
1523
                    )
×
1524
                    check.columnNames!.push(newColumn.name)
×
1525
                    const newCheckName =
×
1526
                        this.connection.namingStrategy.checkConstraintName(
×
1527
                            clonedTable,
×
1528
                            check.expression!,
×
1529
                        )
×
1530

×
1531
                    // build queries
×
1532
                    upQueries.push(
×
1533
                        new Query(
×
1534
                            `EXEC sp_rename "${this.getTablePath(
×
1535
                                clonedTable,
×
1536
                            )}.${check.name}", "${newCheckName}"`,
×
1537
                        ),
×
1538
                    )
×
1539
                    downQueries.push(
×
1540
                        new Query(
×
1541
                            `EXEC sp_rename "${this.getTablePath(
×
1542
                                clonedTable,
×
1543
                            )}.${newCheckName}", "${check.name}"`,
×
1544
                        ),
×
1545
                    )
×
1546

×
1547
                    // replace constraint name
×
1548
                    check.name = newCheckName
×
1549
                })
28✔
1550

28✔
1551
                // rename unique constraints
28✔
1552
                clonedTable.findColumnUniques(oldColumn).forEach((unique) => {
28✔
1553
                    const oldUniqueName =
12✔
1554
                        this.connection.namingStrategy.uniqueConstraintName(
12✔
1555
                            clonedTable,
12✔
1556
                            unique.columnNames,
12✔
1557
                        )
12✔
1558

12✔
1559
                    // Skip renaming if Unique has user defined constraint name
12✔
1560
                    if (unique.name !== oldUniqueName) return
12✔
1561

8✔
1562
                    // build new constraint name
8✔
1563
                    unique.columnNames.splice(
8✔
1564
                        unique.columnNames.indexOf(oldColumn.name),
8✔
1565
                        1,
8✔
1566
                    )
8✔
1567
                    unique.columnNames.push(newColumn.name)
8✔
1568
                    const newUniqueName =
8✔
1569
                        this.connection.namingStrategy.uniqueConstraintName(
8✔
1570
                            clonedTable,
8✔
1571
                            unique.columnNames,
8✔
1572
                        )
8✔
1573

8✔
1574
                    // build queries
8✔
1575
                    upQueries.push(
8✔
1576
                        new Query(
8✔
1577
                            `EXEC sp_rename "${this.getTablePath(
8✔
1578
                                clonedTable,
8✔
1579
                            )}.${unique.name}", "${newUniqueName}"`,
8✔
1580
                        ),
8✔
1581
                    )
8✔
1582
                    downQueries.push(
8✔
1583
                        new Query(
8✔
1584
                            `EXEC sp_rename "${this.getTablePath(
8✔
1585
                                clonedTable,
8✔
1586
                            )}.${newUniqueName}", "${unique.name}"`,
8✔
1587
                        ),
8✔
1588
                    )
8✔
1589

8✔
1590
                    // replace constraint name
8✔
1591
                    unique.name = newUniqueName
8✔
1592
                })
28✔
1593

28✔
1594
                // rename default constraints
28✔
1595
                if (
28✔
1596
                    oldColumn.default !== null &&
28✔
1597
                    oldColumn.default !== undefined
28✔
1598
                ) {
28✔
1599
                    const oldDefaultName =
4✔
1600
                        this.connection.namingStrategy.defaultConstraintName(
4✔
1601
                            table,
4✔
1602
                            oldColumn.name,
4✔
1603
                        )
4✔
1604
                    const newDefaultName =
4✔
1605
                        this.connection.namingStrategy.defaultConstraintName(
4✔
1606
                            table,
4✔
1607
                            newColumn.name,
4✔
1608
                        )
4✔
1609

4✔
1610
                    upQueries.push(
4✔
1611
                        new Query(
4✔
1612
                            `ALTER TABLE ${this.escapePath(
4✔
1613
                                table,
4✔
1614
                            )} DROP CONSTRAINT "${oldDefaultName}"`,
4✔
1615
                        ),
4✔
1616
                    )
4✔
1617
                    downQueries.push(
4✔
1618
                        new Query(
4✔
1619
                            `ALTER TABLE ${this.escapePath(
4✔
1620
                                table,
4✔
1621
                            )} ADD CONSTRAINT "${oldDefaultName}" DEFAULT ${
4✔
1622
                                oldColumn.default
4✔
1623
                            } FOR "${newColumn.name}"`,
4✔
1624
                        ),
4✔
1625
                    )
4✔
1626

4✔
1627
                    upQueries.push(
4✔
1628
                        new Query(
4✔
1629
                            `ALTER TABLE ${this.escapePath(
4✔
1630
                                table,
4✔
1631
                            )} ADD CONSTRAINT "${newDefaultName}" DEFAULT ${
4✔
1632
                                oldColumn.default
4✔
1633
                            } FOR "${newColumn.name}"`,
4✔
1634
                        ),
4✔
1635
                    )
4✔
1636
                    downQueries.push(
4✔
1637
                        new Query(
4✔
1638
                            `ALTER TABLE ${this.escapePath(
4✔
1639
                                table,
4✔
1640
                            )} DROP CONSTRAINT "${newDefaultName}"`,
4✔
1641
                        ),
4✔
1642
                    )
4✔
1643
                }
4✔
1644

28✔
1645
                // change currently used database back to default db.
28✔
1646
                if (dbName && dbName !== currentDB) {
28✔
1647
                    upQueries.push(new Query(`USE "${currentDB}"`))
4✔
1648
                    downQueries.push(new Query(`USE "${dbName}"`))
4✔
1649
                }
4✔
1650

28✔
1651
                // rename old column in the Table object
28✔
1652
                const oldTableColumn = clonedTable.columns.find(
28✔
1653
                    (column) => column.name === oldColumn.name,
28✔
1654
                )
28✔
1655
                clonedTable.columns[
28✔
1656
                    clonedTable.columns.indexOf(oldTableColumn!)
28✔
1657
                ].name = newColumn.name
28✔
1658
                oldColumn.name = newColumn.name
28✔
1659
            }
28✔
1660

54✔
1661
            if (
54✔
1662
                this.isColumnChanged(oldColumn, newColumn, false, false, false)
54✔
1663
            ) {
54!
1664
                upQueries.push(
×
1665
                    new Query(
×
1666
                        `ALTER TABLE ${this.escapePath(
×
1667
                            table,
×
1668
                        )} ALTER COLUMN ${this.buildCreateColumnSql(
×
1669
                            table,
×
1670
                            newColumn,
×
1671
                            true,
×
1672
                            false,
×
1673
                            true,
×
1674
                        )}`,
×
1675
                    ),
×
1676
                )
×
1677
                downQueries.push(
×
1678
                    new Query(
×
1679
                        `ALTER TABLE ${this.escapePath(
×
1680
                            table,
×
1681
                        )} ALTER COLUMN ${this.buildCreateColumnSql(
×
1682
                            table,
×
1683
                            oldColumn,
×
1684
                            true,
×
1685
                            false,
×
1686
                            true,
×
1687
                        )}`,
×
1688
                    ),
×
1689
                )
×
1690
            }
×
1691

54✔
1692
            if (this.isEnumChanged(oldColumn, newColumn)) {
54✔
1693
                const oldExpression = this.getEnumExpression(oldColumn)
2✔
1694
                const oldCheck = new TableCheck({
2✔
1695
                    name: this.connection.namingStrategy.checkConstraintName(
2✔
1696
                        table,
2✔
1697
                        oldExpression,
2✔
1698
                        true,
2✔
1699
                    ),
2✔
1700
                    expression: oldExpression,
2✔
1701
                })
2✔
1702

2✔
1703
                const newExpression = this.getEnumExpression(newColumn)
2✔
1704
                const newCheck = new TableCheck({
2✔
1705
                    name: this.connection.namingStrategy.checkConstraintName(
2✔
1706
                        table,
2✔
1707
                        newExpression,
2✔
1708
                        true,
2✔
1709
                    ),
2✔
1710
                    expression: newExpression,
2✔
1711
                })
2✔
1712

2✔
1713
                upQueries.push(this.dropCheckConstraintSql(table, oldCheck))
2✔
1714
                upQueries.push(this.createCheckConstraintSql(table, newCheck))
2✔
1715

2✔
1716
                downQueries.push(this.dropCheckConstraintSql(table, newCheck))
2✔
1717
                downQueries.push(this.createCheckConstraintSql(table, oldCheck))
2✔
1718
            }
2✔
1719

54✔
1720
            if (newColumn.isPrimary !== oldColumn.isPrimary) {
54✔
1721
                const primaryColumns = clonedTable.primaryColumns
8✔
1722

8✔
1723
                // if primary column state changed, we must always drop existed constraint.
8✔
1724
                if (primaryColumns.length > 0) {
8✔
1725
                    const pkName = primaryColumns[0].primaryKeyConstraintName
6✔
1726
                        ? primaryColumns[0].primaryKeyConstraintName
6!
1727
                        : this.connection.namingStrategy.primaryKeyName(
6✔
1728
                              clonedTable,
6✔
1729
                              primaryColumns.map((column) => column.name),
6✔
1730
                          )
6✔
1731

6✔
1732
                    const columnNames = primaryColumns
6✔
1733
                        .map((column) => `"${column.name}"`)
6✔
1734
                        .join(", ")
6✔
1735
                    upQueries.push(
6✔
1736
                        new Query(
6✔
1737
                            `ALTER TABLE ${this.escapePath(
6✔
1738
                                table,
6✔
1739
                            )} DROP CONSTRAINT "${pkName}"`,
6✔
1740
                        ),
6✔
1741
                    )
6✔
1742
                    downQueries.push(
6✔
1743
                        new Query(
6✔
1744
                            `ALTER TABLE ${this.escapePath(
6✔
1745
                                table,
6✔
1746
                            )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
6✔
1747
                        ),
6✔
1748
                    )
6✔
1749
                }
6✔
1750

8✔
1751
                if (newColumn.isPrimary === true) {
8✔
1752
                    primaryColumns.push(newColumn)
4✔
1753
                    // update column in table
4✔
1754
                    const column = clonedTable.columns.find(
4✔
1755
                        (column) => column.name === newColumn.name,
4✔
1756
                    )
4✔
1757
                    column!.isPrimary = true
4✔
1758
                    const pkName = primaryColumns[0].primaryKeyConstraintName
4✔
1759
                        ? primaryColumns[0].primaryKeyConstraintName
4!
1760
                        : this.connection.namingStrategy.primaryKeyName(
4✔
1761
                              clonedTable,
4✔
1762
                              primaryColumns.map((column) => column.name),
4✔
1763
                          )
4✔
1764

4✔
1765
                    const columnNames = primaryColumns
4✔
1766
                        .map((column) => `"${column.name}"`)
4✔
1767
                        .join(", ")
4✔
1768
                    upQueries.push(
4✔
1769
                        new Query(
4✔
1770
                            `ALTER TABLE ${this.escapePath(
4✔
1771
                                table,
4✔
1772
                            )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
4✔
1773
                        ),
4✔
1774
                    )
4✔
1775
                    downQueries.push(
4✔
1776
                        new Query(
4✔
1777
                            `ALTER TABLE ${this.escapePath(
4✔
1778
                                table,
4✔
1779
                            )} DROP CONSTRAINT "${pkName}"`,
4✔
1780
                        ),
4✔
1781
                    )
4✔
1782
                } else {
4✔
1783
                    const primaryColumn = primaryColumns.find(
4✔
1784
                        (c) => c.name === newColumn.name,
4✔
1785
                    )
4✔
1786
                    primaryColumns.splice(
4✔
1787
                        primaryColumns.indexOf(primaryColumn!),
4✔
1788
                        1,
4✔
1789
                    )
4✔
1790

4✔
1791
                    // update column in table
4✔
1792
                    const column = clonedTable.columns.find(
4✔
1793
                        (column) => column.name === newColumn.name,
4✔
1794
                    )
4✔
1795
                    column!.isPrimary = false
4✔
1796

4✔
1797
                    // if we have another primary keys, we must recreate constraint.
4✔
1798
                    if (primaryColumns.length > 0) {
4✔
1799
                        const pkName = primaryColumns[0]
2✔
1800
                            .primaryKeyConstraintName
2✔
1801
                            ? primaryColumns[0].primaryKeyConstraintName
2!
1802
                            : this.connection.namingStrategy.primaryKeyName(
2✔
1803
                                  clonedTable,
2✔
1804
                                  primaryColumns.map((column) => column.name),
2✔
1805
                              )
2✔
1806

2✔
1807
                        const columnNames = primaryColumns
2✔
1808
                            .map((column) => `"${column.name}"`)
2✔
1809
                            .join(", ")
2✔
1810
                        upQueries.push(
2✔
1811
                            new Query(
2✔
1812
                                `ALTER TABLE ${this.escapePath(
2✔
1813
                                    table,
2✔
1814
                                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
2✔
1815
                            ),
2✔
1816
                        )
2✔
1817
                        downQueries.push(
2✔
1818
                            new Query(
2✔
1819
                                `ALTER TABLE ${this.escapePath(
2✔
1820
                                    table,
2✔
1821
                                )} DROP CONSTRAINT "${pkName}"`,
2✔
1822
                            ),
2✔
1823
                        )
2✔
1824
                    }
2✔
1825
                }
4✔
1826
            }
8✔
1827

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

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

4✔
1918
                if (
4✔
1919
                    newColumn.default !== null &&
4✔
1920
                    newColumn.default !== undefined
4✔
1921
                ) {
4✔
1922
                    const defaultName =
4✔
1923
                        this.connection.namingStrategy.defaultConstraintName(
4✔
1924
                            table,
4✔
1925
                            newColumn.name,
4✔
1926
                        )
4✔
1927
                    upQueries.push(
4✔
1928
                        new Query(
4✔
1929
                            `ALTER TABLE ${this.escapePath(
4✔
1930
                                table,
4✔
1931
                            )} ADD CONSTRAINT "${defaultName}" DEFAULT ${
4✔
1932
                                newColumn.default
4✔
1933
                            } FOR "${newColumn.name}"`,
4✔
1934
                        ),
4✔
1935
                    )
4✔
1936
                    downQueries.push(
4✔
1937
                        new Query(
4✔
1938
                            `ALTER TABLE ${this.escapePath(
4✔
1939
                                table,
4✔
1940
                            )} DROP CONSTRAINT "${defaultName}"`,
4✔
1941
                        ),
4✔
1942
                    )
4✔
1943
                }
4✔
1944
            }
4✔
1945

54✔
1946
            await this.executeQueries(upQueries, downQueries)
54✔
1947
            this.replaceCachedTable(table, clonedTable)
54✔
1948
        }
54✔
1949
    }
104✔
1950

28✔
1951
    /**
28✔
1952
     * Changes a column in the table.
28✔
1953
     * @param tableOrName
28✔
1954
     * @param changedColumns
28✔
1955
     */
28✔
1956
    async changeColumns(
28✔
1957
        tableOrName: Table | string,
46✔
1958
        changedColumns: { newColumn: TableColumn; oldColumn: TableColumn }[],
46✔
1959
    ): Promise<void> {
46✔
1960
        for (const { oldColumn, newColumn } of changedColumns) {
46✔
1961
            await this.changeColumn(tableOrName, oldColumn, newColumn)
64✔
1962
        }
64✔
1963
    }
46✔
1964

28✔
1965
    /**
28✔
1966
     * Drops column in the table.
28✔
1967
     * @param tableOrName
28✔
1968
     * @param columnOrName
28✔
1969
     * @param ifExists
28✔
1970
     */
28✔
1971
    async dropColumn(
28✔
1972
        tableOrName: Table | string,
88✔
1973
        columnOrName: TableColumn | string,
88✔
1974
        ifExists?: boolean,
88✔
1975
    ): Promise<void> {
88✔
1976
        const table = InstanceChecker.isTable(tableOrName)
88✔
1977
            ? tableOrName
88✔
1978
            : await this.getCachedTable(tableOrName)
88✔
1979
        const column = InstanceChecker.isTableColumn(columnOrName)
8✔
1980
            ? columnOrName
88✔
1981
            : table.findColumnByName(columnOrName)
88✔
1982
        if (!column) {
88✔
1983
            if (ifExists) return
4✔
1984
            throw new TypeORMError(
2✔
1985
                `Column "${columnOrName}" was not found in table "${table.name}"`,
2✔
1986
            )
2✔
1987
        }
2✔
1988

84✔
1989
        const clonedTable = table.clone()
84✔
1990
        const upQueries: Query[] = []
84✔
1991
        const downQueries: Query[] = []
84✔
1992

84✔
1993
        // drop primary key constraint
84✔
1994
        if (column.isPrimary) {
88✔
1995
            const pkName = column.primaryKeyConstraintName
14✔
1996
                ? column.primaryKeyConstraintName
14!
1997
                : this.connection.namingStrategy.primaryKeyName(
14✔
1998
                      clonedTable,
14✔
1999
                      clonedTable.primaryColumns.map((column) => column.name),
14✔
2000
                  )
14✔
2001

14✔
2002
            const columnNames = clonedTable.primaryColumns
14✔
2003
                .map((primaryColumn) => `"${primaryColumn.name}"`)
14✔
2004
                .join(", ")
14✔
2005

14✔
2006
            upQueries.push(
14✔
2007
                new Query(
14✔
2008
                    `ALTER TABLE ${this.escapePath(
14✔
2009
                        clonedTable,
14✔
2010
                    )} DROP CONSTRAINT "${pkName}"`,
14✔
2011
                ),
14✔
2012
            )
14✔
2013
            downQueries.push(
14✔
2014
                new Query(
14✔
2015
                    `ALTER TABLE ${this.escapePath(
14✔
2016
                        clonedTable,
14✔
2017
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
14✔
2018
                ),
14✔
2019
            )
14✔
2020

14✔
2021
            // update column in table
14✔
2022
            const tableColumn = clonedTable.findColumnByName(column.name)
14✔
2023
            tableColumn!.isPrimary = false
14✔
2024

14✔
2025
            // if primary key have multiple columns, we must recreate it without dropped column
14✔
2026
            if (clonedTable.primaryColumns.length > 0) {
14✔
2027
                const pkName = clonedTable.primaryColumns[0]
2✔
2028
                    .primaryKeyConstraintName
2✔
2029
                    ? clonedTable.primaryColumns[0].primaryKeyConstraintName
2!
2030
                    : this.connection.namingStrategy.primaryKeyName(
2✔
2031
                          clonedTable,
2✔
2032
                          clonedTable.primaryColumns.map(
2✔
2033
                              (column) => column.name,
2✔
2034
                          ),
2✔
2035
                      )
2✔
2036

2✔
2037
                const columnNames = clonedTable.primaryColumns
2✔
2038
                    .map((primaryColumn) => `"${primaryColumn.name}"`)
2✔
2039
                    .join(", ")
2✔
2040
                upQueries.push(
2✔
2041
                    new Query(
2✔
2042
                        `ALTER TABLE ${this.escapePath(
2✔
2043
                            clonedTable,
2✔
2044
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
2✔
2045
                    ),
2✔
2046
                )
2✔
2047
                downQueries.push(
2✔
2048
                    new Query(
2✔
2049
                        `ALTER TABLE ${this.escapePath(
2✔
2050
                            clonedTable,
2✔
2051
                        )} DROP CONSTRAINT "${pkName}"`,
2✔
2052
                    ),
2✔
2053
                )
2✔
2054
            }
2✔
2055
        }
14✔
2056

84✔
2057
        // drop column index
84✔
2058
        const columnIndex = clonedTable.indices.find(
84✔
2059
            (index) =>
84✔
2060
                index.columnNames.length === 1 &&
2✔
2061
                index.columnNames[0] === column.name,
84✔
2062
        )
84✔
2063
        if (columnIndex) {
88!
2064
            clonedTable.indices.splice(
×
2065
                clonedTable.indices.indexOf(columnIndex),
×
2066
                1,
×
2067
            )
×
2068
            upQueries.push(this.dropIndexSql(table, columnIndex))
×
2069
            downQueries.push(this.createIndexSql(table, columnIndex))
×
2070
        }
×
2071

84✔
2072
        // drop column check
84✔
2073
        const columnCheck = clonedTable.checks.find(
84✔
2074
            (check) =>
84✔
2075
                !!check.columnNames &&
32✔
2076
                check.columnNames.length === 1 &&
32✔
2077
                check.columnNames[0] === column.name,
84✔
2078
        )
84✔
2079
        if (columnCheck) {
88✔
2080
            clonedTable.checks.splice(
4✔
2081
                clonedTable.checks.indexOf(columnCheck),
4✔
2082
                1,
4✔
2083
            )
4✔
2084
            upQueries.push(this.dropCheckConstraintSql(table, columnCheck))
4✔
2085
            downQueries.push(this.createCheckConstraintSql(table, columnCheck))
4✔
2086
        }
4✔
2087

84✔
2088
        // drop column unique
84✔
2089
        const columnUnique = clonedTable.uniques.find(
84✔
2090
            (unique) =>
84✔
2091
                unique.columnNames.length === 1 &&
60✔
2092
                unique.columnNames[0] === column.name,
84✔
2093
        )
84✔
2094
        if (columnUnique) {
88✔
2095
            clonedTable.uniques.splice(
6✔
2096
                clonedTable.uniques.indexOf(columnUnique),
6✔
2097
                1,
6✔
2098
            )
6✔
2099
            upQueries.push(this.dropUniqueConstraintSql(table, columnUnique))
6✔
2100
            downQueries.push(
6✔
2101
                this.createUniqueConstraintSql(table, columnUnique),
6✔
2102
            )
6✔
2103
        }
6✔
2104

84✔
2105
        // drop default constraint
84✔
2106
        if (column.default !== null && column.default !== undefined) {
88✔
2107
            const defaultName =
12✔
2108
                this.connection.namingStrategy.defaultConstraintName(
12✔
2109
                    table,
12✔
2110
                    column.name,
12✔
2111
                )
12✔
2112
            upQueries.push(
12✔
2113
                new Query(
12✔
2114
                    `ALTER TABLE ${this.escapePath(
12✔
2115
                        table,
12✔
2116
                    )} DROP CONSTRAINT "${defaultName}"`,
12✔
2117
                ),
12✔
2118
            )
12✔
2119
            downQueries.push(
12✔
2120
                new Query(
12✔
2121
                    `ALTER TABLE ${this.escapePath(
12✔
2122
                        table,
12✔
2123
                    )} ADD CONSTRAINT "${defaultName}" DEFAULT ${
12✔
2124
                        column.default
12✔
2125
                    } FOR "${column.name}"`,
12✔
2126
                ),
12✔
2127
            )
12✔
2128
        }
12✔
2129

84✔
2130
        if (column.generatedType && column.asExpression) {
88✔
2131
            const parsedTableName = this.driver.parseTableName(table)
8✔
2132

8✔
2133
            if (!parsedTableName.schema) {
8!
2134
                parsedTableName.schema = await this.getCurrentSchema()
×
2135
            }
×
2136

8✔
2137
            const deleteQuery = this.deleteTypeormMetadataSql({
8✔
2138
                database: parsedTableName.database,
8✔
2139
                schema: parsedTableName.schema,
8✔
2140
                table: parsedTableName.tableName,
8✔
2141
                type: MetadataTableType.GENERATED_COLUMN,
8✔
2142
                name: column.name,
8✔
2143
            })
8✔
2144
            const insertQuery = this.insertTypeormMetadataSql({
8✔
2145
                database: parsedTableName.database,
8✔
2146
                schema: parsedTableName.schema,
8✔
2147
                table: parsedTableName.tableName,
8✔
2148
                type: MetadataTableType.GENERATED_COLUMN,
8✔
2149
                name: column.name,
8✔
2150
                value: column.asExpression,
8✔
2151
            })
8✔
2152

8✔
2153
            upQueries.push(deleteQuery)
8✔
2154
            downQueries.push(insertQuery)
8✔
2155
        }
8✔
2156

84✔
2157
        upQueries.push(
84✔
2158
            new Query(
84✔
2159
                `ALTER TABLE ${this.escapePath(table)} DROP COLUMN "${
84✔
2160
                    column.name
84✔
2161
                }"`,
84✔
2162
            ),
84✔
2163
        )
84✔
2164
        downQueries.push(
84✔
2165
            new Query(
84✔
2166
                `ALTER TABLE ${this.escapePath(
84✔
2167
                    table,
84✔
2168
                )} ADD ${this.buildCreateColumnSql(
84✔
2169
                    table,
84✔
2170
                    column,
84✔
2171
                    false,
84✔
2172
                    false,
84✔
2173
                )}`,
84✔
2174
            ),
84✔
2175
        )
84✔
2176

84✔
2177
        await this.executeQueries(upQueries, downQueries)
84✔
2178

84✔
2179
        clonedTable.removeColumn(column)
84✔
2180
        this.replaceCachedTable(table, clonedTable)
84✔
2181
    }
84✔
2182

28✔
2183
    /**
28✔
2184
     * Drops the columns in the table.
28✔
2185
     * @param tableOrName
28✔
2186
     * @param columns
28✔
2187
     * @param ifExists
28✔
2188
     */
28✔
2189
    async dropColumns(
28✔
2190
        tableOrName: Table | string,
10✔
2191
        columns: TableColumn[] | string[],
10✔
2192
        ifExists?: boolean,
10✔
2193
    ): Promise<void> {
10✔
2194
        for (const column of [...columns]) {
10✔
2195
            await this.dropColumn(tableOrName, column, ifExists)
26✔
2196
        }
26✔
2197
    }
10✔
2198

28✔
2199
    /**
28✔
2200
     * Creates a new primary key.
28✔
2201
     * @param tableOrName
28✔
2202
     * @param columnNames
28✔
2203
     * @param constraintName
28✔
2204
     */
28✔
2205
    async createPrimaryKey(
28✔
2206
        tableOrName: Table | string,
4✔
2207
        columnNames: string[],
4✔
2208
        constraintName?: string,
4✔
2209
    ): Promise<void> {
4✔
2210
        const table = InstanceChecker.isTable(tableOrName)
4✔
2211
            ? tableOrName
4!
2212
            : await this.getCachedTable(tableOrName)
4✔
2213
        const clonedTable = table.clone()
4✔
2214

4✔
2215
        const up = this.createPrimaryKeySql(table, columnNames, constraintName)
4✔
2216

4✔
2217
        // mark columns as primary, because dropPrimaryKeySql build constraint name from table primary column names.
4✔
2218
        clonedTable.columns.forEach((column) => {
4✔
2219
            if (columnNames.find((columnName) => columnName === column.name))
10✔
2220
                column.isPrimary = true
10✔
2221
        })
4✔
2222
        const down = this.dropPrimaryKeySql(clonedTable)
4✔
2223

4✔
2224
        await this.executeQueries(up, down)
4✔
2225
        this.replaceCachedTable(table, clonedTable)
4✔
2226
    }
4✔
2227

28✔
2228
    /**
28✔
2229
     * Updates composite primary keys.
28✔
2230
     * @param tableOrName
28✔
2231
     * @param columns
28✔
2232
     */
28✔
2233
    async updatePrimaryKeys(
28✔
2234
        tableOrName: Table | string,
6✔
2235
        columns: TableColumn[],
6✔
2236
    ): Promise<void> {
6✔
2237
        const table = InstanceChecker.isTable(tableOrName)
6✔
2238
            ? tableOrName
6✔
2239
            : await this.getCachedTable(tableOrName)
6!
2240
        const clonedTable = table.clone()
×
2241
        const columnNames = columns.map((column) => column.name)
✔
2242
        const upQueries: Query[] = []
×
2243
        const downQueries: Query[] = []
×
2244

×
2245
        // if table already have primary columns, we must drop them.
×
2246
        const primaryColumns = clonedTable.primaryColumns
×
2247
        if (primaryColumns.length > 0) {
6✔
2248
            const pkName = primaryColumns[0].primaryKeyConstraintName
6✔
2249
                ? primaryColumns[0].primaryKeyConstraintName
6!
2250
                : this.connection.namingStrategy.primaryKeyName(
6✔
2251
                      clonedTable,
6✔
2252
                      primaryColumns.map((column) => column.name),
6✔
2253
                  )
6✔
2254

6✔
2255
            const columnNamesString = primaryColumns
6✔
2256
                .map((column) => `"${column.name}"`)
6✔
2257
                .join(", ")
6✔
2258

6✔
2259
            upQueries.push(
6✔
2260
                new Query(
6✔
2261
                    `ALTER TABLE ${this.escapePath(
6✔
2262
                        table,
6✔
2263
                    )} DROP CONSTRAINT "${pkName}"`,
6✔
2264
                ),
6✔
2265
            )
6✔
2266
            downQueries.push(
6✔
2267
                new Query(
6✔
2268
                    `ALTER TABLE ${this.escapePath(
6✔
2269
                        table,
6✔
2270
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
6✔
2271
                ),
6✔
2272
            )
6✔
2273
        }
6✔
2274

6✔
2275
        // update columns in table.
6✔
2276
        clonedTable.columns
6✔
2277
            .filter((column) => columnNames.indexOf(column.name) !== -1)
6✔
2278
            .forEach((column) => {
6✔
2279
                column.isPrimary = true
12✔
2280
            })
6✔
2281

6✔
2282
        const pkName = primaryColumns[0].primaryKeyConstraintName
6✔
2283
            ? primaryColumns[0].primaryKeyConstraintName
6!
2284
            : this.connection.namingStrategy.primaryKeyName(
6✔
2285
                  clonedTable,
6✔
2286
                  columnNames,
6✔
2287
              )
6✔
2288

6✔
2289
        const columnNamesString = columnNames
6✔
2290
            .map((columnName) => `"${columnName}"`)
6✔
2291
            .join(", ")
6✔
2292

6✔
2293
        upQueries.push(
6✔
2294
            new Query(
6✔
2295
                `ALTER TABLE ${this.escapePath(
6✔
2296
                    table,
6✔
2297
                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
6✔
2298
            ),
6✔
2299
        )
6✔
2300
        downQueries.push(
6✔
2301
            new Query(
6✔
2302
                `ALTER TABLE ${this.escapePath(
6✔
2303
                    table,
6✔
2304
                )} DROP CONSTRAINT "${pkName}"`,
6✔
2305
            ),
6✔
2306
        )
6✔
2307

6✔
2308
        await this.executeQueries(upQueries, downQueries)
6✔
2309
        this.replaceCachedTable(table, clonedTable)
6✔
2310
    }
6✔
2311

28✔
2312
    /**
28✔
2313
     * Drops a primary key.
28✔
2314
     * @param tableOrName
28✔
2315
     * @param constraintName
28✔
2316
     * @param ifExists
28✔
2317
     */
28✔
2318
    async dropPrimaryKey(
28✔
2319
        tableOrName: Table | string,
8✔
2320
        constraintName?: string,
8✔
2321
        ifExists?: boolean,
8✔
2322
    ): Promise<void> {
8✔
2323
        const table = InstanceChecker.isTable(tableOrName)
8✔
2324
            ? tableOrName
8✔
2325
            : await this.getCachedTable(tableOrName)
8✔
2326
        if (ifExists && table.primaryColumns.length === 0) return
8!
2327

8✔
2328
        const up = this.dropPrimaryKeySql(table)
8✔
2329
        const down = this.createPrimaryKeySql(
8✔
2330
            table,
8✔
2331
            table.primaryColumns.map((column) => column.name),
8✔
2332
            constraintName,
8✔
2333
        )
8✔
2334
        await this.executeQueries(up, down)
8✔
2335
        table.primaryColumns.forEach((column) => {
8✔
2336
            column.isPrimary = false
8✔
2337
        })
8✔
2338
    }
8✔
2339

28✔
2340
    /**
28✔
2341
     * Creates a new unique constraint.
28✔
2342
     * @param tableOrName
28✔
2343
     * @param uniqueConstraint
28✔
2344
     */
28✔
2345
    async createUniqueConstraint(
28✔
2346
        tableOrName: Table | string,
16✔
2347
        uniqueConstraint: TableUnique,
16✔
2348
    ): Promise<void> {
16✔
2349
        const table = InstanceChecker.isTable(tableOrName)
16✔
2350
            ? tableOrName
16✔
2351
            : await this.getCachedTable(tableOrName)
16✔
2352

12✔
2353
        // new unique constraint may be passed without name. In this case we generate unique name manually.
12✔
2354
        if (!uniqueConstraint.name)
12✔
2355
            uniqueConstraint.name =
16✔
2356
                this.connection.namingStrategy.uniqueConstraintName(
4✔
2357
                    table,
4✔
2358
                    uniqueConstraint.columnNames,
4✔
2359
                )
4✔
2360

16✔
2361
        const up = this.createUniqueConstraintSql(table, uniqueConstraint)
16✔
2362
        const down = this.dropUniqueConstraintSql(table, uniqueConstraint)
16✔
2363
        await this.executeQueries(up, down)
16✔
2364
        table.addUniqueConstraint(uniqueConstraint)
16✔
2365
    }
16✔
2366

28✔
2367
    /**
28✔
2368
     * Creates a new unique constraints.
28✔
2369
     * @param tableOrName
28✔
2370
     * @param uniqueConstraints
28✔
2371
     */
28✔
2372
    async createUniqueConstraints(
28✔
2373
        tableOrName: Table | string,
6✔
2374
        uniqueConstraints: TableUnique[],
6✔
2375
    ): Promise<void> {
6✔
2376
        const promises = uniqueConstraints.map((uniqueConstraint) =>
6✔
2377
            this.createUniqueConstraint(tableOrName, uniqueConstraint),
6✔
2378
        )
6✔
2379
        await Promise.all(promises)
6✔
2380
    }
6✔
2381

28✔
2382
    /**
28✔
2383
     * Drops unique constraint.
28✔
2384
     * @param tableOrName
28✔
2385
     * @param uniqueOrName
28✔
2386
     * @param ifExists
28✔
2387
     */
28✔
2388
    async dropUniqueConstraint(
28✔
2389
        tableOrName: Table | string,
18✔
2390
        uniqueOrName: TableUnique | string,
18✔
2391
        ifExists?: boolean,
18✔
2392
    ): Promise<void> {
18✔
2393
        const table = InstanceChecker.isTable(tableOrName)
18✔
2394
            ? tableOrName
18✔
2395
            : await this.getCachedTable(tableOrName)
18✔
2396
        const uniqueConstraint = InstanceChecker.isTableUnique(uniqueOrName)
10✔
2397
            ? uniqueOrName
18✔
2398
            : table.uniques.find((u) => u.name === uniqueOrName)
18✔
2399
        if (!uniqueConstraint) {
18✔
2400
            if (ifExists) return
2✔
2401
            throw new TypeORMError(
×
2402
                `Supplied unique constraint was not found in table ${table.name}`,
×
2403
            )
×
2404
        }
×
2405

16✔
2406
        const up = this.dropUniqueConstraintSql(table, uniqueConstraint)
16✔
2407
        const down = this.createUniqueConstraintSql(table, uniqueConstraint)
16✔
2408
        await this.executeQueries(up, down)
16✔
2409
        table.removeUniqueConstraint(uniqueConstraint)
16✔
2410
    }
16✔
2411

28✔
2412
    /**
28✔
2413
     * Drops unique constraints.
28✔
2414
     * @param tableOrName
28✔
2415
     * @param uniqueConstraints
28✔
2416
     * @param ifExists
28✔
2417
     */
28✔
2418
    async dropUniqueConstraints(
28✔
2419
        tableOrName: Table | string,
8✔
2420
        uniqueConstraints: TableUnique[],
8✔
2421
        ifExists?: boolean,
8✔
2422
    ): Promise<void> {
8✔
2423
        const promises = uniqueConstraints.map((uniqueConstraint) =>
8✔
2424
            this.dropUniqueConstraint(tableOrName, uniqueConstraint, ifExists),
8✔
2425
        )
8✔
2426
        await Promise.all(promises)
8✔
2427
    }
8✔
2428

28✔
2429
    /**
28✔
2430
     * Creates a new check constraint.
28✔
2431
     * @param tableOrName
28✔
2432
     * @param checkConstraint
28✔
2433
     */
28✔
2434
    async createCheckConstraint(
28✔
2435
        tableOrName: Table | string,
10✔
2436
        checkConstraint: TableCheck,
10✔
2437
    ): Promise<void> {
10✔
2438
        const table = InstanceChecker.isTable(tableOrName)
10✔
2439
            ? tableOrName
10✔
2440
            : await this.getCachedTable(tableOrName)
10✔
2441

6✔
2442
        // new unique constraint may be passed without name. In this case we generate unique name manually.
6✔
2443
        if (!checkConstraint.name)
6✔
2444
            checkConstraint.name =
6✔
2445
                this.connection.namingStrategy.checkConstraintName(
6✔
2446
                    table,
6✔
2447
                    checkConstraint.expression!,
6✔
2448
                )
6✔
2449

10✔
2450
        const up = this.createCheckConstraintSql(table, checkConstraint)
10✔
2451
        const down = this.dropCheckConstraintSql(table, checkConstraint)
10✔
2452
        await this.executeQueries(up, down)
10✔
2453
        table.addCheckConstraint(checkConstraint)
10✔
2454
    }
10✔
2455

28✔
2456
    /**
28✔
2457
     * Creates a new check constraints.
28✔
2458
     * @param tableOrName
28✔
2459
     * @param checkConstraints
28✔
2460
     */
28✔
2461
    async createCheckConstraints(
28✔
2462
        tableOrName: Table | string,
4✔
2463
        checkConstraints: TableCheck[],
4✔
2464
    ): Promise<void> {
4✔
2465
        const promises = checkConstraints.map((checkConstraint) =>
4✔
2466
            this.createCheckConstraint(tableOrName, checkConstraint),
4✔
2467
        )
4✔
2468
        await Promise.all(promises)
4✔
2469
    }
4✔
2470

28✔
2471
    /**
28✔
2472
     * Drops check constraint.
28✔
2473
     * @param tableOrName
28✔
2474
     * @param checkOrName
28✔
2475
     * @param ifExists
28✔
2476
     */
28✔
2477
    async dropCheckConstraint(
28✔
2478
        tableOrName: Table | string,
8✔
2479
        checkOrName: TableCheck | string,
8✔
2480
        ifExists?: boolean,
8✔
2481
    ): Promise<void> {
8✔
2482
        const table = InstanceChecker.isTable(tableOrName)
8✔
2483
            ? tableOrName
8✔
2484
            : await this.getCachedTable(tableOrName)
8✔
2485
        const checkConstraint = InstanceChecker.isTableCheck(checkOrName)
2✔
2486
            ? checkOrName
8✔
2487
            : table.checks.find((c) => c.name === checkOrName)
8✔
2488
        if (!checkConstraint) {
8✔
2489
            if (ifExists) return
2✔
2490
            throw new TypeORMError(
×
2491
                `Supplied check constraint was not found in table ${table.name}`,
×
2492
            )
×
2493
        }
×
2494

6✔
2495
        const up = this.dropCheckConstraintSql(table, checkConstraint)
6✔
2496
        const down = this.createCheckConstraintSql(table, checkConstraint)
6✔
2497
        await this.executeQueries(up, down)
6✔
2498
        table.removeCheckConstraint(checkConstraint)
6✔
2499
    }
6✔
2500

28✔
2501
    /**
28✔
2502
     * Drops check constraints.
28✔
2503
     * @param tableOrName
28✔
2504
     * @param checkConstraints
28✔
2505
     * @param ifExists
28✔
2506
     */
28✔
2507
    async dropCheckConstraints(
28✔
2508
        tableOrName: Table | string,
4✔
2509
        checkConstraints: TableCheck[],
4✔
2510
        ifExists?: boolean,
4✔
2511
    ): Promise<void> {
4✔
2512
        const promises = checkConstraints.map((checkConstraint) =>
4✔
2513
            this.dropCheckConstraint(tableOrName, checkConstraint, ifExists),
4✔
2514
        )
4✔
2515
        await Promise.all(promises)
4✔
2516
    }
4✔
2517

28✔
2518
    /**
28✔
2519
     * Creates a new exclusion constraint.
28✔
2520
     * @param tableOrName
28✔
2521
     * @param exclusionConstraint
28✔
2522
     */
28✔
2523
    async createExclusionConstraint(
28✔
2524
        tableOrName: Table | string,
×
2525
        exclusionConstraint: TableExclusion,
×
2526
    ): Promise<void> {
×
2527
        throw new TypeORMError(
×
2528
            `SqlServer does not support exclusion constraints.`,
×
2529
        )
×
2530
    }
×
2531

28✔
2532
    /**
28✔
2533
     * Creates a new exclusion constraints.
28✔
2534
     * @param tableOrName
28✔
2535
     * @param exclusionConstraints
28✔
2536
     */
28✔
2537
    async createExclusionConstraints(
28✔
2538
        tableOrName: Table | string,
×
2539
        exclusionConstraints: TableExclusion[],
×
2540
    ): Promise<void> {
×
2541
        throw new TypeORMError(
×
2542
            `SqlServer does not support exclusion constraints.`,
×
2543
        )
×
2544
    }
×
2545

28✔
2546
    /**
28✔
2547
     * Drops exclusion constraint.
28✔
2548
     * @param tableOrName
28✔
2549
     * @param exclusionOrName
28✔
2550
     * @param ifExists
28✔
2551
     */
28✔
2552
    async dropExclusionConstraint(
28✔
2553
        tableOrName: Table | string,
×
2554
        exclusionOrName: TableExclusion | string,
×
2555
        ifExists?: boolean,
×
2556
    ): Promise<void> {
×
2557
        throw new TypeORMError(
×
2558
            `SqlServer does not support exclusion constraints.`,
×
2559
        )
×
2560
    }
×
2561

28✔
2562
    /**
28✔
2563
     * Drops exclusion constraints.
28✔
2564
     * @param tableOrName
28✔
2565
     * @param exclusionConstraints
28✔
2566
     * @param ifExists
28✔
2567
     */
28✔
2568
    async dropExclusionConstraints(
28✔
2569
        tableOrName: Table | string,
×
2570
        exclusionConstraints: TableExclusion[],
×
2571
        ifExists?: boolean,
×
2572
    ): Promise<void> {
×
2573
        throw new TypeORMError(
×
2574
            `SqlServer does not support exclusion constraints.`,
×
2575
        )
×
2576
    }
×
2577

28✔
2578
    /**
28✔
2579
     * Creates a new foreign key.
28✔
2580
     * @param tableOrName
28✔
2581
     * @param foreignKey
28✔
2582
     */
28✔
2583
    async createForeignKey(
28✔
2584
        tableOrName: Table | string,
6,766✔
2585
        foreignKey: TableForeignKey,
6,766✔
2586
    ): Promise<void> {
6,766✔
2587
        const table = InstanceChecker.isTable(tableOrName)
6,766✔
2588
            ? tableOrName
6,766✔
2589
            : await this.getCachedTable(tableOrName)
6,766✔
2590
        const metadata = this.connection.hasMetadata(table.name)
10✔
2591
            ? this.connection.getMetadata(table.name)
6,766✔
2592
            : undefined
6,766✔
2593

6,766✔
2594
        if (
6,766✔
2595
            metadata &&
6,766✔
2596
            metadata.treeParentRelation &&
6,766✔
2597
            metadata.treeParentRelation!.isTreeParent &&
6,766✔
2598
            metadata.foreignKeys.find(
778✔
2599
                (foreignKey) => foreignKey.onDelete !== "NO ACTION",
778✔
2600
            )
6,766✔
2601
        )
6,766✔
2602
            throw new TypeORMError(
6,766!
2603
                "SqlServer does not support options in TreeParent.",
×
2604
            )
×
2605

6,766✔
2606
        // new FK may be passed without name. In this case we generate FK name manually.
6,766✔
2607
        if (!foreignKey.name)
6,766✔
2608
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
6,766✔
2609
                table,
2✔
2610
                foreignKey.columnNames,
2✔
2611
                this.getTablePath(foreignKey),
2✔
2612
                foreignKey.referencedColumnNames,
2✔
2613
            )
2✔
2614

6,766✔
2615
        const up = this.createForeignKeySql(table, foreignKey)
6,766✔
2616
        const down = this.dropForeignKeySql(table, foreignKey)
6,766✔
2617
        await this.executeQueries(up, down)
6,766✔
2618
        table.addForeignKey(foreignKey)
6,766✔
2619
    }
6,766✔
2620

28✔
2621
    /**
28✔
2622
     * Creates a new foreign keys.
28✔
2623
     * @param tableOrName
28✔
2624
     * @param foreignKeys
28✔
2625
     */
28✔
2626
    async createForeignKeys(
28✔
2627
        tableOrName: Table | string,
4,216✔
2628
        foreignKeys: TableForeignKey[],
4,216✔
2629
    ): Promise<void> {
4,216✔
2630
        const promises = foreignKeys.map((foreignKey) =>
4,216✔
2631
            this.createForeignKey(tableOrName, foreignKey),
4,216✔
2632
        )
4,216✔
2633
        await Promise.all(promises)
4,216✔
2634
    }
4,216✔
2635

28✔
2636
    /**
28✔
2637
     * Drops a foreign key from the table.
28✔
2638
     * @param tableOrName
28✔
2639
     * @param foreignKeyOrName
28✔
2640
     * @param ifExists
28✔
2641
     */
28✔
2642
    async dropForeignKey(
28✔
2643
        tableOrName: Table | string,
24✔
2644
        foreignKeyOrName: TableForeignKey | string,
24✔
2645
        ifExists?: boolean,
24✔
2646
    ): Promise<void> {
24✔
2647
        const table = InstanceChecker.isTable(tableOrName)
24✔
2648
            ? tableOrName
24✔
2649
            : await this.getCachedTable(tableOrName)
24✔
2650
        const foreignKey = InstanceChecker.isTableForeignKey(foreignKeyOrName)
10✔
2651
            ? foreignKeyOrName
24✔
2652
            : table.foreignKeys.find((fk) => fk.name === foreignKeyOrName)
24✔
2653
        if (!foreignKey) {
24✔
2654
            if (ifExists) return
2✔
2655
            throw new TypeORMError(
×
2656
                `Supplied foreign key was not found in table ${table.name}`,
×
2657
            )
×
2658
        }
×
2659

22✔
2660
        if (!foreignKey.name) {
24!
2661
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
×
2662
                table,
×
2663
                foreignKey.columnNames,
×
2664
                this.getTablePath(foreignKey),
×
2665
                foreignKey.referencedColumnNames,
×
2666
            )
×
2667
        }
×
2668

22✔
2669
        const up = this.dropForeignKeySql(table, foreignKey)
22✔
2670
        const down = this.createForeignKeySql(table, foreignKey)
22✔
2671
        await this.executeQueries(up, down)
22✔
2672
        table.removeForeignKey(foreignKey)
22✔
2673
    }
22✔
2674

28✔
2675
    /**
28✔
2676
     * Drops a foreign keys from the table.
28✔
2677
     * @param tableOrName
28✔
2678
     * @param foreignKeys
28✔
2679
     * @param ifExists
28✔
2680
     */
28✔
2681
    async dropForeignKeys(
28✔
2682
        tableOrName: Table | string,
14✔
2683
        foreignKeys: TableForeignKey[],
14✔
2684
        ifExists?: boolean,
14✔
2685
    ): Promise<void> {
14✔
2686
        const promises = foreignKeys.map((foreignKey) =>
14✔
2687
            this.dropForeignKey(tableOrName, foreignKey, ifExists),
14✔
2688
        )
14✔
2689
        await Promise.all(promises)
14✔
2690
    }
14✔
2691

28✔
2692
    /**
28✔
2693
     * Creates a new index.
28✔
2694
     * @param tableOrName
28✔
2695
     * @param index
28✔
2696
     */
28✔
2697
    async createIndex(
28✔
2698
        tableOrName: Table | string,
28✔
2699
        index: TableIndex,
28✔
2700
    ): Promise<void> {
28✔
2701
        const table = InstanceChecker.isTable(tableOrName)
28✔
2702
            ? tableOrName
28✔
2703
            : await this.getCachedTable(tableOrName)
28✔
2704

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

28✔
2708
        const up = this.createIndexSql(table, index)
28✔
2709
        const down = this.dropIndexSql(table, index)
28✔
2710
        await this.executeQueries(up, down)
28✔
2711
        table.addIndex(index)
28✔
2712
    }
28✔
2713

28✔
2714
    /**
28✔
2715
     * Creates a new indices
28✔
2716
     * @param tableOrName
28✔
2717
     * @param indices
28✔
2718
     */
28✔
2719
    async createIndices(
28✔
2720
        tableOrName: Table | string,
14✔
2721
        indices: TableIndex[],
14✔
2722
    ): Promise<void> {
14✔
2723
        const promises = indices.map((index) =>
14✔
2724
            this.createIndex(tableOrName, index),
14✔
2725
        )
14✔
2726
        await Promise.all(promises)
14✔
2727
    }
14✔
2728

28✔
2729
    /**
28✔
2730
     * Drops an index.
28✔
2731
     * @param tableOrName
28✔
2732
     * @param indexOrName
28✔
2733
     * @param ifExists
28✔
2734
     */
28✔
2735
    async dropIndex(
28✔
2736
        tableOrName: Table | string,
34✔
2737
        indexOrName: TableIndex | string,
34✔
2738
        ifExists?: boolean,
34✔
2739
    ): Promise<void> {
34✔
2740
        const table = InstanceChecker.isTable(tableOrName)
34✔
2741
            ? tableOrName
34✔
2742
            : await this.getCachedTable(tableOrName)
34✔
2743
        const index = InstanceChecker.isTableIndex(indexOrName)
12✔
2744
            ? indexOrName
34✔
2745
            : table.indices.find((i) => i.name === indexOrName)
34✔
2746
        if (!index) {
34✔
2747
            if (ifExists) return
2✔
2748
            throw new TypeORMError(
×
2749
                `Supplied index was not found in table ${table.name}`,
×
2750
            )
×
2751
        }
×
2752

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

32✔
2756
        const up = this.dropIndexSql(table, index)
32✔
2757
        const down = this.createIndexSql(table, index)
32✔
2758
        await this.executeQueries(up, down)
32✔
2759
        table.removeIndex(index)
32✔
2760
    }
32✔
2761

28✔
2762
    /**
28✔
2763
     * Drops an indices from the table.
28✔
2764
     * @param tableOrName
28✔
2765
     * @param indices
28✔
2766
     * @param ifExists
28✔
2767
     */
28✔
2768
    async dropIndices(
28✔
2769
        tableOrName: Table | string,
4✔
2770
        indices: TableIndex[],
4✔
2771
        ifExists?: boolean,
4✔
2772
    ): Promise<void> {
4✔
2773
        const promises = indices.map((index) =>
4✔
2774
            this.dropIndex(tableOrName, index, ifExists),
4✔
2775
        )
4✔
2776
        await Promise.all(promises)
4✔
2777
    }
4✔
2778

28✔
2779
    /**
28✔
2780
     * Clears all table contents.
28✔
2781
     * Note: this operation uses SQL's TRUNCATE query which cannot be reverted in transactions.
28✔
2782
     * @param tablePath
28✔
2783
     * @param options
28✔
2784
     * @param options.cascade
28✔
2785
     */
28✔
2786
    async clearTable(
28✔
2787
        tablePath: string,
10✔
2788
        options?: { cascade?: boolean },
10✔
2789
    ): Promise<void> {
10✔
2790
        if (options?.cascade) {
10✔
2791
            throw new TypeORMError(
2✔
2792
                `SqlServer does not support clearing table with cascade option`,
2✔
2793
            )
2✔
2794
        }
2✔
2795
        await this.query(`TRUNCATE TABLE ${this.escapePath(tablePath)}`)
8✔
2796
    }
6✔
2797

28✔
2798
    /**
28✔
2799
     * Removes all tables from the currently connected database.
28✔
2800
     * @param database
28✔
2801
     */
28✔
2802
    async clearDatabase(database?: string): Promise<void> {
28✔
2803
        if (database) {
2,970✔
2804
            const isDatabaseExist = await this.hasDatabase(database)
2,970✔
2805
            if (!isDatabaseExist) return Promise.resolve()
2,970!
2806
        }
2,970✔
2807

2,970✔
2808
        const isAnotherTransactionActive = this.isTransactionActive
2,970✔
2809
        if (!isAnotherTransactionActive) await this.startTransaction()
2,970✔
2810
        try {
2,970✔
2811
            const allViewsSql = database
2,970✔
2812
                ? `SELECT * FROM "${database}"."INFORMATION_SCHEMA"."VIEWS"`
2,970✔
2813
                : `SELECT * FROM "INFORMATION_SCHEMA"."VIEWS"`
2,970!
2814
            const allViewsResults: ObjectLiteral[] =
2,970✔
2815
                await this.query(allViewsSql)
2,970✔
2816

2,970✔
2817
            await Promise.all(
2,970✔
2818
                allViewsResults.map((viewResult) => {
2,970✔
2819
                    // 'DROP VIEW' does not allow specifying the database name as a prefix to the object name.
16✔
2820
                    const dropTableSql = `DROP VIEW "${viewResult["TABLE_SCHEMA"]}"."${viewResult["TABLE_NAME"]}"`
16✔
2821
                    return this.query(dropTableSql)
16✔
2822
                }),
2,970✔
2823
            )
2,970✔
2824

2,970✔
2825
            const allTablesSql = database
2,970✔
2826
                ? `SELECT * FROM "${database}"."INFORMATION_SCHEMA"."TABLES" WHERE "TABLE_TYPE" = 'BASE TABLE'`
2,970✔
2827
                : `SELECT * FROM "INFORMATION_SCHEMA"."TABLES" WHERE "TABLE_TYPE" = 'BASE TABLE'`
2,970!
2828
            const allTablesResults: ObjectLiteral[] =
2,970✔
2829
                await this.query(allTablesSql)
2,970✔
2830

2,970✔
2831
            if (allTablesResults.length > 0) {
2,970✔
2832
                const tablesByCatalog: {
2,926✔
2833
                    [key: string]: {
2,926✔
2834
                        TABLE_NAME: string
2,926✔
2835
                        TABLE_SCHEMA: string
2,926✔
2836
                    }[]
2,926✔
2837
                } = allTablesResults.reduce(
2,926✔
2838
                    (c, { TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME }) => {
2,926✔
2839
                        c[TABLE_CATALOG] = c[TABLE_CATALOG] || []
10,288✔
2840
                        c[TABLE_CATALOG].push({ TABLE_SCHEMA, TABLE_NAME })
10,288✔
2841
                        return c
10,288✔
2842
                    },
2,926✔
2843
                    {},
2,926✔
2844
                )
2,926✔
2845

2,926✔
2846
                const foreignKeysSql = Object.entries(tablesByCatalog)
2,926✔
2847
                    .map(([TABLE_CATALOG, tables]) => {
2,926✔
2848
                        const conditions = tables
2,926✔
2849
                            .map(({ TABLE_SCHEMA, TABLE_NAME }) => {
2,926✔
2850
                                return `("fk"."referenced_object_id" = OBJECT_ID('"${TABLE_CATALOG}"."${TABLE_SCHEMA}"."${TABLE_NAME}"'))`
10,288✔
2851
                            })
2,926✔
2852
                            .join(" OR ")
2,926✔
2853

2,926✔
2854
                        return `
2,926✔
2855
                        SELECT DISTINCT '${TABLE_CATALOG}' AS                                              "TABLE_CATALOG",
2,926✔
2856
                                        OBJECT_SCHEMA_NAME("fk"."parent_object_id",
2,926✔
2857
                                                           DB_ID('${TABLE_CATALOG}')) AS                   "TABLE_SCHEMA",
2,926✔
2858
                                        OBJECT_NAME("fk"."parent_object_id", DB_ID('${TABLE_CATALOG}')) AS "TABLE_NAME",
2,926✔
2859
                                        "fk"."name" AS                                                     "CONSTRAINT_NAME"
2,926✔
2860
                        FROM "${TABLE_CATALOG}"."sys"."foreign_keys" AS "fk"
2,926✔
2861
                        WHERE (${conditions})
2,926✔
2862
                    `
2,926✔
2863
                    })
2,926✔
2864
                    .join(" UNION ALL ")
2,926✔
2865

2,926✔
2866
                const foreignKeys: {
2,926✔
2867
                    TABLE_CATALOG: string
2,926✔
2868
                    TABLE_SCHEMA: string
2,926✔
2869
                    TABLE_NAME: string
2,926✔
2870
                    CONSTRAINT_NAME: string
2,926✔
2871
                }[] = await this.query(foreignKeysSql)
2,926✔
2872

2,926✔
2873
                await Promise.all(
2,926✔
2874
                    foreignKeys.map(
2,926✔
2875
                        async ({
2,926✔
2876
                            TABLE_CATALOG,
6,732✔
2877
                            TABLE_SCHEMA,
6,732✔
2878
                            TABLE_NAME,
6,732✔
2879
                            CONSTRAINT_NAME,
6,732✔
2880
                        }) => {
6,732✔
2881
                            // Disable the constraint first.
6,732✔
2882
                            await this.query(
6,732✔
2883
                                `ALTER TABLE "${TABLE_CATALOG}"."${TABLE_SCHEMA}"."${TABLE_NAME}" ` +
6,732✔
2884
                                    `NOCHECK CONSTRAINT "${CONSTRAINT_NAME}"`,
6,732✔
2885
                            )
6,732✔
2886

6,732✔
2887
                            await this.query(
6,732✔
2888
                                `ALTER TABLE "${TABLE_CATALOG}"."${TABLE_SCHEMA}"."${TABLE_NAME}" ` +
6,732✔
2889
                                    `DROP CONSTRAINT "${CONSTRAINT_NAME}" -- FROM CLEAR`,
6,732✔
2890
                            )
6,732✔
2891
                        },
2,926✔
2892
                    ),
2,926✔
2893
                )
2,926✔
2894

2,926✔
2895
                await Promise.all(
2,926✔
2896
                    allTablesResults.map(async (tablesResult) => {
2,926✔
2897
                        if (tablesResult["TABLE_NAME"].startsWith("#")) {
10,288!
UNCOV
2898
                            // don't try to drop temporary tables
×
UNCOV
2899
                            return
×
UNCOV
2900
                        }
×
2901

10,288✔
2902
                        const dropTableSql = `DROP TABLE "${tablesResult["TABLE_CATALOG"]}"."${tablesResult["TABLE_SCHEMA"]}"."${tablesResult["TABLE_NAME"]}"`
10,288✔
2903
                        return this.query(dropTableSql)
10,288✔
2904
                    }),
2,926✔
2905
                )
2,926✔
2906
            }
2,926✔
2907

2,970✔
2908
            if (!isAnotherTransactionActive) await this.commitTransaction()
2,970✔
2909
        } catch (error) {
2,970!
2910
            try {
×
2911
                // we throw original error even if rollback thrown an error
×
2912
                if (!isAnotherTransactionActive)
×
2913
                    await this.rollbackTransaction()
×
2914
            } catch (rollbackError) {}
×
2915
            throw error
×
2916
        }
×
2917
    }
2,970✔
2918

28✔
2919
    // -------------------------------------------------------------------------
28✔
2920
    // Protected Methods
28✔
2921
    // -------------------------------------------------------------------------
28✔
2922

28✔
2923
    protected async loadViews(viewPaths?: string[]): Promise<View[]> {
28✔
2924
        const hasTable = await this.hasTable(this.getTypeormMetadataTableName())
3,080✔
2925
        if (!hasTable) {
3,080✔
2926
            return []
3,050✔
2927
        }
3,050✔
2928

30✔
2929
        if (!viewPaths) {
3,080!
2930
            viewPaths = []
×
2931
        }
×
2932

30✔
2933
        const currentSchema = await this.getCurrentSchema()
30✔
2934
        const currentDatabase = await this.getCurrentDatabase()
30✔
2935

30✔
2936
        const dbNames = viewPaths
30✔
2937
            .map((viewPath) => this.driver.parseTableName(viewPath).database)
30✔
2938
            .filter((database) => database)
30✔
2939

30✔
2940
        if (
30✔
2941
            this.driver.database &&
30✔
2942
            !dbNames.find((dbName) => dbName === this.driver.database)
30✔
2943
        )
3,080✔
2944
            dbNames.push(this.driver.database)
3,080✔
2945

30✔
2946
        const viewsCondition = viewPaths
30✔
2947
            .map((viewPath) => {
30✔
2948
                let { schema, tableName: name } =
22✔
2949
                    this.driver.parseTableName(viewPath)
22✔
2950

22✔
2951
                if (!schema) {
22!
2952
                    schema = currentSchema
×
2953
                }
×
2954
                return `("T"."SCHEMA" = '${schema}' AND "T"."NAME" = '${name}')`
22✔
2955
            })
30✔
2956
            .join(" OR ")
30✔
2957

30✔
2958
        const query = dbNames
30✔
2959
            .map((dbName) => {
30✔
2960
                return (
38✔
2961
                    `SELECT "T".*, "V"."CHECK_OPTION" FROM ${this.escapePath(
38✔
2962
                        this.getTypeormMetadataTableName(),
38✔
2963
                    )} "t" ` +
38✔
2964
                    `INNER JOIN "${dbName}"."INFORMATION_SCHEMA"."VIEWS" "V" ON "V"."TABLE_SCHEMA" = "T"."SCHEMA" AND "v"."TABLE_NAME" = "T"."NAME" WHERE "T"."TYPE" = '${
38✔
2965
                        MetadataTableType.VIEW
38✔
2966
                    }' ${viewsCondition ? `AND (${viewsCondition})` : ""}`
38✔
2967
                )
38✔
2968
            })
30✔
2969
            .join(" UNION ALL ")
30✔
2970

30✔
2971
        const dbViews = await this.query(query)
30✔
2972
        return dbViews.map((dbView: any) => {
30✔
2973
            const view = new View()
6✔
2974
            const db =
6✔
2975
                dbView["TABLE_CATALOG"] === currentDatabase
6✔
2976
                    ? undefined
6!
2977
                    : dbView["TABLE_CATALOG"]
6✔
2978
            const schema =
6✔
2979
                dbView["schema"] === currentSchema &&
6✔
2980
                !this.driver.options.schema
6✔
2981
                    ? undefined
6✔
2982
                    : dbView["schema"]
6!
2983
            view.database = dbView["TABLE_CATALOG"]
6✔
2984
            view.schema = dbView["schema"]
6✔
2985
            view.name = this.driver.buildTableName(dbView["name"], schema, db)
6✔
2986
            view.expression = dbView["value"]
6✔
2987
            return view
6✔
2988
        })
30✔
2989
    }
30✔
2990

28✔
2991
    /**
28✔
2992
     * Loads all tables (with given names) from the database and creates a Table from them.
28✔
2993
     * @param tableNames
28✔
2994
     */
28✔
2995
    protected async loadTables(tableNames?: string[]): Promise<Table[]> {
28✔
2996
        // if no tables given then no need to proceed
3,668✔
2997
        if (tableNames && tableNames.length === 0) {
3,668✔
2998
            return []
14✔
2999
        }
14✔
3000

3,654✔
3001
        const currentSchema = await this.getCurrentSchema()
3,654✔
3002
        const currentDatabase = await this.getCurrentDatabase()
3,654✔
3003

3,654✔
3004
        const dbTables: {
3,654✔
3005
            TABLE_CATALOG: string
3,654✔
3006
            TABLE_SCHEMA: string
3,654✔
3007
            TABLE_NAME: string
3,654✔
3008
        }[] = []
3,654✔
3009

3,654✔
3010
        if (!tableNames) {
3,668!
3011
            const databasesSql =
×
3012
                `SELECT DISTINCT "name" ` +
×
3013
                `FROM "master"."dbo"."sysdatabases" ` +
×
3014
                `WHERE "name" NOT IN ('master', 'model', 'msdb')`
×
3015
            const dbDatabases: { name: string }[] =
×
3016
                await this.query(databasesSql)
×
3017

×
3018
            const tablesSql = dbDatabases
×
3019
                .map(({ name }) => {
×
3020
                    return `
×
3021
                    SELECT DISTINCT
×
3022
                        "TABLE_CATALOG", "TABLE_SCHEMA", "TABLE_NAME"
×
3023
                    FROM "${name}"."INFORMATION_SCHEMA"."TABLES"
×
3024
                    WHERE
×
3025
                      "TABLE_TYPE" = 'BASE TABLE'
×
3026
                      AND
×
3027
                      "TABLE_CATALOG" = '${name}'
×
3028
                      AND
×
3029
                      ISNULL(Objectproperty(Object_id("TABLE_CATALOG" + '.' + "TABLE_SCHEMA" + '.' + "TABLE_NAME"), 'IsMSShipped'), 0) = 0
×
3030
                `
×
3031
                })
×
3032
                .join(" UNION ALL ")
×
3033

×
3034
            dbTables.push(...(await this.query(tablesSql)))
×
3035
        } else {
3,668✔
3036
            const tableNamesByCatalog = tableNames
3,654✔
3037
                .map((tableName) => this.driver.parseTableName(tableName))
3,654✔
3038
                .reduce(
3,654✔
3039
                    (c, { database, ...other }) => {
3,654✔
3040
                        database = database || currentDatabase
11,300!
3041
                        c[database] = c[database] || []
11,300✔
3042
                        c[database].push({
11,300✔
3043
                            schema: other.schema || currentSchema,
11,300!
3044
                            tableName: other.tableName,
11,300✔
3045
                        })
11,300✔
3046
                        return c
11,300✔
3047
                    },
3,654✔
3048
                    {} as {
3,654✔
3049
                        [key: string]: { schema: string; tableName: string }[]
3,654✔
3050
                    },
3,654✔
3051
                )
3,654✔
3052

3,654✔
3053
            const tablesSql = Object.entries(tableNamesByCatalog)
3,654✔
3054
                .map(([database, tables]) => {
3,654✔
3055
                    const tablesCondition = tables
3,662✔
3056
                        .map(({ schema, tableName }) => {
3,662✔
3057
                            return `("TABLE_SCHEMA" = '${schema}' AND "TABLE_NAME" = '${tableName}')`
11,300✔
3058
                        })
3,662✔
3059
                        .join(" OR ")
3,662✔
3060

3,662✔
3061
                    return `
3,662✔
3062
                    SELECT DISTINCT
3,662✔
3063
                        "TABLE_CATALOG", "TABLE_SCHEMA", "TABLE_NAME"
3,662✔
3064
                    FROM "${database}"."INFORMATION_SCHEMA"."TABLES"
3,662✔
3065
                    WHERE
3,662✔
3066
                          "TABLE_TYPE" = 'BASE TABLE' AND
3,662✔
3067
                          "TABLE_CATALOG" = '${database}' AND
3,662✔
3068
                          ${tablesCondition}
3,662✔
3069
                `
3,662✔
3070
                })
3,654✔
3071
                .join(" UNION ALL ")
3,654✔
3072

3,654✔
3073
            dbTables.push(...(await this.query(tablesSql)))
3,654✔
3074
        }
3,654✔
3075

3,654✔
3076
        // if tables were not found in the db, no need to proceed
3,654✔
3077
        if (dbTables.length === 0) {
3,668✔
3078
            return []
2,974✔
3079
        }
2,974✔
3080

680✔
3081
        const dbTablesByCatalog = dbTables.reduce(
680✔
3082
            (c, { TABLE_CATALOG, ...other }) => {
680✔
3083
                c[TABLE_CATALOG] = c[TABLE_CATALOG] || []
1,004✔
3084
                c[TABLE_CATALOG].push(other)
1,004✔
3085
                return c
1,004✔
3086
            },
680✔
3087
            {} as {
680✔
3088
                [key: string]: { TABLE_NAME: string; TABLE_SCHEMA: string }[]
680✔
3089
            },
680✔
3090
        )
680✔
3091

680✔
3092
        const columnsSql = Object.entries(dbTablesByCatalog)
680✔
3093
            .map(([TABLE_CATALOG, tables]) => {
680✔
3094
                const condition = tables
680✔
3095
                    .map(
680✔
3096
                        ({ TABLE_SCHEMA, TABLE_NAME }) =>
680✔
3097
                            `("TABLE_SCHEMA" = '${TABLE_SCHEMA}' AND "TABLE_NAME" = '${TABLE_NAME}')`,
680✔
3098
                    )
680✔
3099
                    .join("OR")
680✔
3100

680✔
3101
                return (
680✔
3102
                    `SELECT "COLUMNS".*, "cc"."is_persisted", "cc"."definition" ` +
680✔
3103
                    `FROM "${TABLE_CATALOG}"."INFORMATION_SCHEMA"."COLUMNS" ` +
680✔
3104
                    `LEFT JOIN "sys"."computed_columns" "cc" ON COL_NAME("cc"."object_id", "cc"."column_id") = "column_name" ` +
680✔
3105
                    `WHERE (${condition})`
680✔
3106
                )
680✔
3107
            })
680✔
3108
            .join(" UNION ALL ")
680✔
3109

680✔
3110
        const constraintsSql = Object.entries(dbTablesByCatalog)
680✔
3111
            .map(([TABLE_CATALOG, tables]) => {
680✔
3112
                const conditions = tables
680✔
3113
                    .map(
680✔
3114
                        ({ TABLE_NAME, TABLE_SCHEMA }) =>
680✔
3115
                            `("columnUsages"."TABLE_SCHEMA" = '${TABLE_SCHEMA}' AND "columnUsages"."TABLE_NAME" = '${TABLE_NAME}')`,
680✔
3116
                    )
680✔
3117
                    .join(" OR ")
680✔
3118

680✔
3119
                return (
680✔
3120
                    `SELECT "columnUsages".*, "tableConstraints"."CONSTRAINT_TYPE", "chk"."definition" ` +
680✔
3121
                    `FROM "${TABLE_CATALOG}"."INFORMATION_SCHEMA"."CONSTRAINT_COLUMN_USAGE" "columnUsages" ` +
680✔
3122
                    `INNER JOIN "${TABLE_CATALOG}"."INFORMATION_SCHEMA"."TABLE_CONSTRAINTS" "tableConstraints" ` +
680✔
3123
                    `ON ` +
680✔
3124
                    `"tableConstraints"."CONSTRAINT_NAME" = "columnUsages"."CONSTRAINT_NAME" AND ` +
680✔
3125
                    `"tableConstraints"."TABLE_SCHEMA" = "columnUsages"."TABLE_SCHEMA" AND ` +
680✔
3126
                    `"tableConstraints"."TABLE_NAME" = "columnUsages"."TABLE_NAME" ` +
680✔
3127
                    `LEFT JOIN "${TABLE_CATALOG}"."sys"."check_constraints" "chk" ` +
680✔
3128
                    `ON ` +
680✔
3129
                    `"chk"."object_id" = OBJECT_ID("columnUsages"."TABLE_CATALOG" + '.' + "columnUsages"."TABLE_SCHEMA" + '.' + "columnUsages"."CONSTRAINT_NAME") ` +
680✔
3130
                    `WHERE ` +
680✔
3131
                    `(${conditions}) AND ` +
680✔
3132
                    `"tableConstraints"."CONSTRAINT_TYPE" IN ('PRIMARY KEY', 'UNIQUE', 'CHECK')`
680✔
3133
                )
680✔
3134
            })
680✔
3135
            .join(" UNION ALL ")
680✔
3136

680✔
3137
        const foreignKeysSql = Object.entries(dbTablesByCatalog)
680✔
3138
            .map(([TABLE_CATALOG, tables]) => {
680✔
3139
                const conditions = tables
680✔
3140
                    .map(
680✔
3141
                        ({ TABLE_NAME, TABLE_SCHEMA }) =>
680✔
3142
                            `("s1"."name" = '${TABLE_SCHEMA}' AND "t1"."name" = '${TABLE_NAME}')`,
680✔
3143
                    )
680✔
3144
                    .join(" OR ")
680✔
3145

680✔
3146
                return (
680✔
3147
                    `SELECT "fk"."name" AS "FK_NAME", '${TABLE_CATALOG}' AS "TABLE_CATALOG", "s1"."name" AS "TABLE_SCHEMA", "t1"."name" AS "TABLE_NAME", ` +
680✔
3148
                    `"col1"."name" AS "COLUMN_NAME", "s2"."name" AS "REF_SCHEMA", "t2"."name" AS "REF_TABLE", "col2"."name" AS "REF_COLUMN", ` +
680✔
3149
                    `"fk"."delete_referential_action_desc" AS "ON_DELETE", "fk"."update_referential_action_desc" AS "ON_UPDATE" ` +
680✔
3150
                    `FROM "${TABLE_CATALOG}"."sys"."foreign_keys" "fk" ` +
680✔
3151
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."foreign_key_columns" "fkc" ON "fkc"."constraint_object_id" = "fk"."object_id" ` +
680✔
3152
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."tables" "t1" ON "t1"."object_id" = "fk"."parent_object_id" ` +
680✔
3153
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."schemas" "s1" ON "s1"."schema_id" = "t1"."schema_id" ` +
680✔
3154
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."tables" "t2" ON "t2"."object_id" = "fk"."referenced_object_id" ` +
680✔
3155
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."schemas" "s2" ON "s2"."schema_id" = "t2"."schema_id" ` +
680✔
3156
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."columns" "col1" ON "col1"."column_id" = "fkc"."parent_column_id" AND "col1"."object_id" = "fk"."parent_object_id" ` +
680✔
3157
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."columns" "col2" ON "col2"."column_id" = "fkc"."referenced_column_id" AND "col2"."object_id" = "fk"."referenced_object_id" ` +
680✔
3158
                    `WHERE (${conditions})`
680✔
3159
                )
680✔
3160
            })
680✔
3161
            .join(" UNION ALL ")
680✔
3162

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

680✔
3172
                return (
680✔
3173
                    `SELECT "TABLE_CATALOG", "TABLE_SCHEMA", "COLUMN_NAME", "TABLE_NAME" ` +
680✔
3174
                    `FROM "${TABLE_CATALOG}"."INFORMATION_SCHEMA"."COLUMNS" ` +
680✔
3175
                    `WHERE ` +
680✔
3176
                    `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✔
3177
                    `(${conditions})`
680✔
3178
                )
680✔
3179
            })
680✔
3180
            .join(" UNION ALL ")
680✔
3181

680✔
3182
        const dbCollationsSql = `SELECT "NAME", "COLLATION_NAME" FROM "sys"."databases"`
680✔
3183

680✔
3184
        const indicesSql = Object.entries(dbTablesByCatalog)
680✔
3185
            .map(([TABLE_CATALOG, tables]) => {
680✔
3186
                const conditions = tables
680✔
3187
                    .map(
680✔
3188
                        ({ TABLE_NAME, TABLE_SCHEMA }) =>
680✔
3189
                            `("s"."name" = '${TABLE_SCHEMA}' AND "t"."name" = '${TABLE_NAME}')`,
680✔
3190
                    )
680✔
3191
                    .join(" OR ")
680✔
3192

680✔
3193
                return (
680✔
3194
                    `SELECT '${TABLE_CATALOG}' AS "TABLE_CATALOG", "s"."name" AS "TABLE_SCHEMA", "t"."name" AS "TABLE_NAME", ` +
680✔
3195
                    `"ind"."name" AS "INDEX_NAME", "col"."name" AS "COLUMN_NAME", "ind"."is_unique" AS "IS_UNIQUE", "ind"."filter_definition" as "CONDITION" ` +
680✔
3196
                    `FROM "${TABLE_CATALOG}"."sys"."indexes" "ind" ` +
680✔
3197
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."index_columns" "ic" ON "ic"."object_id" = "ind"."object_id" AND "ic"."index_id" = "ind"."index_id" ` +
680✔
3198
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."columns" "col" ON "col"."object_id" = "ic"."object_id" AND "col"."column_id" = "ic"."column_id" ` +
680✔
3199
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."tables" "t" ON "t"."object_id" = "ind"."object_id" ` +
680✔
3200
                    `INNER JOIN "${TABLE_CATALOG}"."sys"."schemas" "s" ON "s"."schema_id" = "t"."schema_id" ` +
680✔
3201
                    `WHERE ` +
680✔
3202
                    `"ind"."is_primary_key" = 0 AND "ind"."is_unique_constraint" = 0 AND "t"."is_ms_shipped" = 0 AND ` +
680✔
3203
                    `(${conditions})`
680✔
3204
                )
680✔
3205
            })
680✔
3206
            .join(" UNION ALL ")
680✔
3207

680✔
3208
        const [
680✔
3209
            dbColumns,
680✔
3210
            dbConstraints,
680✔
3211
            dbForeignKeys,
680✔
3212
            dbIdentityColumns,
680✔
3213
            dbCollations,
680✔
3214
            dbIndices,
680✔
3215
        ]: ObjectLiteral[][] = await Promise.all([
680✔
3216
            this.query(columnsSql),
680✔
3217
            this.query(constraintsSql),
680✔
3218
            this.query(foreignKeysSql),
680✔
3219
            this.query(identityColumnsSql),
680✔
3220
            this.query(dbCollationsSql),
680✔
3221
            this.query(indicesSql),
680✔
3222
        ])
680✔
3223

680✔
3224
        // create table schemas for loaded tables
680✔
3225
        return await Promise.all(
680✔
3226
            dbTables.map(async (dbTable) => {
680✔
3227
                const table = new Table()
1,004✔
3228

1,004✔
3229
                const getSchemaFromKey = (dbObject: any, key: string) => {
1,004✔
3230
                    return dbObject[key] === currentSchema &&
1,342✔
3231
                        (!this.driver.options.schema ||
1,252✔
3232
                            this.driver.options.schema === currentSchema)
1,252✔
3233
                        ? undefined
1,342✔
3234
                        : dbObject[key]
1,342✔
3235
                }
1,342✔
3236

1,004✔
3237
                // We do not need to join schema and database names, when db or schema is by default.
1,004✔
3238
                const db =
1,004✔
3239
                    dbTable["TABLE_CATALOG"] === currentDatabase
1,004✔
3240
                        ? undefined
1,004✔
3241
                        : dbTable["TABLE_CATALOG"]
1,004✔
3242
                const schema = getSchemaFromKey(dbTable, "TABLE_SCHEMA")
1,004✔
3243
                table.database = dbTable["TABLE_CATALOG"]
1,004✔
3244
                table.schema = dbTable["TABLE_SCHEMA"]
1,004✔
3245
                table.name = this.driver.buildTableName(
1,004✔
3246
                    dbTable["TABLE_NAME"],
1,004✔
3247
                    schema,
1,004✔
3248
                    db,
1,004✔
3249
                )
1,004✔
3250

1,004✔
3251
                const defaultCollation = dbCollations.find(
1,004✔
3252
                    (dbCollation) =>
1,004✔
3253
                        dbCollation["NAME"] === dbTable["TABLE_CATALOG"],
1,004✔
3254
                )!
1,004✔
3255

1,004✔
3256
                // create columns from the loaded columns
1,004✔
3257
                table.columns = await Promise.all(
1,004✔
3258
                    dbColumns
1,004✔
3259
                        .filter(
1,004✔
3260
                            (dbColumn) =>
1,004✔
3261
                                dbColumn["TABLE_NAME"] ===
10,764✔
3262
                                    dbTable["TABLE_NAME"] &&
10,764✔
3263
                                dbColumn["TABLE_SCHEMA"] ===
3,610✔
3264
                                    dbTable["TABLE_SCHEMA"] &&
10,764✔
3265
                                dbColumn["TABLE_CATALOG"] ===
3,610✔
3266
                                    dbTable["TABLE_CATALOG"],
1,004✔
3267
                        )
1,004✔
3268
                        .map(async (dbColumn) => {
1,004✔
3269
                            const columnConstraints = dbConstraints.filter(
3,610✔
3270
                                (dbConstraint) =>
3,610✔
3271
                                    dbConstraint["TABLE_NAME"] ===
18,984✔
3272
                                        dbColumn["TABLE_NAME"] &&
18,984✔
3273
                                    dbConstraint["TABLE_SCHEMA"] ===
8,130✔
3274
                                        dbColumn["TABLE_SCHEMA"] &&
18,984✔
3275
                                    dbConstraint["TABLE_CATALOG"] ===
8,130✔
3276
                                        dbColumn["TABLE_CATALOG"] &&
18,984✔
3277
                                    dbConstraint["COLUMN_NAME"] ===
8,130✔
3278
                                        dbColumn["COLUMN_NAME"],
3,610✔
3279
                            )
3,610✔
3280

3,610✔
3281
                            const uniqueConstraints = columnConstraints.filter(
3,610✔
3282
                                (constraint) =>
3,610✔
3283
                                    constraint["CONSTRAINT_TYPE"] === "UNIQUE",
3,610✔
3284
                            )
3,610✔
3285
                            const isConstraintComposite =
3,610✔
3286
                                uniqueConstraints.every((uniqueConstraint) => {
3,610✔
3287
                                    return dbConstraints.some(
626✔
3288
                                        (dbConstraint) =>
626✔
3289
                                            dbConstraint["CONSTRAINT_TYPE"] ===
2,598✔
3290
                                                "UNIQUE" &&
2,598✔
3291
                                            dbConstraint["CONSTRAINT_NAME"] ===
1,290✔
3292
                                                uniqueConstraint[
1,290✔
3293
                                                    "CONSTRAINT_NAME"
1,290✔
3294
                                                ] &&
2,598✔
3295
                                            dbConstraint["TABLE_SCHEMA"] ===
810✔
3296
                                                dbColumn["TABLE_SCHEMA"] &&
2,598✔
3297
                                            dbConstraint["TABLE_CATALOG"] ===
810✔
3298
                                                dbColumn["TABLE_CATALOG"] &&
2,598✔
3299
                                            dbConstraint["COLUMN_NAME"] !==
810✔
3300
                                                dbColumn["COLUMN_NAME"],
626✔
3301
                                    )
626✔
3302
                                })
3,610✔
3303

3,610✔
3304
                            const isGenerated = !!dbIdentityColumns.find(
3,610✔
3305
                                (column) =>
3,610✔
3306
                                    column["TABLE_NAME"] ===
5,700✔
3307
                                        dbColumn["TABLE_NAME"] &&
5,700✔
3308
                                    column["TABLE_SCHEMA"] ===
1,774✔
3309
                                        dbColumn["TABLE_SCHEMA"] &&
5,700✔
3310
                                    column["TABLE_CATALOG"] ===
1,774✔
3311
                                        dbColumn["TABLE_CATALOG"] &&
5,700✔
3312
                                    column["COLUMN_NAME"] ===
1,774✔
3313
                                        dbColumn["COLUMN_NAME"],
3,610✔
3314
                            )
3,610✔
3315

3,610✔
3316
                            const tableColumn = new TableColumn()
3,610✔
3317
                            tableColumn.name = dbColumn["COLUMN_NAME"]
3,610✔
3318
                            tableColumn.type =
3,610✔
3319
                                dbColumn["DATA_TYPE"].toLowerCase()
3,610✔
3320

3,610✔
3321
                            // check only columns that have length property
3,610✔
3322
                            if (
3,610✔
3323
                                this.driver.withLengthColumnTypes.indexOf(
3,610✔
3324
                                    tableColumn.type as ColumnType,
3,610✔
3325
                                ) !== -1 &&
3,610✔
3326
                                dbColumn["CHARACTER_MAXIMUM_LENGTH"]
1,966✔
3327
                            ) {
3,610✔
3328
                                const length =
1,966✔
3329
                                    dbColumn[
1,966✔
3330
                                        "CHARACTER_MAXIMUM_LENGTH"
1,966✔
3331
                                    ].toString()
1,966✔
3332
                                if (length === "-1") {
1,966✔
3333
                                    tableColumn.length = "MAX"
8✔
3334
                                } else {
1,966✔
3335
                                    if (tableColumn.type === "vector") {
1,958✔
3336
                                        const len = +length
2✔
3337
                                        // NOTE: real returned length is (N*4 + 8) where N is desired dimensions
2✔
3338
                                        if (!Number.isNaN(len)) {
2✔
3339
                                            tableColumn.length = String(
2✔
3340
                                                (len - 8) / 4,
2✔
3341
                                            )
2✔
3342
                                        }
2✔
3343
                                    } else {
1,958✔
3344
                                        tableColumn.length =
1,956✔
3345
                                            !this.isDefaultColumnLength(
1,956✔
3346
                                                table,
1,956✔
3347
                                                tableColumn,
1,956✔
3348
                                                length,
1,956✔
3349
                                            )
1,956✔
3350
                                                ? length
1,956✔
3351
                                                : ""
1,956✔
3352
                                    }
1,956✔
3353
                                }
1,958✔
3354
                            }
1,966✔
3355

3,610✔
3356
                            if (
3,610✔
3357
                                tableColumn.type === "decimal" ||
3,610✔
3358
                                tableColumn.type === "numeric"
3,600✔
3359
                            ) {
3,610✔
3360
                                if (
14✔
3361
                                    dbColumn["NUMERIC_PRECISION"] !== null &&
14✔
3362
                                    !this.isDefaultColumnPrecision(
14✔
3363
                                        table,
14✔
3364
                                        tableColumn,
14✔
3365
                                        dbColumn["NUMERIC_PRECISION"],
14✔
3366
                                    )
14✔
3367
                                )
14✔
3368
                                    tableColumn.precision =
14✔
3369
                                        dbColumn["NUMERIC_PRECISION"]
8✔
3370
                                if (
14✔
3371
                                    dbColumn["NUMERIC_SCALE"] !== null &&
14✔
3372
                                    !this.isDefaultColumnScale(
14✔
3373
                                        table,
14✔
3374
                                        tableColumn,
14✔
3375
                                        dbColumn["NUMERIC_SCALE"],
14✔
3376
                                    )
14✔
3377
                                )
14✔
3378
                                    tableColumn.scale =
14✔
3379
                                        dbColumn["NUMERIC_SCALE"]
8✔
3380
                            }
14✔
3381

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

3,610✔
3427
                            const primaryConstraint = columnConstraints.find(
3,610✔
3428
                                (constraint) =>
3,610✔
3429
                                    constraint["CONSTRAINT_TYPE"] ===
1,856✔
3430
                                    "PRIMARY KEY",
3,610✔
3431
                            )
3,610✔
3432
                            if (primaryConstraint) {
3,610✔
3433
                                tableColumn.isPrimary = true
1,040✔
3434
                                // find another columns involved in primary key constraint
1,040✔
3435
                                const anotherPrimaryConstraints =
1,040✔
3436
                                    dbConstraints.filter(
1,040✔
3437
                                        (constraint) =>
1,040✔
3438
                                            constraint["TABLE_NAME"] ===
5,484✔
3439
                                                dbColumn["TABLE_NAME"] &&
5,484✔
3440
                                            constraint["TABLE_SCHEMA"] ===
1,982✔
3441
                                                dbColumn["TABLE_SCHEMA"] &&
5,484✔
3442
                                            constraint["TABLE_CATALOG"] ===
1,982✔
3443
                                                dbColumn["TABLE_CATALOG"] &&
5,484✔
3444
                                            constraint["COLUMN_NAME"] !==
1,982✔
3445
                                                dbColumn["COLUMN_NAME"] &&
5,484✔
3446
                                            constraint["CONSTRAINT_TYPE"] ===
922✔
3447
                                                "PRIMARY KEY",
1,040✔
3448
                                    )
1,040✔
3449

1,040✔
3450
                                // collect all column names
1,040✔
3451
                                const columnNames =
1,040✔
3452
                                    anotherPrimaryConstraints.map(
1,040✔
3453
                                        (constraint) =>
1,040✔
3454
                                            constraint["COLUMN_NAME"],
1,040✔
3455
                                    )
1,040✔
3456
                                columnNames.push(dbColumn["COLUMN_NAME"])
1,040✔
3457

1,040✔
3458
                                // build default primary key constraint name
1,040✔
3459
                                const pkName =
1,040✔
3460
                                    this.connection.namingStrategy.primaryKeyName(
1,040✔
3461
                                        table,
1,040✔
3462
                                        columnNames,
1,040✔
3463
                                    )
1,040✔
3464

1,040✔
3465
                                // if primary key has user-defined constraint name, write it in table column
1,040✔
3466
                                if (
1,040✔
3467
                                    primaryConstraint["CONSTRAINT_NAME"] !==
1,040✔
3468
                                    pkName
1,040✔
3469
                                ) {
1,040✔
3470
                                    tableColumn.primaryKeyConstraintName =
52✔
3471
                                        primaryConstraint["CONSTRAINT_NAME"]
52✔
3472
                                }
52✔
3473
                            }
1,040✔
3474

3,610✔
3475
                            tableColumn.default =
3,610✔
3476
                                dbColumn["COLUMN_DEFAULT"] !== null &&
3,610✔
3477
                                dbColumn["COLUMN_DEFAULT"] !== undefined
224✔
3478
                                    ? this.removeParenthesisFromDefault(
3,610✔
3479
                                          dbColumn["COLUMN_DEFAULT"],
224✔
3480
                                      )
3,610✔
3481
                                    : undefined
3,610✔
3482
                            tableColumn.isNullable =
3,610✔
3483
                                dbColumn["IS_NULLABLE"] === "YES"
3,610✔
3484
                            tableColumn.isUnique =
3,610✔
3485
                                uniqueConstraints.length > 0 &&
3,610✔
3486
                                !isConstraintComposite
626✔
3487
                            tableColumn.isGenerated = isGenerated
3,610✔
3488
                            if (isGenerated)
3,610✔
3489
                                tableColumn.generationStrategy = "increment"
3,610✔
3490
                            if (tableColumn.default === "newsequentialid()") {
3,610✔
3491
                                tableColumn.isGenerated = true
14✔
3492
                                tableColumn.generationStrategy = "uuid"
14✔
3493
                                tableColumn.default = undefined
14✔
3494
                            }
14✔
3495

3,610✔
3496
                            // todo: unable to get default charset
3,610✔
3497
                            // tableColumn.charset = dbColumn["CHARACTER_SET_NAME"];
3,610✔
3498
                            if (dbColumn["COLLATION_NAME"])
3,610✔
3499
                                tableColumn.collation =
3,610✔
3500
                                    dbColumn["COLLATION_NAME"] ===
1,918✔
3501
                                    defaultCollation["COLLATION_NAME"]
1,918✔
3502
                                        ? undefined
1,918✔
3503
                                        : dbColumn["COLLATION_NAME"]
1,918✔
3504

3,610✔
3505
                            if (
3,610✔
3506
                                tableColumn.type === "datetime2" ||
3,610✔
3507
                                tableColumn.type === "time" ||
3,610✔
3508
                                tableColumn.type === "datetimeoffset"
3,582✔
3509
                            ) {
3,610✔
3510
                                tableColumn.precision =
32✔
3511
                                    !this.isDefaultColumnPrecision(
32✔
3512
                                        table,
32✔
3513
                                        tableColumn,
32✔
3514
                                        dbColumn["DATETIME_PRECISION"],
32✔
3515
                                    )
32✔
3516
                                        ? dbColumn["DATETIME_PRECISION"]
32✔
3517
                                        : undefined
32✔
3518
                            }
32✔
3519

3,610✔
3520
                            if (
3,610✔
3521
                                dbColumn["is_persisted"] !== null &&
3,610✔
3522
                                dbColumn["is_persisted"] !== undefined &&
3,610✔
3523
                                dbColumn["definition"]
94✔
3524
                            ) {
3,610✔
3525
                                tableColumn.generatedType =
94✔
3526
                                    dbColumn["is_persisted"] === true
94✔
3527
                                        ? "STORED"
94✔
3528
                                        : "VIRTUAL"
94✔
3529
                                // We cannot relay on information_schema.columns.generation_expression, because it is formatted different.
94✔
3530
                                const asExpressionQuery =
94✔
3531
                                    this.selectTypeormMetadataSql({
94✔
3532
                                        database: dbTable["TABLE_CATALOG"],
94✔
3533
                                        schema: dbTable["TABLE_SCHEMA"],
94✔
3534
                                        table: dbTable["TABLE_NAME"],
94✔
3535
                                        type: MetadataTableType.GENERATED_COLUMN,
94✔
3536
                                        name: tableColumn.name,
94✔
3537
                                    })
94✔
3538

94✔
3539
                                const results = await this.query(
94✔
3540
                                    asExpressionQuery.query,
94✔
3541
                                    asExpressionQuery.parameters,
94✔
3542
                                )
94✔
3543
                                if (results[0] && results[0].value) {
94✔
3544
                                    tableColumn.asExpression = results[0].value
94✔
3545
                                } else {
94!
3546
                                    tableColumn.asExpression = ""
×
3547
                                }
×
3548
                            }
94✔
3549

3,610✔
3550
                            return tableColumn
3,610✔
3551
                        }),
1,004✔
3552
                )
1,004✔
3553

1,004✔
3554
                // find unique constraints of table, group them by constraint name and build TableUnique.
1,004✔
3555
                const tableUniqueConstraints = OrmUtils.uniq(
1,004✔
3556
                    dbConstraints.filter(
1,004✔
3557
                        (dbConstraint) =>
1,004✔
3558
                            dbConstraint["TABLE_NAME"] ===
5,374✔
3559
                                dbTable["TABLE_NAME"] &&
5,374✔
3560
                            dbConstraint["TABLE_SCHEMA"] ===
1,876✔
3561
                                dbTable["TABLE_SCHEMA"] &&
5,374✔
3562
                            dbConstraint["TABLE_CATALOG"] ===
1,876✔
3563
                                dbTable["TABLE_CATALOG"] &&
5,374✔
3564
                            dbConstraint["CONSTRAINT_TYPE"] === "UNIQUE",
1,004✔
3565
                    ),
1,004✔
3566
                    (dbConstraint) => dbConstraint["CONSTRAINT_NAME"],
1,004✔
3567
                )
1,004✔
3568

1,004✔
3569
                table.uniques = tableUniqueConstraints.map((constraint) => {
1,004✔
3570
                    const uniques = dbConstraints.filter(
438✔
3571
                        (dbC) =>
438✔
3572
                            dbC["CONSTRAINT_NAME"] ===
2,714✔
3573
                            constraint["CONSTRAINT_NAME"],
438✔
3574
                    )
438✔
3575
                    return new TableUnique({
438✔
3576
                        name: constraint["CONSTRAINT_NAME"],
438✔
3577
                        columnNames: uniques.map((u) => u["COLUMN_NAME"]),
438✔
3578
                    })
438✔
3579
                })
1,004✔
3580

1,004✔
3581
                // find check constraints of table, group them by constraint name and build TableCheck.
1,004✔
3582
                const tableCheckConstraints = OrmUtils.uniq(
1,004✔
3583
                    dbConstraints.filter(
1,004✔
3584
                        (dbConstraint) =>
1,004✔
3585
                            dbConstraint["TABLE_NAME"] ===
5,374✔
3586
                                dbTable["TABLE_NAME"] &&
5,374✔
3587
                            dbConstraint["TABLE_SCHEMA"] ===
1,876✔
3588
                                dbTable["TABLE_SCHEMA"] &&
5,374✔
3589
                            dbConstraint["TABLE_CATALOG"] ===
1,876✔
3590
                                dbTable["TABLE_CATALOG"] &&
5,374✔
3591
                            dbConstraint["CONSTRAINT_TYPE"] === "CHECK",
1,004✔
3592
                    ),
1,004✔
3593
                    (dbConstraint) => dbConstraint["CONSTRAINT_NAME"],
1,004✔
3594
                )
1,004✔
3595

1,004✔
3596
                table.checks = tableCheckConstraints
1,004✔
3597
                    .filter(
1,004✔
3598
                        (constraint) =>
1,004✔
3599
                            !this.isEnumCheckConstraint(
196✔
3600
                                constraint["CONSTRAINT_NAME"],
196✔
3601
                            ),
1,004✔
3602
                    )
1,004✔
3603
                    .map((constraint) => {
1,004✔
3604
                        const checks = dbConstraints.filter(
186✔
3605
                            (dbC) =>
186✔
3606
                                dbC["CONSTRAINT_NAME"] ===
1,278✔
3607
                                constraint["CONSTRAINT_NAME"],
186✔
3608
                        )
186✔
3609
                        return new TableCheck({
186✔
3610
                            name: constraint["CONSTRAINT_NAME"],
186✔
3611
                            columnNames: checks.map((c) => c["COLUMN_NAME"]),
186✔
3612
                            expression: constraint["definition"],
186✔
3613
                        })
186✔
3614
                    })
1,004✔
3615

1,004✔
3616
                // find foreign key constraints of table, group them by constraint name and build TableForeignKey.
1,004✔
3617
                const tableForeignKeyConstraints = OrmUtils.uniq(
1,004✔
3618
                    dbForeignKeys.filter(
1,004✔
3619
                        (dbForeignKey) =>
1,004✔
3620
                            dbForeignKey["TABLE_NAME"] ===
1,280✔
3621
                                dbTable["TABLE_NAME"] &&
1,280✔
3622
                            dbForeignKey["TABLE_SCHEMA"] ===
346✔
3623
                                dbTable["TABLE_SCHEMA"] &&
1,280✔
3624
                            dbForeignKey["TABLE_CATALOG"] ===
346✔
3625
                                dbTable["TABLE_CATALOG"],
1,004✔
3626
                    ),
1,004✔
3627
                    (dbForeignKey) => dbForeignKey["FK_NAME"],
1,004✔
3628
                )
1,004✔
3629

1,004✔
3630
                table.foreignKeys = tableForeignKeyConstraints.map(
1,004✔
3631
                    (dbForeignKey) => {
1,004✔
3632
                        const foreignKeys = dbForeignKeys.filter(
338✔
3633
                            (dbFk) =>
338✔
3634
                                dbFk["FK_NAME"] === dbForeignKey["FK_NAME"],
338✔
3635
                        )
338✔
3636

338✔
3637
                        // if referenced table located in currently used db and schema, we don't need to concat db and schema names to table name.
338✔
3638
                        const db =
338✔
3639
                            dbForeignKey["TABLE_CATALOG"] === currentDatabase
338✔
3640
                                ? undefined
338✔
3641
                                : dbForeignKey["TABLE_CATALOG"]
338✔
3642
                        const schema = getSchemaFromKey(
338✔
3643
                            dbForeignKey,
338✔
3644
                            "REF_SCHEMA",
338✔
3645
                        )
338✔
3646
                        const referencedTableName = this.driver.buildTableName(
338✔
3647
                            dbForeignKey["REF_TABLE"],
338✔
3648
                            schema,
338✔
3649
                            db,
338✔
3650
                        )
338✔
3651

338✔
3652
                        return new TableForeignKey({
338✔
3653
                            name: dbForeignKey["FK_NAME"],
338✔
3654
                            columnNames: foreignKeys.map(
338✔
3655
                                (dbFk) => dbFk["COLUMN_NAME"],
338✔
3656
                            ),
338✔
3657
                            referencedDatabase: dbForeignKey["TABLE_CATALOG"],
338✔
3658
                            referencedSchema: dbForeignKey["REF_SCHEMA"],
338✔
3659
                            referencedTableName: referencedTableName,
338✔
3660
                            referencedColumnNames: foreignKeys.map(
338✔
3661
                                (dbFk) => dbFk["REF_COLUMN"],
338✔
3662
                            ),
338✔
3663
                            onDelete: dbForeignKey["ON_DELETE"].replace(
338✔
3664
                                "_",
338✔
3665
                                " ",
338✔
3666
                            ), // SqlServer returns NO_ACTION, instead of NO ACTION
338✔
3667
                            onUpdate: dbForeignKey["ON_UPDATE"].replace(
338✔
3668
                                "_",
338✔
3669
                                " ",
338✔
3670
                            ), // SqlServer returns NO_ACTION, instead of NO ACTION
338✔
3671
                        })
338✔
3672
                    },
1,004✔
3673
                )
1,004✔
3674

1,004✔
3675
                // find index constraints of table, group them by constraint name and build TableIndex.
1,004✔
3676
                const tableIndexConstraints = OrmUtils.uniq(
1,004✔
3677
                    dbIndices.filter(
1,004✔
3678
                        (dbIndex) =>
1,004✔
3679
                            dbIndex["TABLE_NAME"] === dbTable["TABLE_NAME"] &&
722✔
3680
                            dbIndex["TABLE_SCHEMA"] ===
332✔
3681
                                dbTable["TABLE_SCHEMA"] &&
722✔
3682
                            dbIndex["TABLE_CATALOG"] ===
332✔
3683
                                dbTable["TABLE_CATALOG"],
1,004✔
3684
                    ),
1,004✔
3685
                    (dbIndex) => dbIndex["INDEX_NAME"],
1,004✔
3686
                )
1,004✔
3687

1,004✔
3688
                table.indices = tableIndexConstraints.map((constraint) => {
1,004✔
3689
                    const indices = dbIndices.filter((index) => {
288✔
3690
                        return (
964✔
3691
                            index["TABLE_CATALOG"] ===
964✔
3692
                                constraint["TABLE_CATALOG"] &&
964✔
3693
                            index["TABLE_SCHEMA"] ===
964✔
3694
                                constraint["TABLE_SCHEMA"] &&
964✔
3695
                            index["TABLE_NAME"] === constraint["TABLE_NAME"] &&
964✔
3696
                            index["INDEX_NAME"] === constraint["INDEX_NAME"]
694✔
3697
                        )
964✔
3698
                    })
288✔
3699
                    return new TableIndex(<TableIndexOptions>{
288✔
3700
                        table: table,
288✔
3701
                        name: constraint["INDEX_NAME"],
288✔
3702
                        columnNames: indices.map((i) => i["COLUMN_NAME"]),
288✔
3703
                        isUnique: constraint["IS_UNIQUE"],
288✔
3704
                        where: constraint["CONDITION"],
288✔
3705
                    })
288✔
3706
                })
1,004✔
3707

1,004✔
3708
                return table
1,004✔
3709
            }),
680✔
3710
        )
680✔
3711
    }
680✔
3712

28✔
3713
    /**
28✔
3714
     * Builds and returns SQL for create table.
28✔
3715
     * @param table
28✔
3716
     * @param createForeignKeys
28✔
3717
     */
28✔
3718
    protected createTableSql(table: Table, createForeignKeys?: boolean): Query {
28✔
3719
        const columnDefinitions = table.columns
10,380✔
3720
            .map((column) =>
10,380✔
3721
                this.buildCreateColumnSql(table, column, false, true),
10,380✔
3722
            )
10,380✔
3723
            .join(", ")
10,380✔
3724
        let sql = `CREATE TABLE ${this.escapePath(table)} (${columnDefinitions}`
10,380✔
3725

10,380✔
3726
        table.columns
10,380✔
3727
            .filter((column) => column.isUnique)
10,380✔
3728
            .forEach((column) => {
10,380✔
3729
                const isUniqueExist = table.uniques.some(
728✔
3730
                    (unique) =>
728✔
3731
                        unique.columnNames.length === 1 &&
882✔
3732
                        unique.columnNames[0] === column.name,
728✔
3733
                )
728✔
3734
                if (!isUniqueExist)
728✔
3735
                    table.uniques.push(
728✔
3736
                        new TableUnique({
8✔
3737
                            name: this.connection.namingStrategy.uniqueConstraintName(
8✔
3738
                                table,
8✔
3739
                                [column.name],
8✔
3740
                            ),
8✔
3741
                            columnNames: [column.name],
8✔
3742
                        }),
8✔
3743
                    )
8✔
3744
            })
10,380✔
3745

10,380✔
3746
        if (table.uniques.length > 0) {
10,380✔
3747
            const uniquesSql = table.uniques
662✔
3748
                .map((unique) => {
662✔
3749
                    const uniqueName = unique.name
972✔
3750
                        ? unique.name
972✔
3751
                        : this.connection.namingStrategy.uniqueConstraintName(
972✔
3752
                              table,
4✔
3753
                              unique.columnNames,
4✔
3754
                          )
972✔
3755
                    const columnNames = unique.columnNames
972✔
3756
                        .map((columnName) => `"${columnName}"`)
972✔
3757
                        .join(", ")
972✔
3758
                    return `CONSTRAINT "${uniqueName}" UNIQUE (${columnNames})`
972✔
3759
                })
662✔
3760
                .join(", ")
662✔
3761

662✔
3762
            sql += `, ${uniquesSql}`
662✔
3763
        }
662✔
3764

10,380✔
3765
        if (table.checks.length > 0) {
10,380✔
3766
            const checksSql = table.checks
164✔
3767
                .map((check) => {
164✔
3768
                    const checkName = check.name
166✔
3769
                        ? check.name
166✔
3770
                        : this.connection.namingStrategy.checkConstraintName(
166✔
3771
                              table,
2✔
3772
                              check.expression!,
2✔
3773
                          )
166✔
3774
                    return `CONSTRAINT "${checkName}" CHECK (${check.expression})`
166✔
3775
                })
164✔
3776
                .join(", ")
164✔
3777

164✔
3778
            sql += `, ${checksSql}`
164✔
3779
        }
164✔
3780

10,380✔
3781
        if (table.foreignKeys.length > 0 && createForeignKeys) {
10,380✔
3782
            const foreignKeysSql = table.foreignKeys
8✔
3783
                .map((fk) => {
8✔
3784
                    const columnNames = fk.columnNames
10✔
3785
                        .map((columnName) => `"${columnName}"`)
10✔
3786
                        .join(", ")
10✔
3787
                    if (!fk.name)
10✔
3788
                        fk.name = this.connection.namingStrategy.foreignKeyName(
10✔
3789
                            table,
6✔
3790
                            fk.columnNames,
6✔
3791
                            this.getTablePath(fk),
6✔
3792
                            fk.referencedColumnNames,
6✔
3793
                        )
6✔
3794
                    const referencedColumnNames = fk.referencedColumnNames
10✔
3795
                        .map((columnName) => `"${columnName}"`)
10✔
3796
                        .join(", ")
10✔
3797

10✔
3798
                    let constraint = `CONSTRAINT "${
10✔
3799
                        fk.name
10✔
3800
                    }" FOREIGN KEY (${columnNames}) REFERENCES ${this.escapePath(
10✔
3801
                        this.getTablePath(fk),
10✔
3802
                    )} (${referencedColumnNames})`
10✔
3803
                    if (fk.onDelete) constraint += ` ON DELETE ${fk.onDelete}`
10✔
3804
                    if (fk.onUpdate) constraint += ` ON UPDATE ${fk.onUpdate}`
10✔
3805

10✔
3806
                    return constraint
10✔
3807
                })
8✔
3808
                .join(", ")
8✔
3809

8✔
3810
            sql += `, ${foreignKeysSql}`
8✔
3811
        }
8✔
3812

10,380✔
3813
        const primaryColumns = table.columns.filter(
10,380✔
3814
            (column) => column.isPrimary,
10,380✔
3815
        )
10,380✔
3816
        if (primaryColumns.length > 0) {
10,380✔
3817
            const primaryKeyName = primaryColumns[0].primaryKeyConstraintName
10,354✔
3818
                ? primaryColumns[0].primaryKeyConstraintName
10,354✔
3819
                : this.connection.namingStrategy.primaryKeyName(
10,354✔
3820
                      table,
10,322✔
3821
                      primaryColumns.map((column) => column.name),
10,322✔
3822
                  )
10,354✔
3823

10,354✔
3824
            const columnNames = primaryColumns
10,354✔
3825
                .map((column) => `"${column.name}"`)
10,354✔
3826
                .join(", ")
10,354✔
3827
            sql += `, CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNames})`
10,354✔
3828
        }
10,354✔
3829

10,380✔
3830
        sql += `)`
10,380✔
3831

10,380✔
3832
        return new Query(sql)
10,380✔
3833
    }
10,380✔
3834

28✔
3835
    /**
28✔
3836
     * Builds drop table sql.
28✔
3837
     * @param tableOrName
28✔
3838
     * @param ifExists
28✔
3839
     */
28✔
3840
    protected dropTableSql(
28✔
3841
        tableOrName: Table | string,
10,380✔
3842
        ifExists?: boolean,
10,380✔
3843
    ): Query {
10,380✔
3844
        const query = ifExists
10,380✔
3845
            ? `DROP TABLE IF EXISTS ${this.escapePath(tableOrName)}`
10,380!
3846
            : `DROP TABLE ${this.escapePath(tableOrName)}`
10,380✔
3847
        return new Query(query)
10,380✔
3848
    }
10,380✔
3849

28✔
3850
    protected createViewSql(view: View): Query {
28✔
3851
        const parsedName = this.driver.parseTableName(view)
16✔
3852

16✔
3853
        // Can't use `escapePath` here because `CREATE VIEW` does not accept database names.
16✔
3854
        const viewIdentifier = parsedName.schema
16✔
3855
            ? `"${parsedName.schema}"."${parsedName.tableName}"`
16✔
3856
            : `"${parsedName.tableName}"`
16!
3857

16✔
3858
        if (typeof view.expression === "string") {
16✔
3859
            return new Query(
4✔
3860
                `CREATE VIEW ${viewIdentifier} AS ${view.expression}`,
4✔
3861
            )
4✔
3862
        } else {
16✔
3863
            return new Query(
12✔
3864
                `CREATE VIEW ${viewIdentifier} AS ${view
12✔
3865
                    .expression(this.connection)
12✔
3866
                    .getQuery()}`,
12✔
3867
            )
12✔
3868
        }
12✔
3869
    }
16✔
3870

28✔
3871
    protected async insertViewDefinitionSql(view: View): Promise<Query> {
28✔
3872
        const parsedTableName = this.driver.parseTableName(view)
16✔
3873

16✔
3874
        if (!parsedTableName.schema) {
16!
3875
            parsedTableName.schema = await this.getCurrentSchema()
×
3876
        }
×
3877

16✔
3878
        const expression =
16✔
3879
            typeof view.expression === "string"
16✔
3880
                ? view.expression.trim()
16✔
3881
                : view.expression(this.connection).getQuery()
16✔
3882
        return this.insertTypeormMetadataSql({
16✔
3883
            type: MetadataTableType.VIEW,
16✔
3884
            database: parsedTableName.database,
16✔
3885
            schema: parsedTableName.schema,
16✔
3886
            name: parsedTableName.tableName,
16✔
3887
            value: expression,
16✔
3888
        })
16✔
3889
    }
16✔
3890

28✔
3891
    /**
28✔
3892
     * Builds drop view sql.
28✔
3893
     * @param viewOrPath
28✔
3894
     * @param ifExists
28✔
3895
     */
28✔
3896
    protected dropViewSql(
28✔
3897
        viewOrPath: View | string,
16✔
3898
        ifExists?: boolean,
16✔
3899
    ): Query {
16✔
3900
        const query = ifExists
16✔
3901
            ? `DROP VIEW IF EXISTS ${this.escapePath(viewOrPath)}`
16!
3902
            : `DROP VIEW ${this.escapePath(viewOrPath)}`
16✔
3903
        return new Query(query)
16✔
3904
    }
16✔
3905

28✔
3906
    /**
28✔
3907
     * Builds remove view sql.
28✔
3908
     * @param viewOrPath
28✔
3909
     */
28✔
3910
    protected async deleteViewDefinitionSql(
28✔
3911
        viewOrPath: View | string,
16✔
3912
    ): Promise<Query> {
16✔
3913
        const parsedTableName = this.driver.parseTableName(viewOrPath)
16✔
3914

16✔
3915
        if (!parsedTableName.schema) {
16!
3916
            parsedTableName.schema = await this.getCurrentSchema()
×
3917
        }
×
3918

16✔
3919
        return this.deleteTypeormMetadataSql({
16✔
3920
            type: MetadataTableType.VIEW,
16✔
3921
            database: parsedTableName.database,
16✔
3922
            schema: parsedTableName.schema,
16✔
3923
            name: parsedTableName.tableName,
16✔
3924
        })
16✔
3925
    }
16✔
3926

28✔
3927
    /**
28✔
3928
     * Builds create index sql.
28✔
3929
     * @param table
28✔
3930
     * @param index
28✔
3931
     */
28✔
3932
    protected createIndexSql(table: Table, index: TableIndex): Query {
28✔
3933
        const columns = index.columnNames
4,376✔
3934
            .map((columnName) => `"${columnName}"`)
4,376✔
3935
            .join(", ")
4,376✔
3936
        return new Query(
4,376✔
3937
            `CREATE ${index.isUnique ? "UNIQUE " : ""}INDEX "${
4,376✔
3938
                index.name
4,376✔
3939
            }" ON ${this.escapePath(table)} (${columns}) ${
4,376✔
3940
                index.where ? "WHERE " + index.where : ""
4,376✔
3941
            }`,
4,376✔
3942
        )
4,376✔
3943
    }
4,376✔
3944

28✔
3945
    /**
28✔
3946
     * Builds drop index sql.
28✔
3947
     * @param table
28✔
3948
     * @param indexOrName
28✔
3949
     */
28✔
3950
    protected dropIndexSql(
28✔
3951
        table: Table,
4,376✔
3952
        indexOrName: TableIndex | string,
4,376✔
3953
    ): Query {
4,376✔
3954
        const indexName = InstanceChecker.isTableIndex(indexOrName)
4,376✔
3955
            ? indexOrName.name
4,376✔
3956
            : indexOrName
4,376!
3957
        return new Query(
4,376✔
3958
            `DROP INDEX "${indexName}" ON ${this.escapePath(table)}`,
4,376✔
3959
        )
4,376✔
3960
    }
4,376✔
3961

28✔
3962
    /**
28✔
3963
     * Builds create primary key sql.
28✔
3964
     * @param table
28✔
3965
     * @param columnNames
28✔
3966
     * @param constraintName
28✔
3967
     */
28✔
3968
    protected createPrimaryKeySql(
28✔
3969
        table: Table,
12✔
3970
        columnNames: string[],
12✔
3971
        constraintName?: string,
12✔
3972
    ): Query {
12✔
3973
        const primaryKeyName = constraintName
12✔
3974
            ? constraintName
12✔
3975
            : this.connection.namingStrategy.primaryKeyName(table, columnNames)
12✔
3976

12✔
3977
        const columnNamesString = columnNames
12✔
3978
            .map((columnName) => `"${columnName}"`)
12✔
3979
            .join(", ")
12✔
3980
        return new Query(
12✔
3981
            `ALTER TABLE ${this.escapePath(
12✔
3982
                table,
12✔
3983
            )} ADD CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNamesString})`,
12✔
3984
        )
12✔
3985
    }
12✔
3986

28✔
3987
    /**
28✔
3988
     * Builds drop primary key sql.
28✔
3989
     * @param table
28✔
3990
     */
28✔
3991
    protected dropPrimaryKeySql(table: Table): Query {
28✔
3992
        const columnNames = table.primaryColumns.map((column) => column.name)
12✔
3993
        const constraintName = table.primaryColumns[0].primaryKeyConstraintName
12✔
3994
        const primaryKeyName = constraintName
12✔
3995
            ? constraintName
12!
3996
            : this.connection.namingStrategy.primaryKeyName(table, columnNames)
12✔
3997

12✔
3998
        return new Query(
12✔
3999
            `ALTER TABLE ${this.escapePath(
12✔
4000
                table,
12✔
4001
            )} DROP CONSTRAINT "${primaryKeyName}"`,
12✔
4002
        )
12✔
4003
    }
12✔
4004

28✔
4005
    /**
28✔
4006
     * Builds create unique constraint sql.
28✔
4007
     * @param table
28✔
4008
     * @param uniqueConstraint
28✔
4009
     */
28✔
4010
    protected createUniqueConstraintSql(
28✔
4011
        table: Table,
38✔
4012
        uniqueConstraint: TableUnique,
38✔
4013
    ): Query {
38✔
4014
        const columnNames = uniqueConstraint.columnNames
38✔
4015
            .map((column) => `"` + column + `"`)
38✔
4016
            .join(", ")
38✔
4017
        return new Query(
38✔
4018
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
38✔
4019
                uniqueConstraint.name
38✔
4020
            }" UNIQUE (${columnNames})`,
38✔
4021
        )
38✔
4022
    }
38✔
4023

28✔
4024
    /**
28✔
4025
     * Builds drop unique constraint sql.
28✔
4026
     * @param table
28✔
4027
     * @param uniqueOrName
28✔
4028
     */
28✔
4029
    protected dropUniqueConstraintSql(
28✔
4030
        table: Table,
38✔
4031
        uniqueOrName: TableUnique | string,
38✔
4032
    ): Query {
38✔
4033
        const uniqueName = InstanceChecker.isTableUnique(uniqueOrName)
38✔
4034
            ? uniqueOrName.name
38✔
4035
            : uniqueOrName
38!
4036
        return new Query(
38✔
4037
            `ALTER TABLE ${this.escapePath(
38✔
4038
                table,
38✔
4039
            )} DROP CONSTRAINT "${uniqueName}"`,
38✔
4040
        )
38✔
4041
    }
38✔
4042

28✔
4043
    /**
28✔
4044
     * Builds create check constraint sql.
28✔
4045
     * @param table
28✔
4046
     * @param checkConstraint
28✔
4047
     */
28✔
4048
    protected createCheckConstraintSql(
28✔
4049
        table: Table,
24✔
4050
        checkConstraint: TableCheck,
24✔
4051
    ): Query {
24✔
4052
        return new Query(
24✔
4053
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
24✔
4054
                checkConstraint.name
24✔
4055
            }" CHECK (${checkConstraint.expression})`,
24✔
4056
        )
24✔
4057
    }
24✔
4058

28✔
4059
    /**
28✔
4060
     * Builds drop check constraint sql.
28✔
4061
     * @param table
28✔
4062
     * @param checkOrName
28✔
4063
     */
28✔
4064
    protected dropCheckConstraintSql(
28✔
4065
        table: Table,
24✔
4066
        checkOrName: TableCheck | string,
24✔
4067
    ): Query {
24✔
4068
        const checkName = InstanceChecker.isTableCheck(checkOrName)
24✔
4069
            ? checkOrName.name
24✔
4070
            : checkOrName
24!
4071
        return new Query(
24✔
4072
            `ALTER TABLE ${this.escapePath(
24✔
4073
                table,
24✔
4074
            )} DROP CONSTRAINT "${checkName}"`,
24✔
4075
        )
24✔
4076
    }
24✔
4077

28✔
4078
    /**
28✔
4079
     * Builds create foreign key sql.
28✔
4080
     * @param table
28✔
4081
     * @param foreignKey
28✔
4082
     */
28✔
4083
    protected createForeignKeySql(
28✔
4084
        table: Table,
6,788✔
4085
        foreignKey: TableForeignKey,
6,788✔
4086
    ): Query {
6,788✔
4087
        const columnNames = foreignKey.columnNames
6,788✔
4088
            .map((column) => `"` + column + `"`)
6,788✔
4089
            .join(", ")
6,788✔
4090
        const referencedColumnNames = foreignKey.referencedColumnNames
6,788✔
4091
            .map((column) => `"` + column + `"`)
6,788✔
4092
            .join(",")
6,788✔
4093
        let sql =
6,788✔
4094
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
6,788✔
4095
                foreignKey.name
6,788✔
4096
            }" FOREIGN KEY (${columnNames}) ` +
6,788✔
4097
            `REFERENCES ${this.escapePath(
6,788✔
4098
                this.getTablePath(foreignKey),
6,788✔
4099
            )}(${referencedColumnNames})`
6,788✔
4100
        if (foreignKey.onDelete) sql += ` ON DELETE ${foreignKey.onDelete}`
6,788✔
4101
        if (foreignKey.onUpdate) sql += ` ON UPDATE ${foreignKey.onUpdate}`
6,788✔
4102

6,788✔
4103
        return new Query(sql)
6,788✔
4104
    }
6,788✔
4105

28✔
4106
    /**
28✔
4107
     * Builds drop foreign key sql.
28✔
4108
     * @param table
28✔
4109
     * @param foreignKeyOrName
28✔
4110
     */
28✔
4111
    protected dropForeignKeySql(
28✔
4112
        table: Table,
6,798✔
4113
        foreignKeyOrName: TableForeignKey | string,
6,798✔
4114
    ): Query {
6,798✔
4115
        const foreignKeyName = InstanceChecker.isTableForeignKey(
6,798✔
4116
            foreignKeyOrName,
6,798✔
4117
        )
6,798✔
4118
            ? foreignKeyOrName.name
6,798✔
4119
            : foreignKeyOrName
6,798!
4120
        return new Query(
6,798✔
4121
            `ALTER TABLE ${this.escapePath(
6,798✔
4122
                table,
6,798✔
4123
            )} DROP CONSTRAINT "${foreignKeyName}"`,
6,798✔
4124
        )
6,798✔
4125
    }
6,798✔
4126

28✔
4127
    /**
28✔
4128
     * Escapes given table or View path.
28✔
4129
     * @param target
28✔
4130
     */
28✔
4131
    protected escapePath(target: Table | View | string): string {
28✔
4132
        const { database, schema, tableName } =
50,616✔
4133
            this.driver.parseTableName(target)
50,616✔
4134

50,616✔
4135
        if (database && database !== this.driver.database) {
50,616✔
4136
            if (schema && schema !== this.driver.searchSchema) {
96✔
4137
                return `"${database}"."${schema}"."${tableName}"`
84✔
4138
            }
84✔
4139

12✔
4140
            return `"${database}".."${tableName}"`
12✔
4141
        }
12✔
4142

50,520✔
4143
        if (schema && schema !== this.driver.searchSchema) {
50,616✔
4144
            return `"${schema}"."${tableName}"`
148✔
4145
        }
148✔
4146

50,372✔
4147
        return `"${tableName}"`
50,372✔
4148
    }
50,372✔
4149

28✔
4150
    /**
28✔
4151
     * Concat database name and schema name to the foreign key name.
28✔
4152
     * Needs because FK name is relevant to the schema and database.
28✔
4153
     * @param fkName
28✔
4154
     * @param schemaName
28✔
4155
     * @param dbName
28✔
4156
     */
28✔
4157
    protected buildForeignKeyName(
28✔
4158
        fkName: string,
8✔
4159
        schemaName: string | undefined,
8✔
4160
        dbName: string | undefined,
8✔
4161
    ): string {
8✔
4162
        let joinedFkName = fkName
8✔
4163
        if (schemaName && schemaName !== this.driver.searchSchema)
8✔
4164
            joinedFkName = schemaName + "." + joinedFkName
8✔
4165
        if (dbName && dbName !== this.driver.database)
8✔
4166
            joinedFkName = dbName + "." + joinedFkName
8✔
4167

8✔
4168
        return joinedFkName
8✔
4169
    }
8✔
4170

28✔
4171
    /**
28✔
4172
     * Removes parenthesis around default value.
28✔
4173
     * Sql server returns default value with parenthesis around, e.g.
28✔
4174
     *  ('My text') - for string
28✔
4175
     *  ((1)) - for number
28✔
4176
     *  (newsequentialId()) - for function
28✔
4177
     * @param defaultValue
28✔
4178
     */
28✔
4179
    protected removeParenthesisFromDefault(defaultValue: string): any {
28✔
4180
        if (defaultValue.substr(0, 1) !== "(") return defaultValue
452✔
4181
        const normalizedDefault = defaultValue.substr(
228✔
4182
            1,
228✔
4183
            defaultValue.lastIndexOf(")") - 1,
228✔
4184
        )
228✔
4185
        return this.removeParenthesisFromDefault(normalizedDefault)
228✔
4186
    }
228✔
4187

28✔
4188
    /**
28✔
4189
     * Builds a query for create column.
28✔
4190
     * @param table
28✔
4191
     * @param column
28✔
4192
     * @param skipIdentity
28✔
4193
     * @param createDefault
28✔
4194
     * @param skipEnum
28✔
4195
     */
28✔
4196
    protected buildCreateColumnSql(
28✔
4197
        table: Table,
33,772✔
4198
        column: TableColumn,
33,772✔
4199
        skipIdentity: boolean,
33,772✔
4200
        createDefault: boolean,
33,772✔
4201
        skipEnum?: boolean,
33,772✔
4202
    ) {
33,772✔
4203
        let c = `"${column.name}" ${this.connection.driver.createFullType(
33,772✔
4204
            column,
33,772✔
4205
        )}`
33,772✔
4206

33,772✔
4207
        if (!skipEnum && column.enum) {
33,772✔
4208
            const expression = this.getEnumExpression(column)
48✔
4209
            const checkName =
48✔
4210
                this.connection.namingStrategy.checkConstraintName(
48✔
4211
                    table,
48✔
4212
                    expression,
48✔
4213
                    true,
48✔
4214
                )
48✔
4215
            c += ` CONSTRAINT ${checkName} CHECK(${expression})`
48✔
4216
        }
48✔
4217

33,772✔
4218
        if (column.collation) c += " COLLATE " + column.collation
33,772✔
4219

33,772✔
4220
        if (column.asExpression) {
33,772✔
4221
            c += ` AS (${column.asExpression})`
78✔
4222
            if (column.generatedType === "STORED") {
78✔
4223
                c += ` PERSISTED`
42✔
4224

42✔
4225
                // NOT NULL can be specified for computed columns only if PERSISTED is also specified
42✔
4226
                if (column.isNullable !== true) c += " NOT NULL"
42✔
4227
            }
42✔
4228
        } else {
33,772✔
4229
            if (column.isNullable !== true) c += " NOT NULL"
33,694✔
4230
        }
33,694✔
4231

33,772✔
4232
        if (
33,772✔
4233
            column.isGenerated === true &&
33,772✔
4234
            column.generationStrategy === "increment" &&
33,772✔
4235
            !skipIdentity
6,248✔
4236
        )
33,772✔
4237
            // don't use skipPrimary here since updates can update already exist primary without auto inc.
33,772✔
4238
            c += " IDENTITY(1,1)"
33,772✔
4239

33,772✔
4240
        if (
33,772✔
4241
            column.default !== undefined &&
33,772✔
4242
            column.default !== null &&
33,772✔
4243
            createDefault
2,018✔
4244
        ) {
33,772✔
4245
            // we create named constraint to be able to delete this constraint when column been dropped
2,006✔
4246
            const defaultName =
2,006✔
4247
                this.connection.namingStrategy.defaultConstraintName(
2,006✔
4248
                    table,
2,006✔
4249
                    column.name,
2,006✔
4250
                )
2,006✔
4251
            c += ` CONSTRAINT "${defaultName}" DEFAULT ${column.default}`
2,006✔
4252
        }
2,006✔
4253

33,772✔
4254
        if (
33,772✔
4255
            column.isGenerated &&
33,772✔
4256
            column.generationStrategy === "uuid" &&
33,772✔
4257
            !column.default
234✔
4258
        ) {
33,772✔
4259
            // we create named constraint to be able to delete this constraint when column been dropped
234✔
4260
            const defaultName =
234✔
4261
                this.connection.namingStrategy.defaultConstraintName(
234✔
4262
                    table,
234✔
4263
                    column.name,
234✔
4264
                )
234✔
4265
            c += ` CONSTRAINT "${defaultName}" DEFAULT NEWSEQUENTIALID()`
234✔
4266
        }
234✔
4267
        return c
33,772✔
4268
    }
33,772✔
4269

28✔
4270
    private getEnumExpression(column: TableColumn) {
28✔
4271
        if (!column.enum) {
52!
4272
            throw new Error(`Enum is not defined in column ${column.name}`)
×
4273
        }
×
4274
        return (
52✔
4275
            column.name +
52✔
4276
            " IN (" +
52✔
4277
            column.enum.map((val) => "'" + val + "'").join(",") +
52✔
4278
            ")"
52✔
4279
        )
52✔
4280
    }
52✔
4281

28✔
4282
    protected isEnumCheckConstraint(name: string): boolean {
28✔
4283
        return name.indexOf("CHK_") !== -1 && name.indexOf("_ENUM") !== -1
218✔
4284
    }
218✔
4285

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

28✔
4377
    /**
28✔
4378
     * Converts string literal of isolation level to enum.
28✔
4379
     * The underlying mssql driver requires an enum for the isolation level.
28✔
4380
     * @param isolation
28✔
4381
     */
28✔
4382
    convertIsolationLevel(isolation: IsolationLevel) {
28✔
4383
        const ISOLATION_LEVEL = this.driver.mssql.ISOLATION_LEVEL
12✔
4384
        switch (isolation) {
12✔
4385
            case "READ UNCOMMITTED":
12✔
4386
                return ISOLATION_LEVEL.READ_UNCOMMITTED
4✔
4387
            case "REPEATABLE READ":
12✔
4388
                return ISOLATION_LEVEL.REPEATABLE_READ
2✔
4389
            case "SERIALIZABLE":
12✔
4390
                return ISOLATION_LEVEL.SERIALIZABLE
4✔
4391

12✔
4392
            case "READ COMMITTED":
12✔
4393
            default:
12✔
4394
                return ISOLATION_LEVEL.READ_COMMITTED
2✔
4395
        }
12✔
4396
    }
12✔
4397

28✔
4398
    /**
28✔
4399
     * Change table comment.
28✔
4400
     * @param tableOrName
28✔
4401
     * @param comment
28✔
4402
     */
28✔
4403
    changeTableComment(
28✔
4404
        tableOrName: Table | string,
×
4405
        comment?: string,
×
4406
    ): Promise<void> {
×
4407
        throw new TypeORMError(
×
4408
            `sqlserver driver does not support change table comment.`,
×
4409
        )
×
4410
    }
×
4411
}
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