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

typeorm / typeorm / 23390157208

21 Mar 2026 10:26PM UTC coverage: 56.678% (-16.6%) from 73.277%
23390157208

Pull #12252

github

web-flow
Merge 5b60ba41c into 7038fa166
Pull Request #12252: fix: unskip cascade soft remove test

17767 of 26580 branches covered (66.84%)

Branch coverage included in aggregate %.

64033 of 117744 relevant lines covered (54.38%)

1514.83 hits per line

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

28.57
/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()
34✔
60
        this.driver = driver
34✔
61
        this.connection = driver.connection
34✔
62
        this.broadcaster = new Broadcaster(this)
34✔
63
        this.mode = mode
34✔
64
    }
34✔
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()
×
76
    }
×
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
34✔
84
        return Promise.resolve()
34✔
85
    }
34✔
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()
20!
93

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

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

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

20✔
146
        if (!this.isTransactionActive) throw new TransactionNotStartedError()
20!
147

20✔
148
        await this.broadcaster.broadcast("BeforeTransactionCommit")
20✔
149

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

20✔
157
                    await this.broadcaster.broadcast("AfterTransactionCommit")
20✔
158

20✔
159
                    ok()
20✔
160
                    this.connection.logger.logQuery("COMMIT")
20✔
161
                    this.transactionDepth -= 1
20✔
162
                })
20✔
163
            })
20✔
164
        }
20✔
165
        this.transactionDepth -= 1
×
166
    }
×
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()
×
174

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

×
177
        await this.broadcaster.broadcast("BeforeTransactionRollback")
×
178

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

×
191
                    await this.broadcaster.broadcast("AfterTransactionRollback")
×
192

×
193
                    ok()
×
194
                    this.connection.logger.logQuery("ROLLBACK")
×
195
                    this.transactionDepth -= 1
×
196
                })
×
197
            })
×
198
        }
×
199
    }
×
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,
152✔
209
        parameters?: any[],
152✔
210
        useStructuredResult = false,
152✔
211
    ): Promise<any> {
152✔
212
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
152!
213

152✔
214
        const release = await this.lock.acquire()
152✔
215

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

152✔
219
        const broadcasterResult = new BroadcasterResult()
152✔
220

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

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

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

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

152✔
280
                    if (err) {
152!
281
                        fail(new QueryFailedError(query, parameters, err))
×
282
                    }
×
283

152✔
284
                    ok(raw)
152✔
285
                })
152✔
286
            })
152✔
287

152✔
288
            const result = new QueryResult()
152✔
289

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

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

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

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

×
330
            throw err
×
331
        } finally {
×
332
            await broadcasterResult.wait()
152✔
333

152✔
334
            release()
152✔
335
        }
152✔
336
    }
152✔
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,
×
347
        parameters?: any[],
×
348
        onEnd?: Function,
×
349
        onError?: Function,
×
350
    ): Promise<ReadStream> {
×
351
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
×
352

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

×
355
        this.driver.connection.logger.logQuery(query, parameters, this)
×
356
        const pool = await (this.mode === "slave"
×
357
            ? this.driver.obtainSlaveConnection()
×
358
            : this.driver.obtainMasterConnection())
×
359
        const request = new this.driver.mssql.Request(
×
360
            this.isTransactionActive ? this.databaseConnection : pool,
×
361
        )
×
362
        if (parameters && parameters.length) {
×
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

×
377
        request.query(query)
×
378

×
379
        const streamRequest = request.toReadableStream()
×
380

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

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

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

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

×
403
        return streamRequest
×
404
    }
×
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"`, [
4✔
433
            database,
4✔
434
        ])
4✔
435
        const dbId = result[0]["db_id"]
4✔
436
        return !!dbId
4✔
437
    }
4✔
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"`)
4✔
444
        return currentDBQuery[0]["db_name"]
4✔
445
    }
4✔
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"`, [
×
453
            schema,
×
454
        ])
×
455
        const schemaId = result[0]["schema_id"]
×
456
        return !!schemaId
×
457
    }
×
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(
6✔
464
            `SELECT SCHEMA_NAME() AS "schema_name"`,
6✔
465
        )
6✔
466
        return currentSchemaQuery[0]["schema_name"]
6✔
467
    }
6✔
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)
4✔
475

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

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

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

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

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

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

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

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

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

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

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

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

×
599
        await this.executeQueries(upQueries, downQueries)
×
600
    }
×
601

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

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

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

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

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

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

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

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

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

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

12✔
699
        for (const column of generatedColumns) {
12!
700
            const parsedTableName = this.driver.parseTableName(table)
×
701

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

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

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

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

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

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

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

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

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

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

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

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

×
781
        for (const column of generatedColumns) {
×
782
            const parsedTableName = this.driver.parseTableName(table)
×
783

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

×
988
            // replace constraint name
×
989
            unique.name = newUniqueName
×
990
        })
×
991

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

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

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

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

×
1026
            // replace constraint name
×
1027
            index.name = newIndexName
×
1028
        })
×
1029

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

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

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

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

×
1072
            // replace constraint name
×
1073
            foreignKey.name = newForeignKeyName
×
1074
        })
×
1075

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

×
1082
        await this.executeQueries(upQueries, downQueries)
×
1083

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

×
1265
        await this.executeQueries(upQueries, downQueries)
×
1266

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

×
1484
                    // replace constraint name
×
1485
                    index.name = newIndexName
×
1486
                })
×
1487

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

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

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

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

×
1537
                        // replace constraint name
×
1538
                        foreignKey.name = newForeignKeyName
×
1539
                    })
×
1540

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

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

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

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

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

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

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

×
1614
                    // replace constraint name
×
1615
                    unique.name = newUniqueName
×
1616
                })
×
1617

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

×
2154
        if (column.generatedType && column.asExpression) {
×
2155
            const parsedTableName = this.driver.parseTableName(table)
×
2156

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

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

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

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

×
2201
        await this.executeQueries(upQueries, downQueries)
×
2202

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

×
2953
        if (!viewPaths) {
×
2954
            viewPaths = []
×
2955
        }
×
2956

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

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

×
2964
        if (
×
2965
            this.driver.database &&
×
2966
            !dbNames.find((dbName) => dbName === this.driver.database)
×
2967
        )
4✔
2968
            dbNames.push(this.driver.database)
4!
2969

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

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

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

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

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

4✔
3025
        const currentSchema = await this.getCurrentSchema()
4✔
3026
        const currentDatabase = await this.getCurrentDatabase()
4✔
3027

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

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

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

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

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

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

4✔
3097
            dbTables.push(...(await this.query(tablesSql)))
4✔
3098
        }
4✔
3099

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

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

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

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

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

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

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

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

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

×
3196
                return (
×
3197
                    `SELECT "TABLE_CATALOG", "TABLE_SCHEMA", "COLUMN_NAME", "TABLE_NAME" ` +
×
3198
                    `FROM "${TABLE_CATALOG}"."INFORMATION_SCHEMA"."COLUMNS" ` +
×
3199
                    `WHERE ` +
×
3200
                    `EXISTS(SELECT 1 FROM "${TABLE_CATALOG}"."sys"."columns" "S" WHERE OBJECT_ID("TABLE_CATALOG" + '.' + "TABLE_SCHEMA" + '.' + "TABLE_NAME") = "S"."OBJECT_ID" AND "COLUMN_NAME" = "S"."NAME" AND "S"."is_identity" = 1) AND ` +
×
3201
                    `(${conditions})`
×
3202
                )
×
3203
            })
×
3204
            .join(" UNION ALL ")
×
3205

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

×
3574
                            return tableColumn
×
3575
                        }),
×
3576
                )
×
3577

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

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

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

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

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

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

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

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

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

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

×
3732
                return table
×
3733
            }),
×
3734
        )
×
3735
    }
×
3736

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

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

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

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

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

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

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

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

×
3830
                    return constraint
×
3831
                })
×
3832
                .join(", ")
×
3833

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

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

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

12✔
3854
        sql += `)`
12✔
3855

12✔
3856
        return new Query(sql)
12✔
3857
    }
12✔
3858

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

12✔
4127
        return new Query(sql)
12✔
4128
    }
12✔
4129

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

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

76✔
4159
        if (database && database !== this.driver.database) {
76!
4160
            if (schema && schema !== this.driver.searchSchema) {
×
4161
                return `"${database}"."${schema}"."${tableName}"`
×
4162
            }
×
4163

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

76✔
4167
        if (schema && schema !== this.driver.searchSchema) {
76!
4168
            return `"${schema}"."${tableName}"`
×
4169
        }
×
4170

76✔
4171
        return `"${tableName}"`
76✔
4172
    }
76✔
4173

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

×
4192
        return joinedFkName
×
4193
    }
×
4194

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

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

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

36✔
4242
        if (column.collation) c += " COLLATE " + column.collation
36!
4243

36✔
4244
        if (column.asExpression) {
36!
4245
            c += ` AS (${column.asExpression})`
×
4246
            if (column.generatedType === "STORED") {
×
4247
                c += ` PERSISTED`
×
4248

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

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

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

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

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

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

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

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

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

28✔
4422
    /**
28✔
4423
     * Change table comment.
28✔
4424
     * @param tableOrName
28✔
4425
     * @param comment
28✔
4426
     */
28✔
4427
    changeTableComment(
28✔
4428
        tableOrName: Table | string,
×
4429
        comment?: string,
×
4430
    ): Promise<void> {
×
4431
        throw new TypeORMError(
×
4432
            `sqlserver driver does not support change table comment.`,
×
4433
        )
×
4434
    }
×
4435
}
28✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc