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

typeorm / typeorm / 15805338492

22 Jun 2025 09:51AM UTC coverage: 76.344% (-0.07%) from 76.418%
15805338492

push

github

web-flow
fix: add stricter type-checking and improve event loop handling (#11540)

* refactor: minor type improvements

* chore: add type-checked eslint rules

* fix: enable no-misused-promises

* fix: enable no-floating-promises

* fix: enable await-thenable

* fix: enable require-await

* fix: enable no-misused-new

* refactor: enable no-namespace

* refactor: enable tseslint eslint recommended

* code review

9281 of 12872 branches covered (72.1%)

Branch coverage included in aggregate %.

117 of 210 new or added lines in 18 files covered. (55.71%)

19 existing lines in 9 files now uncovered.

18996 of 24167 relevant lines covered (78.6%)

119161.42 hits per line

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

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

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

40
    /**
41
     * Database driver used by connection.
42
     */
43
    driver: SapDriver
44

45
    // -------------------------------------------------------------------------
46
    // Protected Properties
47
    // -------------------------------------------------------------------------
48

49
    /**
50
     * Promise used to obtain a database connection from a pool for a first time.
51
     */
52
    protected databaseConnectionPromise: Promise<any>
53

54
    private lock: QueryLock = new QueryLock()
22,268✔
55

56
    // -------------------------------------------------------------------------
57
    // Constructor
58
    // -------------------------------------------------------------------------
59

60
    constructor(driver: SapDriver, mode: ReplicationMode) {
61
        super()
22,268✔
62
        this.driver = driver
22,268✔
63
        this.connection = driver.connection
22,268✔
64
        this.broadcaster = new Broadcaster(this)
22,268✔
65
        this.mode = mode
22,268✔
66
    }
67

68
    // -------------------------------------------------------------------------
69
    // Public Methods
70
    // -------------------------------------------------------------------------
71

72
    /**
73
     * Creates/uses database connection from the connection pool to perform further operations.
74
     * Returns obtained database connection.
75
     */
76
    async connect(): Promise<any> {
77
        if (this.databaseConnection) return this.databaseConnection
129,876✔
78

79
        this.databaseConnection = await this.driver.obtainMasterConnection()
21,392✔
80

81
        return this.databaseConnection
21,392✔
82
    }
83

84
    /**
85
     * Releases used database connection.
86
     * You cannot use query runner methods once its released.
87
     */
88
    release(): Promise<void> {
89
        this.isReleased = true
22,266✔
90

91
        if (this.databaseConnection) {
22,266✔
92
            return this.driver.master.release(this.databaseConnection)
21,390✔
93
        }
94

95
        return Promise.resolve()
876✔
96
    }
97

98
    /**
99
     * Starts transaction.
100
     */
101
    async startTransaction(isolationLevel?: IsolationLevel): Promise<void> {
102
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
16,808!
103

104
        if (
16,808✔
105
            this.isTransactionActive &&
16,814✔
106
            this.driver.transactionSupport === "simple"
107
        )
108
            throw new TransactionAlreadyStartedError()
6✔
109

110
        await this.broadcaster.broadcast("BeforeTransactionStart")
16,802✔
111

112
        this.isTransactionActive = true
16,802✔
113

114
        /**
115
         * Disable AUTOCOMMIT while running transaction.
116
         *  Otherwise, COMMIT/ROLLBACK doesn't work in autocommit mode.
117
         */
118
        await this.setAutoCommit({ status: "off" })
16,802✔
119

120
        if (isolationLevel) {
16,802✔
121
            await this.query(
10✔
122
                `SET TRANSACTION ISOLATION LEVEL ${isolationLevel || ""}`,
10!
123
            )
124
        }
125

126
        await this.broadcaster.broadcast("AfterTransactionStart")
16,802✔
127
    }
128

129
    /**
130
     * Commits transaction.
131
     * Error will be thrown if transaction was not started.
132
     */
133
    async commitTransaction(): Promise<void> {
134
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
16,762!
135

136
        if (!this.isTransactionActive) throw new TransactionNotStartedError()
16,762✔
137

138
        await this.broadcaster.broadcast("BeforeTransactionCommit")
16,760✔
139

140
        await this.query("COMMIT")
16,760✔
141
        this.isTransactionActive = false
16,760✔
142

143
        await this.setAutoCommit({ status: "on" })
16,760✔
144
        await this.broadcaster.broadcast("AfterTransactionCommit")
16,760✔
145
    }
146

147
    /**
148
     * Rollbacks transaction.
149
     * Error will be thrown if transaction was not started.
150
     */
151
    async rollbackTransaction(): Promise<void> {
152
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
48!
153

154
        if (!this.isTransactionActive) throw new TransactionNotStartedError()
48✔
155

156
        await this.broadcaster.broadcast("BeforeTransactionRollback")
42✔
157

158
        await this.query("ROLLBACK")
42✔
159
        this.isTransactionActive = false
42✔
160

161
        await this.setAutoCommit({ status: "on" })
42✔
162
        await this.broadcaster.broadcast("AfterTransactionRollback")
42✔
163
    }
164

165
    /**
166
     * @description Switches on/off AUTOCOMMIT mode
167
     * @link https://help.sap.com/docs/HANA_SERVICE_CF/7c78579ce9b14a669c1f3295b0d8ca16/d538d11053bd4f3f847ec5ce817a3d4c.html?locale=en-US
168
     */
169
    async setAutoCommit(options: { status: "on" | "off" }) {
170
        const connection = await this.connect()
33,604✔
171

172
        const execute = promisify(connection.exec.bind(connection))
33,604✔
173

174
        connection.setAutoCommit(options.status === "on")
33,604✔
175

176
        const query = `SET TRANSACTION AUTOCOMMIT DDL ${options.status.toUpperCase()};`
33,604✔
177
        try {
33,604✔
178
            await execute(query)
33,604✔
179
        } catch (error) {
180
            throw new QueryFailedError(query, [], error)
×
181
        }
182
    }
183

184
    /**
185
     * Executes a given SQL query.
186
     */
187
    async query(
188
        query: string,
189
        parameters?: any[],
190
        useStructuredResult = false,
62,176✔
191
    ): Promise<any> {
192
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
96,270!
193

194
        const release = await this.lock.acquire()
96,270✔
195

196
        const databaseConnection = await this.connect()
96,270✔
197

198
        let statement: any
199
        const result = new QueryResult()
96,270✔
200

201
        this.driver.connection.logger.logQuery(query, parameters, this)
96,270✔
202
        await this.broadcaster.broadcast("BeforeQuery", query, parameters)
96,270✔
203

204
        const broadcasterResult = new BroadcasterResult()
96,270✔
205

206
        try {
96,270✔
207
            const queryStartTime = Date.now()
96,270✔
208
            const isInsertQuery = query.substr(0, 11) === "INSERT INTO"
96,270✔
209

210
            if (parameters?.some(Array.isArray)) {
96,270!
211
                statement = await promisify(databaseConnection.prepare).call(
×
212
                    databaseConnection,
213
                    query,
214
                )
215
            }
216

217
            let raw: any
218
            try {
96,270✔
219
                raw = statement
96,270!
220
                    ? await promisify(statement.exec).call(
221
                          statement,
222
                          parameters,
223
                      )
224
                    : await promisify(databaseConnection.exec).call(
225
                          databaseConnection,
226
                          query,
227
                          parameters,
228
                          {},
229
                      )
230
            } catch (err) {
231
                throw new QueryFailedError(query, parameters, err)
12✔
232
            }
233

234
            // log slow queries if maxQueryExecution time is set
235
            const maxQueryExecutionTime =
236
                this.driver.connection.options.maxQueryExecutionTime
96,258✔
237
            const queryEndTime = Date.now()
96,258✔
238
            const queryExecutionTime = queryEndTime - queryStartTime
96,258✔
239

240
            this.broadcaster.broadcastAfterQueryEvent(
96,258✔
241
                broadcasterResult,
242
                query,
243
                parameters,
244
                true,
245
                queryExecutionTime,
246
                raw,
247
                undefined,
248
            )
249

250
            if (
96,258!
251
                maxQueryExecutionTime &&
96,258!
252
                queryExecutionTime > maxQueryExecutionTime
253
            ) {
254
                this.driver.connection.logger.logQuerySlow(
×
255
                    queryExecutionTime,
256
                    query,
257
                    parameters,
258
                    this,
259
                )
260
            }
261

262
            if (typeof raw === "number") {
96,258✔
263
                result.affected = raw
37,064✔
264
            } else if (Array.isArray(raw)) {
59,194✔
265
                result.records = raw
31,404✔
266
            }
267

268
            result.raw = raw
96,258✔
269

270
            if (isInsertQuery) {
96,258✔
271
                const lastIdQuery = `SELECT CURRENT_IDENTITY_VALUE() FROM "SYS"."DUMMY"`
18,668✔
272
                this.driver.connection.logger.logQuery(lastIdQuery, [], this)
18,668✔
273
                const identityValueResult = await new Promise<any>(
18,668✔
274
                    (ok, fail) => {
275
                        databaseConnection.exec(
18,668✔
276
                            lastIdQuery,
277
                            (err: any, raw: any) =>
278
                                err
18,668!
279
                                    ? fail(
280
                                          new QueryFailedError(
281
                                              lastIdQuery,
282
                                              [],
283
                                              err,
284
                                          ),
285
                                      )
286
                                    : ok(raw),
287
                        )
288
                    },
289
                )
290

291
                result.raw = identityValueResult[0]["CURRENT_IDENTITY_VALUE()"]
18,668✔
292
                result.records = identityValueResult
18,668✔
293
            }
294
        } catch (err) {
295
            this.driver.connection.logger.logQueryError(
12✔
296
                err,
297
                query,
298
                parameters,
299
                this,
300
            )
301
            this.broadcaster.broadcastAfterQueryEvent(
12✔
302
                broadcasterResult,
303
                query,
304
                parameters,
305
                false,
306
                undefined,
307
                undefined,
308
                err,
309
            )
310
            throw err
12✔
311
        } finally {
312
            // Never forget to drop the statement we reserved
313
            if (statement?.drop) {
96,270!
314
                await new Promise<void>((ok) => statement.drop(() => ok()))
×
315
            }
316

317
            await broadcasterResult.wait()
96,270✔
318

319
            // Always release the lock.
320
            release()
96,270✔
321
        }
322

323
        if (useStructuredResult) {
96,258✔
324
            return result
34,084✔
325
        } else {
326
            return result.raw
62,174✔
327
        }
328
    }
329

330
    /**
331
     * Returns raw data stream.
332
     */
333
    async stream(
334
        query: string,
335
        parameters?: any[],
336
        onEnd?: Function,
337
        onError?: Function,
338
    ): Promise<ReadStream> {
339
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
2!
340

341
        const release = await this.lock.acquire()
2✔
342
        let statement: any
343
        let resultSet: any
344

345
        const cleanup = async () => {
2✔
346
            if (resultSet) {
2✔
347
                await promisify(resultSet.close).call(resultSet)
2✔
348
            }
349
            if (statement) {
2✔
350
                await promisify(statement.drop).call(statement)
2✔
351
            }
352
            release()
2✔
353
        }
354

355
        try {
2✔
356
            const databaseConnection = await this.connect()
2✔
357
            this.driver.connection.logger.logQuery(query, parameters, this)
2✔
358

359
            statement = await promisify(databaseConnection.prepare).call(
2✔
360
                databaseConnection,
361
                query,
362
            )
363
            resultSet = await promisify(statement.executeQuery).call(
2✔
364
                statement,
365
                parameters,
366
            )
367

368
            const stream =
369
                this.driver.streamClient.createObjectStream(resultSet)
2✔
370
            stream.on("end", async () => {
2✔
371
                await cleanup()
2✔
372
                onEnd?.()
2✔
373
            })
374
            stream.on("error", async (error: Error) => {
2✔
375
                this.driver.connection.logger.logQueryError(
×
376
                    error,
377
                    query,
378
                    parameters,
379
                    this,
380
                )
381
                await cleanup()
×
382
                onError?.(error)
×
383
            })
384

385
            return stream
2✔
386
        } catch (error) {
387
            this.driver.connection.logger.logQueryError(
×
388
                error,
389
                query,
390
                parameters,
391
                this,
392
            )
393
            await cleanup()
×
394
            throw new QueryFailedError(query, parameters, error)
×
395
        }
396
    }
397

398
    /**
399
     * Returns all available database names including system databases.
400
     */
401
    async getDatabases(): Promise<string[]> {
402
        const results: ObjectLiteral[] = await this.query(
×
403
            `SELECT DATABASE_NAME FROM "SYS"."M_DATABASES"`,
404
        )
405
        return results.map((result) => result["DATABASE_NAME"])
×
406
    }
407

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

420
    /**
421
     * Checks if database with the given name exist.
422
     */
423
    async hasDatabase(database: string): Promise<boolean> {
424
        const databases = await this.getDatabases()
×
425
        return databases.indexOf(database) !== -1
×
426
    }
427

428
    /**
429
     * Returns current database.
430
     */
431
    async getCurrentDatabase(): Promise<string> {
432
        const currentDBQuery: [{ dbName: string }] = await this.query(
2,894✔
433
            `SELECT "DATABASE_NAME" AS "dbName" FROM "SYS"."M_DATABASE"`,
434
        )
435

436
        return currentDBQuery[0].dbName
2,894✔
437
    }
438

439
    /**
440
     * Returns the database server version.
441
     */
442
    async getDatabaseAndVersion(): Promise<{
443
        database: string
444
        version: string
445
    }> {
446
        const currentDBQuery: [{ database: string; version: string }] =
447
            await this.query(
846✔
448
                `SELECT  "DATABASE_NAME" AS "database", "VERSION" AS "version" FROM "SYS"."M_DATABASE"`,
449
            )
450

451
        return currentDBQuery[0]
846✔
452
    }
453

454
    /**
455
     * Checks if schema with the given name exist.
456
     */
457
    async hasSchema(schema: string): Promise<boolean> {
458
        const schemas = await this.getSchemas()
6✔
459
        return schemas.indexOf(schema) !== -1
6✔
460
    }
461

462
    /**
463
     * Returns current schema.
464
     */
465
    async getCurrentSchema(): Promise<string> {
466
        const currentSchemaQuery: [{ schemaName: string }] = await this.query(
3,740✔
467
            `SELECT CURRENT_SCHEMA AS "schemaName" FROM "SYS"."DUMMY"`,
468
        )
469

470
        return currentSchemaQuery[0].schemaName
3,740✔
471
    }
472

473
    /**
474
     * Checks if table with the given name exist in the database.
475
     */
476
    async hasTable(tableOrName: Table | string): Promise<boolean> {
477
        const parsedTableName = this.driver.parseTableName(tableOrName)
2,602✔
478

479
        if (!parsedTableName.schema) {
2,602!
480
            parsedTableName.schema = await this.getCurrentSchema()
×
481
        }
482

483
        const sql = `SELECT COUNT(*) as "hasTable" FROM "SYS"."TABLES" WHERE "SCHEMA_NAME" = '${parsedTableName.schema}' AND "TABLE_NAME" = '${parsedTableName.tableName}'`
2,602✔
484
        const result: [{ hasTable: number }] = await this.query(sql)
2,602✔
485

486
        return result[0].hasTable > 0
2,602✔
487
    }
488

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

498
        if (!parsedTableName.schema) {
8!
499
            parsedTableName.schema = await this.getCurrentSchema()
×
500
        }
501

502
        const sql = `SELECT COUNT(*) as "hasColumn" FROM "SYS"."TABLE_COLUMNS" WHERE "SCHEMA_NAME" = '${parsedTableName.schema}' AND "TABLE_NAME" = '${parsedTableName.tableName}' AND "COLUMN_NAME" = '${columnName}'`
8✔
503
        const result: [{ hasColumn: number }] = await this.query(sql)
8✔
504

505
        return result[0].hasColumn > 0
8✔
506
    }
507

508
    /**
509
     * Creates a new database.
510
     */
511
    async createDatabase(
512
        database: string,
513
        ifNotExist?: boolean,
514
    ): Promise<void> {
515
        return Promise.resolve()
×
516
    }
517

518
    /**
519
     * Drops database.
520
     */
521
    async dropDatabase(database: string, ifExist?: boolean): Promise<void> {
522
        return Promise.resolve()
4✔
523
    }
524

525
    /**
526
     * Creates a new table schema.
527
     */
528
    async createSchema(
529
        schemaPath: string,
530
        ifNotExist?: boolean,
531
    ): Promise<void> {
532
        const schema =
533
            schemaPath.indexOf(".") === -1
12!
534
                ? schemaPath
535
                : schemaPath.split(".")[1]
536

537
        let exist = false
12✔
538
        if (ifNotExist) {
12✔
539
            const result = await this.query(
12✔
540
                `SELECT * FROM "SYS"."SCHEMAS" WHERE "SCHEMA_NAME" = '${schema}'`,
541
            )
542
            exist = !!result.length
12✔
543
        }
544
        if (!ifNotExist || (ifNotExist && !exist)) {
12✔
545
            const up = `CREATE SCHEMA "${schema}"`
10✔
546
            const down = `DROP SCHEMA "${schema}" CASCADE`
10✔
547
            await this.executeQueries(new Query(up), new Query(down))
10✔
548
        }
549
    }
550

551
    /**
552
     * Drops table schema
553
     */
554
    async dropSchema(
555
        schemaPath: string,
556
        ifExist?: boolean,
557
        isCascade?: boolean,
558
    ): Promise<void> {
559
        const schema =
560
            schemaPath.indexOf(".") === -1
2!
561
                ? schemaPath
562
                : schemaPath.split(".")[0]
563
        let exist = false
2✔
564
        if (ifExist) {
2!
565
            const result = await this.query(
×
566
                `SELECT * FROM "SYS"."SCHEMAS" WHERE "SCHEMA_NAME" = '${schema}'`,
567
            )
568
            exist = !!result.length
×
569
        }
570
        if (!ifExist || (ifExist && exist)) {
2!
571
            const up = `DROP SCHEMA "${schema}" ${isCascade ? "CASCADE" : ""}`
2!
572
            const down = `CREATE SCHEMA "${schema}"`
2✔
573
            await this.executeQueries(new Query(up), new Query(down))
2✔
574
        }
575
    }
576

577
    /**
578
     * Creates a new table.
579
     */
580
    async createTable(
581
        table: Table,
582
        ifNotExist: boolean = false,
42✔
583
        createForeignKeys: boolean = true,
76✔
584
        createIndices: boolean = true,
8,480✔
585
    ): Promise<void> {
586
        if (ifNotExist) {
8,480✔
587
            const isTableExist = await this.hasTable(table)
34✔
588
            if (isTableExist) return Promise.resolve()
34✔
589
        }
590
        const upQueries: Query[] = []
8,478✔
591
        const downQueries: Query[] = []
8,478✔
592

593
        upQueries.push(this.createTableSql(table, createForeignKeys))
8,478✔
594
        downQueries.push(this.dropTableSql(table))
8,478✔
595

596
        // if createForeignKeys is true, we must drop created foreign keys in down query.
597
        // createTable does not need separate method to create foreign keys, because it create fk's in the same query with table creation.
598
        if (createForeignKeys)
8,478✔
599
            table.foreignKeys.forEach((foreignKey) =>
74✔
600
                downQueries.push(this.dropForeignKeySql(table, foreignKey)),
8✔
601
            )
602

603
        if (createIndices) {
8,478✔
604
            table.indices.forEach((index) => {
8,478✔
605
                // new index may be passed without name. In this case we generate index name manually.
606
                if (!index.name)
4,688✔
607
                    index.name = this.connection.namingStrategy.indexName(
10✔
608
                        table,
609
                        index.columnNames,
610
                        index.where,
611
                    )
612
                upQueries.push(this.createIndexSql(table, index))
4,688✔
613
                downQueries.push(this.dropIndexSql(table, index))
4,688✔
614
            })
615
        }
616

617
        await this.executeQueries(upQueries, downQueries)
8,478✔
618
    }
619

620
    /**
621
     * Drops the table.
622
     */
623
    async dropTable(
624
        tableOrName: Table | string,
625
        ifExist?: boolean,
626
        dropForeignKeys: boolean = true,
8✔
627
        dropIndices: boolean = true,
16✔
628
    ): Promise<void> {
629
        if (ifExist) {
16✔
630
            const isTableExist = await this.hasTable(tableOrName)
8✔
631
            if (!isTableExist) return Promise.resolve()
8!
632
        }
633

634
        // if dropTable called with dropForeignKeys = true, we must create foreign keys in down query.
635
        const createForeignKeys: boolean = dropForeignKeys
16✔
636
        const table = InstanceChecker.isTable(tableOrName)
16✔
637
            ? tableOrName
638
            : await this.getCachedTable(tableOrName)
639
        const upQueries: Query[] = []
16✔
640
        const downQueries: Query[] = []
16✔
641

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

645
        if (dropIndices) {
16✔
646
            table.indices.forEach((index) => {
16✔
647
                upQueries.push(this.dropIndexSql(table, index))
6✔
648
                downQueries.push(this.createIndexSql(table, index))
6✔
649
            })
650
        }
651

652
        // if dropForeignKeys is true, we just drop the table, otherwise we also drop table foreign keys.
653
        // createTable does not need separate method to create foreign keys, because it create fk's in the same query with table creation.
654
        if (dropForeignKeys)
16✔
655
            table.foreignKeys.forEach((foreignKey) =>
8✔
656
                upQueries.push(this.dropForeignKeySql(table, foreignKey)),
4✔
657
            )
658

659
        upQueries.push(this.dropTableSql(table))
16✔
660
        downQueries.push(this.createTableSql(table, createForeignKeys))
16✔
661

662
        await this.executeQueries(upQueries, downQueries)
16✔
663
    }
664

665
    /**
666
     * Creates a new view.
667
     */
668
    async createView(
669
        view: View,
670
        syncWithMetadata: boolean = false,
×
671
    ): Promise<void> {
672
        const upQueries: Query[] = []
12✔
673
        const downQueries: Query[] = []
12✔
674
        upQueries.push(this.createViewSql(view))
12✔
675
        if (syncWithMetadata)
12✔
676
            upQueries.push(await this.insertViewDefinitionSql(view))
12✔
677
        downQueries.push(this.dropViewSql(view))
12✔
678
        if (syncWithMetadata)
12✔
679
            downQueries.push(await this.deleteViewDefinitionSql(view))
12✔
680
        await this.executeQueries(upQueries, downQueries)
12✔
681
    }
682

683
    /**
684
     * Drops the view.
685
     */
686
    async dropView(target: View | string): Promise<void> {
687
        const viewName = InstanceChecker.isView(target) ? target.name : target
×
688
        const view = await this.getCachedView(viewName)
×
689

690
        const upQueries: Query[] = []
×
691
        const downQueries: Query[] = []
×
692
        upQueries.push(await this.deleteViewDefinitionSql(view))
×
693
        upQueries.push(this.dropViewSql(view))
×
694
        downQueries.push(await this.insertViewDefinitionSql(view))
×
695
        downQueries.push(this.createViewSql(view))
×
696
        await this.executeQueries(upQueries, downQueries)
×
697
    }
698

699
    /**
700
     * Renames a table.
701
     */
702
    async renameTable(
703
        oldTableOrName: Table | string,
704
        newTableName: string,
705
    ): Promise<void> {
706
        const upQueries: Query[] = []
10✔
707
        const downQueries: Query[] = []
10✔
708
        const oldTable = InstanceChecker.isTable(oldTableOrName)
10✔
709
            ? oldTableOrName
710
            : await this.getCachedTable(oldTableOrName)
711
        const newTable = oldTable.clone()
10✔
712

713
        const { schema: schemaName, tableName: oldTableName } =
714
            this.driver.parseTableName(oldTable)
10✔
715

716
        newTable.name = schemaName
10!
717
            ? `${schemaName}.${newTableName}`
718
            : newTableName
719

720
        // rename table
721
        upQueries.push(
10✔
722
            new Query(
723
                `RENAME TABLE ${this.escapePath(oldTable)} TO ${this.escapePath(
724
                    newTable,
725
                )}`,
726
            ),
727
        )
728
        downQueries.push(
10✔
729
            new Query(
730
                `RENAME TABLE ${this.escapePath(newTable)} TO ${this.escapePath(
731
                    oldTable,
732
                )}`,
733
            ),
734
        )
735

736
        // drop old FK's. Foreign keys must be dropped before the primary keys are dropped
737
        newTable.foreignKeys.forEach((foreignKey) => {
10✔
738
            upQueries.push(this.dropForeignKeySql(newTable, foreignKey))
2✔
739
            downQueries.push(this.createForeignKeySql(newTable, foreignKey))
2✔
740
        })
741

742
        // SAP HANA does not allow to drop PK's which is referenced by foreign keys.
743
        // To avoid this, we must drop all referential foreign keys and recreate them later
744
        const referencedForeignKeySql = `SELECT * FROM "SYS"."REFERENTIAL_CONSTRAINTS" WHERE "REFERENCED_SCHEMA_NAME" = '${schemaName}' AND "REFERENCED_TABLE_NAME" = '${oldTableName}'`
10✔
745
        const dbForeignKeys: ObjectLiteral[] = await this.query(
10✔
746
            referencedForeignKeySql,
747
        )
748
        let referencedForeignKeys: TableForeignKey[] = []
10✔
749
        const referencedForeignKeyTableMapping: {
750
            tableName: string
751
            fkName: string
752
        }[] = []
10✔
753
        if (dbForeignKeys.length > 0) {
10✔
754
            referencedForeignKeys = dbForeignKeys.map((dbForeignKey) => {
6✔
755
                const foreignKeys = dbForeignKeys.filter(
6✔
756
                    (dbFk) =>
757
                        dbFk["CONSTRAINT_NAME"] ===
6✔
758
                        dbForeignKey["CONSTRAINT_NAME"],
759
                )
760

761
                referencedForeignKeyTableMapping.push({
6✔
762
                    tableName: `${dbForeignKey["SCHEMA_NAME"]}.${dbForeignKey["TABLE_NAME"]}`,
763
                    fkName: dbForeignKey["CONSTRAINT_NAME"],
764
                })
765
                return new TableForeignKey({
6✔
766
                    name: dbForeignKey["CONSTRAINT_NAME"],
767
                    columnNames: foreignKeys.map((dbFk) => dbFk["COLUMN_NAME"]),
6✔
768
                    referencedDatabase: newTable.database,
769
                    referencedSchema: newTable.schema,
770
                    referencedTableName: newTable.name, // we use renamed table name
771
                    referencedColumnNames: foreignKeys.map(
772
                        (dbFk) => dbFk["REFERENCED_COLUMN_NAME"],
6✔
773
                    ),
774
                    onDelete:
775
                        dbForeignKey["DELETE_RULE"] === "RESTRICT"
6!
776
                            ? "NO ACTION"
777
                            : dbForeignKey["DELETE_RULE"],
778
                    onUpdate:
779
                        dbForeignKey["UPDATE_RULE"] === "RESTRICT"
6!
780
                            ? "NO ACTION"
781
                            : dbForeignKey["UPDATE_RULE"],
782
                    deferrable: dbForeignKey["CHECK_TIME"].replace("_", " "), // "CHECK_TIME" is "INITIALLY_IMMEDIATE" or "INITIALLY DEFERRED"
783
                })
784
            })
785

786
            // drop referenced foreign keys
787
            referencedForeignKeys.forEach((foreignKey) => {
6✔
788
                const mapping = referencedForeignKeyTableMapping.find(
6✔
789
                    (it) => it.fkName === foreignKey.name,
6✔
790
                )
791
                upQueries.push(
6✔
792
                    this.dropForeignKeySql(mapping!.tableName, foreignKey),
793
                )
794
                downQueries.push(
6✔
795
                    this.createForeignKeySql(mapping!.tableName, foreignKey),
796
                )
797
            })
798
        }
799

800
        // rename primary key constraint
801
        if (newTable.primaryColumns.length > 0) {
10✔
802
            const columnNames = newTable.primaryColumns.map(
10✔
803
                (column) => column.name,
10✔
804
            )
805
            const columnNamesString = columnNames
10✔
806
                .map((columnName) => `"${columnName}"`)
10✔
807
                .join(", ")
808

809
            const oldPkName = this.connection.namingStrategy.primaryKeyName(
10✔
810
                oldTable,
811
                columnNames,
812
            )
813
            const newPkName = this.connection.namingStrategy.primaryKeyName(
10✔
814
                newTable,
815
                columnNames,
816
            )
817

818
            // drop old PK
819
            upQueries.push(
10✔
820
                new Query(
821
                    `ALTER TABLE ${this.escapePath(
822
                        newTable,
823
                    )} DROP CONSTRAINT "${oldPkName}"`,
824
                ),
825
            )
826
            downQueries.push(
10✔
827
                new Query(
828
                    `ALTER TABLE ${this.escapePath(
829
                        newTable,
830
                    )} ADD CONSTRAINT "${oldPkName}" PRIMARY KEY (${columnNamesString})`,
831
                ),
832
            )
833

834
            // create new PK
835
            upQueries.push(
10✔
836
                new Query(
837
                    `ALTER TABLE ${this.escapePath(
838
                        newTable,
839
                    )} ADD CONSTRAINT "${newPkName}" PRIMARY KEY (${columnNamesString})`,
840
                ),
841
            )
842
            downQueries.push(
10✔
843
                new Query(
844
                    `ALTER TABLE ${this.escapePath(
845
                        newTable,
846
                    )} DROP CONSTRAINT "${newPkName}"`,
847
                ),
848
            )
849
        }
850

851
        // recreate foreign keys with new constraint names
852
        newTable.foreignKeys.forEach((foreignKey) => {
10✔
853
            // replace constraint name
854
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
2✔
855
                newTable,
856
                foreignKey.columnNames,
857
                this.getTablePath(foreignKey),
858
                foreignKey.referencedColumnNames,
859
            )
860

861
            // create new FK's
862
            upQueries.push(this.createForeignKeySql(newTable, foreignKey))
2✔
863
            downQueries.push(this.dropForeignKeySql(newTable, foreignKey))
2✔
864
        })
865

866
        // restore referenced foreign keys
867
        referencedForeignKeys.forEach((foreignKey) => {
10✔
868
            const mapping = referencedForeignKeyTableMapping.find(
6✔
869
                (it) => it.fkName === foreignKey.name,
6✔
870
            )
871
            upQueries.push(
6✔
872
                this.createForeignKeySql(mapping!.tableName, foreignKey),
873
            )
874
            downQueries.push(
6✔
875
                this.dropForeignKeySql(mapping!.tableName, foreignKey),
876
            )
877
        })
878

879
        // rename index constraints
880
        newTable.indices.forEach((index) => {
10✔
881
            // build new constraint name
882
            const newIndexName = this.connection.namingStrategy.indexName(
8✔
883
                newTable,
884
                index.columnNames,
885
                index.where,
886
            )
887

888
            // drop old index
889
            upQueries.push(this.dropIndexSql(newTable, index))
8✔
890
            downQueries.push(this.createIndexSql(newTable, index))
8✔
891

892
            // replace constraint name
893
            index.name = newIndexName
8✔
894

895
            // create new index
896
            upQueries.push(this.createIndexSql(newTable, index))
8✔
897
            downQueries.push(this.dropIndexSql(newTable, index))
8✔
898
        })
899

900
        await this.executeQueries(upQueries, downQueries)
10✔
901

902
        // rename old table and replace it in cached tabled;
903
        oldTable.name = newTable.name
10✔
904
        this.replaceCachedTable(oldTable, newTable)
10✔
905
    }
906

907
    /**
908
     * Creates a new column from the column in the table.
909
     */
910
    async addColumn(
911
        tableOrName: Table | string,
912
        column: TableColumn,
913
    ): Promise<void> {
914
        const table = InstanceChecker.isTable(tableOrName)
52✔
915
            ? tableOrName
916
            : await this.getCachedTable(tableOrName)
917
        const parsedTableName = this.driver.parseTableName(table)
52✔
918

919
        if (!parsedTableName.schema) {
52!
920
            parsedTableName.schema = await this.getCurrentSchema()
×
921
        }
922

923
        const clonedTable = table.clone()
52✔
924
        const upQueries: Query[] = []
52✔
925
        const downQueries: Query[] = []
52✔
926

927
        upQueries.push(new Query(this.addColumnSql(table, column)))
52✔
928
        downQueries.push(new Query(this.dropColumnSql(table, column)))
52✔
929

930
        // create or update primary key constraint
931
        if (column.isPrimary) {
52✔
932
            const primaryColumns = clonedTable.primaryColumns
14✔
933
            // if table already have primary key, me must drop it and recreate again
934
            if (primaryColumns.length > 0) {
14✔
935
                // SAP HANA does not allow to drop PK's which is referenced by foreign keys.
936
                // To avoid this, we must drop all referential foreign keys and recreate them later
937
                const referencedForeignKeySql = `SELECT * FROM "SYS"."REFERENTIAL_CONSTRAINTS" WHERE "REFERENCED_SCHEMA_NAME" = '${parsedTableName.schema}' AND "REFERENCED_TABLE_NAME" = '${parsedTableName.tableName}'`
4✔
938
                const dbForeignKeys: ObjectLiteral[] = await this.query(
4✔
939
                    referencedForeignKeySql,
940
                )
941
                let referencedForeignKeys: TableForeignKey[] = []
4✔
942
                const referencedForeignKeyTableMapping: {
943
                    tableName: string
944
                    fkName: string
945
                }[] = []
4✔
946
                if (dbForeignKeys.length > 0) {
4✔
947
                    referencedForeignKeys = dbForeignKeys.map(
2✔
948
                        (dbForeignKey) => {
949
                            const foreignKeys = dbForeignKeys.filter(
2✔
950
                                (dbFk) =>
951
                                    dbFk["CONSTRAINT_NAME"] ===
2✔
952
                                    dbForeignKey["CONSTRAINT_NAME"],
953
                            )
954

955
                            referencedForeignKeyTableMapping.push({
2✔
956
                                tableName: `${dbForeignKey["SCHEMA_NAME"]}.${dbForeignKey["TABLE_NAME"]}`,
957
                                fkName: dbForeignKey["CONSTRAINT_NAME"],
958
                            })
959
                            return new TableForeignKey({
2✔
960
                                name: dbForeignKey["CONSTRAINT_NAME"],
961
                                columnNames: foreignKeys.map(
962
                                    (dbFk) => dbFk["COLUMN_NAME"],
2✔
963
                                ),
964
                                referencedDatabase: table.database,
965
                                referencedSchema: table.schema,
966
                                referencedTableName: table.name,
967
                                referencedColumnNames: foreignKeys.map(
968
                                    (dbFk) => dbFk["REFERENCED_COLUMN_NAME"],
2✔
969
                                ),
970
                                onDelete:
971
                                    dbForeignKey["DELETE_RULE"] === "RESTRICT"
2!
972
                                        ? "NO ACTION"
973
                                        : dbForeignKey["DELETE_RULE"],
974
                                onUpdate:
975
                                    dbForeignKey["UPDATE_RULE"] === "RESTRICT"
2!
976
                                        ? "NO ACTION"
977
                                        : dbForeignKey["UPDATE_RULE"],
978
                                deferrable: dbForeignKey["CHECK_TIME"].replace(
979
                                    "_",
980
                                    " ",
981
                                ),
982
                            })
983
                        },
984
                    )
985

986
                    // drop referenced foreign keys
987
                    referencedForeignKeys.forEach((foreignKey) => {
2✔
988
                        const mapping = referencedForeignKeyTableMapping.find(
2✔
989
                            (it) => it.fkName === foreignKey.name,
2✔
990
                        )
991
                        upQueries.push(
2✔
992
                            this.dropForeignKeySql(
993
                                mapping!.tableName,
994
                                foreignKey,
995
                            ),
996
                        )
997
                        downQueries.push(
2✔
998
                            this.createForeignKeySql(
999
                                mapping!.tableName,
1000
                                foreignKey,
1001
                            ),
1002
                        )
1003
                    })
1004
                }
1005

1006
                const pkName = this.connection.namingStrategy.primaryKeyName(
4✔
1007
                    clonedTable,
1008
                    primaryColumns.map((column) => column.name),
4✔
1009
                )
1010
                const columnNames = primaryColumns
4✔
1011
                    .map((column) => `"${column.name}"`)
4✔
1012
                    .join(", ")
1013
                upQueries.push(
4✔
1014
                    new Query(
1015
                        `ALTER TABLE ${this.escapePath(
1016
                            table,
1017
                        )} DROP CONSTRAINT "${pkName}"`,
1018
                    ),
1019
                )
1020
                downQueries.push(
4✔
1021
                    new Query(
1022
                        `ALTER TABLE ${this.escapePath(
1023
                            table,
1024
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1025
                    ),
1026
                )
1027

1028
                // restore referenced foreign keys
1029
                referencedForeignKeys.forEach((foreignKey) => {
4✔
1030
                    const mapping = referencedForeignKeyTableMapping.find(
2✔
1031
                        (it) => it.fkName === foreignKey.name,
2✔
1032
                    )
1033
                    upQueries.push(
2✔
1034
                        this.createForeignKeySql(
1035
                            mapping!.tableName,
1036
                            foreignKey,
1037
                        ),
1038
                    )
1039
                    downQueries.push(
2✔
1040
                        this.dropForeignKeySql(mapping!.tableName, foreignKey),
1041
                    )
1042
                })
1043
            }
1044

1045
            primaryColumns.push(column)
14✔
1046
            const pkName = this.connection.namingStrategy.primaryKeyName(
14✔
1047
                clonedTable,
1048
                primaryColumns.map((column) => column.name),
18✔
1049
            )
1050
            const columnNames = primaryColumns
14✔
1051
                .map((column) => `"${column.name}"`)
18✔
1052
                .join(", ")
1053
            upQueries.push(
14✔
1054
                new Query(
1055
                    `ALTER TABLE ${this.escapePath(
1056
                        table,
1057
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1058
                ),
1059
            )
1060
            downQueries.push(
14✔
1061
                new Query(
1062
                    `ALTER TABLE ${this.escapePath(
1063
                        table,
1064
                    )} DROP CONSTRAINT "${pkName}"`,
1065
                ),
1066
            )
1067
        }
1068

1069
        // create column index
1070
        const columnIndex = clonedTable.indices.find(
52✔
1071
            (index) =>
1072
                index.columnNames.length === 1 &&
50✔
1073
                index.columnNames[0] === column.name,
1074
        )
1075
        if (columnIndex) {
52!
1076
            upQueries.push(this.createIndexSql(table, columnIndex))
×
1077
            downQueries.push(this.dropIndexSql(table, columnIndex))
×
1078
        } else if (column.isUnique) {
52✔
1079
            const uniqueIndex = new TableIndex({
6✔
1080
                name: this.connection.namingStrategy.indexName(table, [
1081
                    column.name,
1082
                ]),
1083
                columnNames: [column.name],
1084
                isUnique: true,
1085
            })
1086
            clonedTable.indices.push(uniqueIndex)
6✔
1087
            clonedTable.uniques.push(
6✔
1088
                new TableUnique({
1089
                    name: uniqueIndex.name,
1090
                    columnNames: uniqueIndex.columnNames,
1091
                }),
1092
            )
1093
            upQueries.push(this.createIndexSql(table, uniqueIndex))
6✔
1094
            downQueries.push(this.dropIndexSql(table, uniqueIndex))
6✔
1095
        }
1096

1097
        await this.executeQueries(upQueries, downQueries)
52✔
1098

1099
        clonedTable.addColumn(column)
50✔
1100
        this.replaceCachedTable(table, clonedTable)
50✔
1101
    }
1102

1103
    /**
1104
     * Creates a new columns from the column in the table.
1105
     */
1106
    async addColumns(
1107
        tableOrName: Table | string,
1108
        columns: TableColumn[],
1109
    ): Promise<void> {
1110
        for (const column of columns) {
8✔
1111
            await this.addColumn(tableOrName, column)
10✔
1112
        }
1113
    }
1114

1115
    /**
1116
     * Renames column in the given table.
1117
     */
1118
    async renameColumn(
1119
        tableOrName: Table | string,
1120
        oldTableColumnOrName: TableColumn | string,
1121
        newTableColumnOrName: TableColumn | string,
1122
    ): Promise<void> {
1123
        const table = InstanceChecker.isTable(tableOrName)
28✔
1124
            ? tableOrName
1125
            : await this.getCachedTable(tableOrName)
1126
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
28✔
1127
            ? oldTableColumnOrName
1128
            : table.columns.find((c) => c.name === oldTableColumnOrName)
14✔
1129
        if (!oldColumn)
28!
1130
            throw new TypeORMError(
×
1131
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
1132
            )
1133

1134
        let newColumn: TableColumn | undefined = undefined
28✔
1135
        if (InstanceChecker.isTableColumn(newTableColumnOrName)) {
28✔
1136
            newColumn = newTableColumnOrName
20✔
1137
        } else {
1138
            newColumn = oldColumn.clone()
8✔
1139
            newColumn.name = newTableColumnOrName
8✔
1140
        }
1141

1142
        await this.changeColumn(table, oldColumn, newColumn)
28✔
1143
    }
1144

1145
    /**
1146
     * Changes a column in the table.
1147
     */
1148
    async changeColumn(
1149
        tableOrName: Table | string,
1150
        oldTableColumnOrName: TableColumn | string,
1151
        newColumn: TableColumn,
1152
    ): Promise<void> {
1153
        const table = InstanceChecker.isTable(tableOrName)
86!
1154
            ? tableOrName
1155
            : await this.getCachedTable(tableOrName)
1156
        let clonedTable = table.clone()
86✔
1157
        const upQueries: Query[] = []
86✔
1158
        const downQueries: Query[] = []
86✔
1159

1160
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
86!
1161
            ? oldTableColumnOrName
1162
            : table.columns.find(
1163
                  (column) => column.name === oldTableColumnOrName,
×
1164
              )
1165
        if (!oldColumn)
86!
1166
            throw new TypeORMError(
×
1167
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
1168
            )
1169

1170
        if (
86✔
1171
            (newColumn.isGenerated !== oldColumn.isGenerated &&
242✔
1172
                newColumn.generationStrategy !== "uuid") ||
1173
            newColumn.type !== oldColumn.type ||
1174
            newColumn.length !== oldColumn.length
1175
        ) {
1176
            // SQL Server does not support changing of IDENTITY column, so we must drop column and recreate it again.
1177
            // Also, we recreate column if column type changed
1178
            await this.dropColumn(table, oldColumn)
36✔
1179
            await this.addColumn(table, newColumn)
36✔
1180

1181
            // update cloned table
1182
            clonedTable = table.clone()
36✔
1183
        } else {
1184
            if (newColumn.name !== oldColumn.name) {
50✔
1185
                // rename column
1186
                upQueries.push(
14✔
1187
                    new Query(
1188
                        `RENAME COLUMN ${this.escapePath(table)}."${
1189
                            oldColumn.name
1190
                        }" TO "${newColumn.name}"`,
1191
                    ),
1192
                )
1193
                downQueries.push(
14✔
1194
                    new Query(
1195
                        `RENAME COLUMN ${this.escapePath(table)}."${
1196
                            newColumn.name
1197
                        }" TO "${oldColumn.name}"`,
1198
                    ),
1199
                )
1200

1201
                if (oldColumn.isPrimary === true) {
14✔
1202
                    const primaryColumns = clonedTable.primaryColumns
2✔
1203

1204
                    // build old primary constraint name
1205
                    const columnNames = primaryColumns.map(
2✔
1206
                        (column) => column.name,
2✔
1207
                    )
1208
                    const oldPkName =
1209
                        this.connection.namingStrategy.primaryKeyName(
2✔
1210
                            clonedTable,
1211
                            columnNames,
1212
                        )
1213

1214
                    // replace old column name with new column name
1215
                    columnNames.splice(columnNames.indexOf(oldColumn.name), 1)
2✔
1216
                    columnNames.push(newColumn.name)
2✔
1217
                    const columnNamesString = columnNames
2✔
1218
                        .map((columnName) => `"${columnName}"`)
2✔
1219
                        .join(", ")
1220

1221
                    // drop old PK
1222
                    upQueries.push(
2✔
1223
                        new Query(
1224
                            `ALTER TABLE ${this.escapePath(
1225
                                clonedTable,
1226
                            )} DROP CONSTRAINT "${oldPkName}"`,
1227
                        ),
1228
                    )
1229
                    downQueries.push(
2✔
1230
                        new Query(
1231
                            `ALTER TABLE ${this.escapePath(
1232
                                clonedTable,
1233
                            )} ADD CONSTRAINT "${oldPkName}" PRIMARY KEY (${columnNamesString})`,
1234
                        ),
1235
                    )
1236

1237
                    // build new primary constraint name
1238
                    const newPkName =
1239
                        this.connection.namingStrategy.primaryKeyName(
2✔
1240
                            clonedTable,
1241
                            columnNames,
1242
                        )
1243

1244
                    // create new PK
1245
                    upQueries.push(
2✔
1246
                        new Query(
1247
                            `ALTER TABLE ${this.escapePath(
1248
                                clonedTable,
1249
                            )} ADD CONSTRAINT "${newPkName}" PRIMARY KEY (${columnNamesString})`,
1250
                        ),
1251
                    )
1252
                    downQueries.push(
2✔
1253
                        new Query(
1254
                            `ALTER TABLE ${this.escapePath(
1255
                                clonedTable,
1256
                            )} DROP CONSTRAINT "${newPkName}"`,
1257
                        ),
1258
                    )
1259
                }
1260

1261
                // rename index constraints
1262
                clonedTable.findColumnIndices(oldColumn).forEach((index) => {
14✔
1263
                    // build new constraint name
1264
                    index.columnNames.splice(
8✔
1265
                        index.columnNames.indexOf(oldColumn.name),
1266
                        1,
1267
                    )
1268
                    index.columnNames.push(newColumn.name)
8✔
1269
                    const newIndexName =
1270
                        this.connection.namingStrategy.indexName(
8✔
1271
                            clonedTable,
1272
                            index.columnNames,
1273
                            index.where,
1274
                        )
1275

1276
                    // drop old index
1277
                    upQueries.push(this.dropIndexSql(clonedTable, index))
8✔
1278
                    downQueries.push(this.createIndexSql(clonedTable, index))
8✔
1279

1280
                    // replace constraint name
1281
                    index.name = newIndexName
8✔
1282

1283
                    // create new index
1284
                    upQueries.push(this.createIndexSql(clonedTable, index))
8✔
1285
                    downQueries.push(this.dropIndexSql(clonedTable, index))
8✔
1286
                })
1287

1288
                // rename foreign key constraints
1289
                clonedTable
14✔
1290
                    .findColumnForeignKeys(oldColumn)
1291
                    .forEach((foreignKey) => {
1292
                        // build new constraint name
1293
                        foreignKey.columnNames.splice(
2✔
1294
                            foreignKey.columnNames.indexOf(oldColumn.name),
1295
                            1,
1296
                        )
1297
                        foreignKey.columnNames.push(newColumn.name)
2✔
1298
                        const newForeignKeyName =
1299
                            this.connection.namingStrategy.foreignKeyName(
2✔
1300
                                clonedTable,
1301
                                foreignKey.columnNames,
1302
                                this.getTablePath(foreignKey),
1303
                                foreignKey.referencedColumnNames,
1304
                            )
1305

1306
                        upQueries.push(
2✔
1307
                            this.dropForeignKeySql(clonedTable, foreignKey),
1308
                        )
1309
                        downQueries.push(
2✔
1310
                            this.createForeignKeySql(clonedTable, foreignKey),
1311
                        )
1312

1313
                        // replace constraint name
1314
                        foreignKey.name = newForeignKeyName
2✔
1315

1316
                        // create new FK's
1317
                        upQueries.push(
2✔
1318
                            this.createForeignKeySql(clonedTable, foreignKey),
1319
                        )
1320
                        downQueries.push(
2✔
1321
                            this.dropForeignKeySql(clonedTable, foreignKey),
1322
                        )
1323
                    })
1324

1325
                // rename check constraints
1326
                clonedTable.findColumnChecks(oldColumn).forEach((check) => {
14✔
1327
                    // build new constraint name
1328
                    check.columnNames!.splice(
×
1329
                        check.columnNames!.indexOf(oldColumn.name),
1330
                        1,
1331
                    )
1332
                    check.columnNames!.push(newColumn.name)
×
1333
                    const newCheckName =
1334
                        this.connection.namingStrategy.checkConstraintName(
×
1335
                            clonedTable,
1336
                            check.expression!,
1337
                        )
1338

1339
                    upQueries.push(
×
1340
                        this.dropCheckConstraintSql(clonedTable, check),
1341
                    )
1342
                    downQueries.push(
×
1343
                        this.createCheckConstraintSql(clonedTable, check),
1344
                    )
1345

1346
                    // replace constraint name
1347
                    check.name = newCheckName
×
1348

1349
                    upQueries.push(
×
1350
                        this.createCheckConstraintSql(clonedTable, check),
1351
                    )
1352
                    downQueries.push(
×
1353
                        this.dropCheckConstraintSql(clonedTable, check),
1354
                    )
1355
                })
1356

1357
                // rename old column in the Table object
1358
                const oldTableColumn = clonedTable.columns.find(
14✔
1359
                    (column) => column.name === oldColumn.name,
36✔
1360
                )
1361
                clonedTable.columns[
14✔
1362
                    clonedTable.columns.indexOf(oldTableColumn!)
1363
                ].name = newColumn.name
1364
                oldColumn.name = newColumn.name
14✔
1365
            }
1366

1367
            if (this.isColumnChanged(oldColumn, newColumn, true)) {
50✔
1368
                upQueries.push(
8✔
1369
                    new Query(
1370
                        `ALTER TABLE ${this.escapePath(
1371
                            table,
1372
                        )} ALTER (${this.buildCreateColumnSql(
1373
                            newColumn,
1374
                            !(
1375
                                oldColumn.default === null ||
16✔
1376
                                oldColumn.default === undefined
1377
                            ),
1378
                            !oldColumn.isNullable,
1379
                        )})`,
1380
                    ),
1381
                )
1382
                downQueries.push(
8✔
1383
                    new Query(
1384
                        `ALTER TABLE ${this.escapePath(
1385
                            table,
1386
                        )} ALTER (${this.buildCreateColumnSql(
1387
                            oldColumn,
1388
                            !(
1389
                                newColumn.default === null ||
16✔
1390
                                newColumn.default === undefined
1391
                            ),
1392
                            !newColumn.isNullable,
1393
                        )})`,
1394
                    ),
1395
                )
1396
            } else if (oldColumn.comment !== newColumn.comment) {
42✔
1397
                upQueries.push(
6✔
1398
                    new Query(
1399
                        `COMMENT ON COLUMN ${this.escapePath(table)}."${
1400
                            oldColumn.name
1401
                        }" IS ${this.escapeComment(newColumn.comment)}`,
1402
                    ),
1403
                )
1404
                downQueries.push(
6✔
1405
                    new Query(
1406
                        `COMMENT ON COLUMN ${this.escapePath(table)}."${
1407
                            newColumn.name
1408
                        }" IS ${this.escapeComment(oldColumn.comment)}`,
1409
                    ),
1410
                )
1411
            }
1412

1413
            if (newColumn.isPrimary !== oldColumn.isPrimary) {
50✔
1414
                const primaryColumns = clonedTable.primaryColumns
6✔
1415

1416
                // if primary column state changed, we must always drop existed constraint.
1417
                if (primaryColumns.length > 0) {
6✔
1418
                    const pkName =
1419
                        this.connection.namingStrategy.primaryKeyName(
6✔
1420
                            clonedTable,
1421
                            primaryColumns.map((column) => column.name),
8✔
1422
                        )
1423
                    const columnNames = primaryColumns
6✔
1424
                        .map((column) => `"${column.name}"`)
8✔
1425
                        .join(", ")
1426
                    upQueries.push(
6✔
1427
                        new Query(
1428
                            `ALTER TABLE ${this.escapePath(
1429
                                table,
1430
                            )} DROP CONSTRAINT "${pkName}"`,
1431
                        ),
1432
                    )
1433
                    downQueries.push(
6✔
1434
                        new Query(
1435
                            `ALTER TABLE ${this.escapePath(
1436
                                table,
1437
                            )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1438
                        ),
1439
                    )
1440
                }
1441

1442
                if (newColumn.isPrimary === true) {
6✔
1443
                    primaryColumns.push(newColumn)
2✔
1444
                    // update column in table
1445
                    const column = clonedTable.columns.find(
2✔
1446
                        (column) => column.name === newColumn.name,
6✔
1447
                    )
1448
                    column!.isPrimary = true
2✔
1449
                    const pkName =
1450
                        this.connection.namingStrategy.primaryKeyName(
2✔
1451
                            clonedTable,
1452
                            primaryColumns.map((column) => column.name),
4✔
1453
                        )
1454
                    const columnNames = primaryColumns
2✔
1455
                        .map((column) => `"${column.name}"`)
4✔
1456
                        .join(", ")
1457
                    upQueries.push(
2✔
1458
                        new Query(
1459
                            `ALTER TABLE ${this.escapePath(
1460
                                table,
1461
                            )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1462
                        ),
1463
                    )
1464
                    downQueries.push(
2✔
1465
                        new Query(
1466
                            `ALTER TABLE ${this.escapePath(
1467
                                table,
1468
                            )} DROP CONSTRAINT "${pkName}"`,
1469
                        ),
1470
                    )
1471
                } else {
1472
                    const primaryColumn = primaryColumns.find(
4✔
1473
                        (c) => c.name === newColumn.name,
4✔
1474
                    )
1475
                    primaryColumns.splice(
4✔
1476
                        primaryColumns.indexOf(primaryColumn!),
1477
                        1,
1478
                    )
1479

1480
                    // update column in table
1481
                    const column = clonedTable.columns.find(
4✔
1482
                        (column) => column.name === newColumn.name,
12✔
1483
                    )
1484
                    column!.isPrimary = false
4✔
1485

1486
                    // if we have another primary keys, we must recreate constraint.
1487
                    if (primaryColumns.length > 0) {
4✔
1488
                        const pkName =
1489
                            this.connection.namingStrategy.primaryKeyName(
2✔
1490
                                clonedTable,
1491
                                primaryColumns.map((column) => column.name),
2✔
1492
                            )
1493
                        const columnNames = primaryColumns
2✔
1494
                            .map((column) => `"${column.name}"`)
2✔
1495
                            .join(", ")
1496
                        upQueries.push(
2✔
1497
                            new Query(
1498
                                `ALTER TABLE ${this.escapePath(
1499
                                    table,
1500
                                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1501
                            ),
1502
                        )
1503
                        downQueries.push(
2✔
1504
                            new Query(
1505
                                `ALTER TABLE ${this.escapePath(
1506
                                    table,
1507
                                )} DROP CONSTRAINT "${pkName}"`,
1508
                            ),
1509
                        )
1510
                    }
1511
                }
1512
            }
1513

1514
            if (newColumn.isUnique !== oldColumn.isUnique) {
50✔
1515
                if (newColumn.isUnique === true) {
2!
1516
                    const uniqueIndex = new TableIndex({
2✔
1517
                        name: this.connection.namingStrategy.indexName(table, [
1518
                            newColumn.name,
1519
                        ]),
1520
                        columnNames: [newColumn.name],
1521
                        isUnique: true,
1522
                    })
1523
                    clonedTable.indices.push(uniqueIndex)
2✔
1524
                    clonedTable.uniques.push(
2✔
1525
                        new TableUnique({
1526
                            name: uniqueIndex.name,
1527
                            columnNames: uniqueIndex.columnNames,
1528
                        }),
1529
                    )
1530
                    upQueries.push(this.createIndexSql(table, uniqueIndex))
2✔
1531
                    downQueries.push(this.dropIndexSql(table, uniqueIndex))
2✔
1532
                } else {
1533
                    const uniqueIndex = clonedTable.indices.find((index) => {
×
1534
                        return (
×
1535
                            index.columnNames.length === 1 &&
×
1536
                            index.isUnique === true &&
1537
                            !!index.columnNames.find(
1538
                                (columnName) => columnName === newColumn.name,
×
1539
                            )
1540
                        )
1541
                    })
1542
                    clonedTable.indices.splice(
×
1543
                        clonedTable.indices.indexOf(uniqueIndex!),
1544
                        1,
1545
                    )
1546

1547
                    const tableUnique = clonedTable.uniques.find(
×
1548
                        (unique) => unique.name === uniqueIndex!.name,
×
1549
                    )
1550
                    clonedTable.uniques.splice(
×
1551
                        clonedTable.uniques.indexOf(tableUnique!),
1552
                        1,
1553
                    )
1554

1555
                    upQueries.push(this.dropIndexSql(table, uniqueIndex!))
×
1556
                    downQueries.push(this.createIndexSql(table, uniqueIndex!))
×
1557
                }
1558
            }
1559

1560
            await this.executeQueries(upQueries, downQueries)
50✔
1561
            this.replaceCachedTable(table, clonedTable)
50✔
1562
        }
1563
    }
1564

1565
    /**
1566
     * Changes a column in the table.
1567
     */
1568
    async changeColumns(
1569
        tableOrName: Table | string,
1570
        changedColumns: { newColumn: TableColumn; oldColumn: TableColumn }[],
1571
    ): Promise<void> {
1572
        for (const { oldColumn, newColumn } of changedColumns) {
36✔
1573
            await this.changeColumn(tableOrName, oldColumn, newColumn)
46✔
1574
        }
1575
    }
1576

1577
    /**
1578
     * Drops column in the table.
1579
     */
1580
    async dropColumn(
1581
        tableOrName: Table | string,
1582
        columnOrName: TableColumn | string,
1583
    ): Promise<void> {
1584
        const table = InstanceChecker.isTable(tableOrName)
60✔
1585
            ? tableOrName
1586
            : await this.getCachedTable(tableOrName)
1587
        const parsedTableName = this.driver.parseTableName(table)
60✔
1588

1589
        if (!parsedTableName.schema) {
60!
1590
            parsedTableName.schema = await this.getCurrentSchema()
×
1591
        }
1592

1593
        const column = InstanceChecker.isTableColumn(columnOrName)
60✔
1594
            ? columnOrName
1595
            : table.findColumnByName(columnOrName)
1596
        if (!column)
60✔
1597
            throw new TypeORMError(
2✔
1598
                `Column "${columnOrName}" was not found in table "${table.name}"`,
1599
            )
1600

1601
        const clonedTable = table.clone()
58✔
1602
        const upQueries: Query[] = []
58✔
1603
        const downQueries: Query[] = []
58✔
1604

1605
        // drop primary key constraint
1606
        if (column.isPrimary) {
58✔
1607
            // SAP HANA does not allow to drop PK's which is referenced by foreign keys.
1608
            // To avoid this, we must drop all referential foreign keys and recreate them later
1609
            const referencedForeignKeySql = `SELECT * FROM "SYS"."REFERENTIAL_CONSTRAINTS" WHERE "REFERENCED_SCHEMA_NAME" = '${parsedTableName.schema}' AND "REFERENCED_TABLE_NAME" = '${parsedTableName.tableName}'`
14✔
1610
            const dbForeignKeys: ObjectLiteral[] = await this.query(
14✔
1611
                referencedForeignKeySql,
1612
            )
1613
            let referencedForeignKeys: TableForeignKey[] = []
14✔
1614
            const referencedForeignKeyTableMapping: {
1615
                tableName: string
1616
                fkName: string
1617
            }[] = []
14✔
1618
            if (dbForeignKeys.length > 0) {
14✔
1619
                referencedForeignKeys = dbForeignKeys.map((dbForeignKey) => {
4✔
1620
                    const foreignKeys = dbForeignKeys.filter(
4✔
1621
                        (dbFk) =>
1622
                            dbFk["CONSTRAINT_NAME"] ===
4✔
1623
                            dbForeignKey["CONSTRAINT_NAME"],
1624
                    )
1625

1626
                    referencedForeignKeyTableMapping.push({
4✔
1627
                        tableName: `${dbForeignKey["SCHEMA_NAME"]}.${dbForeignKey["TABLE_NAME"]}`,
1628
                        fkName: dbForeignKey["CONSTRAINT_NAME"],
1629
                    })
1630
                    return new TableForeignKey({
4✔
1631
                        name: dbForeignKey["CONSTRAINT_NAME"],
1632
                        columnNames: foreignKeys.map(
1633
                            (dbFk) => dbFk["COLUMN_NAME"],
4✔
1634
                        ),
1635
                        referencedDatabase: table.database,
1636
                        referencedSchema: table.schema,
1637
                        referencedTableName: table.name,
1638
                        referencedColumnNames: foreignKeys.map(
1639
                            (dbFk) => dbFk["REFERENCED_COLUMN_NAME"],
4✔
1640
                        ),
1641
                        onDelete:
1642
                            dbForeignKey["DELETE_RULE"] === "RESTRICT"
4!
1643
                                ? "NO ACTION"
1644
                                : dbForeignKey["DELETE_RULE"],
1645
                        onUpdate:
1646
                            dbForeignKey["UPDATE_RULE"] === "RESTRICT"
4!
1647
                                ? "NO ACTION"
1648
                                : dbForeignKey["UPDATE_RULE"],
1649
                        deferrable: dbForeignKey["CHECK_TIME"].replace(
1650
                            "_",
1651
                            " ",
1652
                        ),
1653
                    })
1654
                })
1655

1656
                // drop referenced foreign keys
1657
                referencedForeignKeys.forEach((foreignKey) => {
4✔
1658
                    const mapping = referencedForeignKeyTableMapping.find(
4✔
1659
                        (it) => it.fkName === foreignKey.name,
4✔
1660
                    )
1661
                    upQueries.push(
4✔
1662
                        this.dropForeignKeySql(mapping!.tableName, foreignKey),
1663
                    )
1664
                    downQueries.push(
4✔
1665
                        this.createForeignKeySql(
1666
                            mapping!.tableName,
1667
                            foreignKey,
1668
                        ),
1669
                    )
1670
                })
1671
            }
1672

1673
            const pkName = this.connection.namingStrategy.primaryKeyName(
14✔
1674
                clonedTable,
1675
                clonedTable.primaryColumns.map((column) => column.name),
16✔
1676
            )
1677
            const columnNames = clonedTable.primaryColumns
14✔
1678
                .map((primaryColumn) => `"${primaryColumn.name}"`)
16✔
1679
                .join(", ")
1680
            upQueries.push(
14✔
1681
                new Query(
1682
                    `ALTER TABLE ${this.escapePath(
1683
                        clonedTable,
1684
                    )} DROP CONSTRAINT "${pkName}"`,
1685
                ),
1686
            )
1687
            downQueries.push(
14✔
1688
                new Query(
1689
                    `ALTER TABLE ${this.escapePath(
1690
                        clonedTable,
1691
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1692
                ),
1693
            )
1694

1695
            // update column in table
1696
            const tableColumn = clonedTable.findColumnByName(column.name)
14✔
1697
            tableColumn!.isPrimary = false
14✔
1698

1699
            // if primary key have multiple columns, we must recreate it without dropped column
1700
            if (clonedTable.primaryColumns.length > 0) {
14✔
1701
                const pkName = this.connection.namingStrategy.primaryKeyName(
2✔
1702
                    clonedTable,
1703
                    clonedTable.primaryColumns.map((column) => column.name),
2✔
1704
                )
1705
                const columnNames = clonedTable.primaryColumns
2✔
1706
                    .map((primaryColumn) => `"${primaryColumn.name}"`)
2✔
1707
                    .join(", ")
1708
                upQueries.push(
2✔
1709
                    new Query(
1710
                        `ALTER TABLE ${this.escapePath(
1711
                            clonedTable,
1712
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1713
                    ),
1714
                )
1715
                downQueries.push(
2✔
1716
                    new Query(
1717
                        `ALTER TABLE ${this.escapePath(
1718
                            clonedTable,
1719
                        )} DROP CONSTRAINT "${pkName}"`,
1720
                    ),
1721
                )
1722
            }
1723

1724
            // restore referenced foreign keys
1725
            referencedForeignKeys.forEach((foreignKey) => {
14✔
1726
                const mapping = referencedForeignKeyTableMapping.find(
4✔
1727
                    (it) => it.fkName === foreignKey.name,
4✔
1728
                )
1729
                upQueries.push(
4✔
1730
                    this.createForeignKeySql(mapping!.tableName, foreignKey),
1731
                )
1732
                downQueries.push(
4✔
1733
                    this.dropForeignKeySql(mapping!.tableName, foreignKey),
1734
                )
1735
            })
1736
        }
1737

1738
        // drop column index
1739
        const columnIndex = clonedTable.indices.find(
58✔
1740
            (index) =>
1741
                index.columnNames.length === 1 &&
55✔
1742
                index.columnNames[0] === column.name,
1743
        )
1744
        if (columnIndex) {
58✔
1745
            clonedTable.indices.splice(
6✔
1746
                clonedTable.indices.indexOf(columnIndex),
1747
                1,
1748
            )
1749
            upQueries.push(this.dropIndexSql(table, columnIndex))
6✔
1750
            downQueries.push(this.createIndexSql(table, columnIndex))
6✔
1751
        } else if (column.isUnique) {
52✔
1752
            // we splice constraints both from table uniques and indices.
1753
            const uniqueName =
1754
                this.connection.namingStrategy.uniqueConstraintName(table, [
2✔
1755
                    column.name,
1756
                ])
1757
            const foundUnique = clonedTable.uniques.find(
2✔
1758
                (unique) => unique.name === uniqueName,
×
1759
            )
1760
            if (foundUnique) {
2!
1761
                clonedTable.uniques.splice(
×
1762
                    clonedTable.uniques.indexOf(foundUnique),
1763
                    1,
1764
                )
1765
                upQueries.push(this.dropIndexSql(table, uniqueName))
×
1766
                downQueries.push(
×
1767
                    new Query(
1768
                        `CREATE UNIQUE INDEX "${uniqueName}" ON ${this.escapePath(
1769
                            table,
1770
                        )} ("${column.name}")`,
1771
                    ),
1772
                )
1773
            }
1774

1775
            const indexName = this.connection.namingStrategy.indexName(table, [
2✔
1776
                column.name,
1777
            ])
1778
            const foundIndex = clonedTable.indices.find(
2✔
1779
                (index) => index.name === indexName,
×
1780
            )
1781
            if (foundIndex) {
2!
1782
                clonedTable.indices.splice(
×
1783
                    clonedTable.indices.indexOf(foundIndex),
1784
                    1,
1785
                )
1786
                upQueries.push(this.dropIndexSql(table, indexName))
×
1787
                downQueries.push(
×
1788
                    new Query(
1789
                        `CREATE UNIQUE INDEX "${indexName}" ON ${this.escapePath(
1790
                            table,
1791
                        )} ("${column.name}")`,
1792
                    ),
1793
                )
1794
            }
1795
        }
1796

1797
        // drop column check
1798
        const columnCheck = clonedTable.checks.find(
58✔
1799
            (check) =>
1800
                !!check.columnNames &&
24✔
1801
                check.columnNames.length === 1 &&
1802
                check.columnNames[0] === column.name,
1803
        )
1804
        if (columnCheck) {
58!
1805
            clonedTable.checks.splice(
×
1806
                clonedTable.checks.indexOf(columnCheck),
1807
                1,
1808
            )
1809
            upQueries.push(this.dropCheckConstraintSql(table, columnCheck))
×
1810
            downQueries.push(this.createCheckConstraintSql(table, columnCheck))
×
1811
        }
1812

1813
        upQueries.push(new Query(this.dropColumnSql(table, column)))
58✔
1814
        downQueries.push(new Query(this.addColumnSql(table, column)))
58✔
1815

1816
        await this.executeQueries(upQueries, downQueries)
58✔
1817

1818
        clonedTable.removeColumn(column)
58✔
1819
        this.replaceCachedTable(table, clonedTable)
58✔
1820
    }
1821

1822
    /**
1823
     * Drops the columns in the table.
1824
     */
1825
    async dropColumns(
1826
        tableOrName: Table | string,
1827
        columns: TableColumn[] | string[],
1828
    ): Promise<void> {
1829
        for (const column of columns) {
8✔
1830
            await this.dropColumn(tableOrName, column)
18✔
1831
        }
1832
    }
1833

1834
    /**
1835
     * Creates a new primary key.
1836
     */
1837
    async createPrimaryKey(
1838
        tableOrName: Table | string,
1839
        columnNames: string[],
1840
    ): Promise<void> {
1841
        const table = InstanceChecker.isTable(tableOrName)
4!
1842
            ? tableOrName
1843
            : await this.getCachedTable(tableOrName)
1844
        const clonedTable = table.clone()
4✔
1845

1846
        const up = this.createPrimaryKeySql(table, columnNames)
4✔
1847

1848
        // mark columns as primary, because dropPrimaryKeySql build constraint name from table primary column names.
1849
        clonedTable.columns.forEach((column) => {
4✔
1850
            if (columnNames.find((columnName) => columnName === column.name))
14✔
1851
                column.isPrimary = true
6✔
1852
        })
1853
        const down = this.dropPrimaryKeySql(clonedTable)
4✔
1854

1855
        await this.executeQueries(up, down)
4✔
1856
        this.replaceCachedTable(table, clonedTable)
4✔
1857
    }
1858

1859
    /**
1860
     * Updates composite primary keys.
1861
     */
1862
    async updatePrimaryKeys(
1863
        tableOrName: Table | string,
1864
        columns: TableColumn[],
1865
    ): Promise<void> {
1866
        const table = InstanceChecker.isTable(tableOrName)
6!
1867
            ? tableOrName
1868
            : await this.getCachedTable(tableOrName)
1869
        const parsedTableName = this.driver.parseTableName(table)
6✔
1870

1871
        if (!parsedTableName.schema) {
6!
1872
            parsedTableName.schema = await this.getCurrentSchema()
×
1873
        }
1874

1875
        const clonedTable = table.clone()
6✔
1876
        const columnNames = columns.map((column) => column.name)
12✔
1877
        const upQueries: Query[] = []
6✔
1878
        const downQueries: Query[] = []
6✔
1879

1880
        // SAP HANA does not allow to drop PK's which is referenced by foreign keys.
1881
        // To avoid this, we must drop all referential foreign keys and recreate them later
1882
        const referencedForeignKeySql = `SELECT * FROM "SYS"."REFERENTIAL_CONSTRAINTS" WHERE "REFERENCED_SCHEMA_NAME" = '${parsedTableName.schema}' AND "REFERENCED_TABLE_NAME" = '${parsedTableName.tableName}'`
6✔
1883
        const dbForeignKeys: ObjectLiteral[] = await this.query(
6✔
1884
            referencedForeignKeySql,
1885
        )
1886
        let referencedForeignKeys: TableForeignKey[] = []
6✔
1887
        const referencedForeignKeyTableMapping: {
1888
            tableName: string
1889
            fkName: string
1890
        }[] = []
6✔
1891
        if (dbForeignKeys.length > 0) {
6✔
1892
            referencedForeignKeys = dbForeignKeys.map((dbForeignKey) => {
2✔
1893
                const foreignKeys = dbForeignKeys.filter(
2✔
1894
                    (dbFk) =>
1895
                        dbFk["CONSTRAINT_NAME"] ===
2✔
1896
                        dbForeignKey["CONSTRAINT_NAME"],
1897
                )
1898

1899
                referencedForeignKeyTableMapping.push({
2✔
1900
                    tableName: `${dbForeignKey["SCHEMA_NAME"]}.${dbForeignKey["TABLE_NAME"]}`,
1901
                    fkName: dbForeignKey["CONSTRAINT_NAME"],
1902
                })
1903
                return new TableForeignKey({
2✔
1904
                    name: dbForeignKey["CONSTRAINT_NAME"],
1905
                    columnNames: foreignKeys.map((dbFk) => dbFk["COLUMN_NAME"]),
2✔
1906
                    referencedDatabase: table.database,
1907
                    referencedSchema: table.schema,
1908
                    referencedTableName: table.name,
1909
                    referencedColumnNames: foreignKeys.map(
1910
                        (dbFk) => dbFk["REFERENCED_COLUMN_NAME"],
2✔
1911
                    ),
1912
                    onDelete:
1913
                        dbForeignKey["DELETE_RULE"] === "RESTRICT"
2!
1914
                            ? "NO ACTION"
1915
                            : dbForeignKey["DELETE_RULE"],
1916
                    onUpdate:
1917
                        dbForeignKey["UPDATE_RULE"] === "RESTRICT"
2!
1918
                            ? "NO ACTION"
1919
                            : dbForeignKey["UPDATE_RULE"],
1920
                    deferrable: dbForeignKey["CHECK_TIME"].replace("_", " "),
1921
                })
1922
            })
1923

1924
            // drop referenced foreign keys
1925
            referencedForeignKeys.forEach((foreignKey) => {
2✔
1926
                const mapping = referencedForeignKeyTableMapping.find(
2✔
1927
                    (it) => it.fkName === foreignKey.name,
2✔
1928
                )
1929
                upQueries.push(
2✔
1930
                    this.dropForeignKeySql(mapping!.tableName, foreignKey),
1931
                )
1932
                downQueries.push(
2✔
1933
                    this.createForeignKeySql(mapping!.tableName, foreignKey),
1934
                )
1935
            })
1936
        }
1937

1938
        // if table already have primary columns, we must drop them.
1939
        const primaryColumns = clonedTable.primaryColumns
6✔
1940
        if (primaryColumns.length > 0) {
6✔
1941
            const pkName = this.connection.namingStrategy.primaryKeyName(
6✔
1942
                clonedTable,
1943
                primaryColumns.map((column) => column.name),
6✔
1944
            )
1945
            const columnNamesString = primaryColumns
6✔
1946
                .map((column) => `"${column.name}"`)
6✔
1947
                .join(", ")
1948
            upQueries.push(
6✔
1949
                new Query(
1950
                    `ALTER TABLE ${this.escapePath(
1951
                        table,
1952
                    )} DROP CONSTRAINT "${pkName}"`,
1953
                ),
1954
            )
1955
            downQueries.push(
6✔
1956
                new Query(
1957
                    `ALTER TABLE ${this.escapePath(
1958
                        table,
1959
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
1960
                ),
1961
            )
1962
        }
1963

1964
        // update columns in table.
1965
        clonedTable.columns
6✔
1966
            .filter((column) => columnNames.indexOf(column.name) !== -1)
24✔
1967
            .forEach((column) => (column.isPrimary = true))
12✔
1968

1969
        const pkName = this.connection.namingStrategy.primaryKeyName(
6✔
1970
            clonedTable,
1971
            columnNames,
1972
        )
1973
        const columnNamesString = columnNames
6✔
1974
            .map((columnName) => `"${columnName}"`)
12✔
1975
            .join(", ")
1976
        upQueries.push(
6✔
1977
            new Query(
1978
                `ALTER TABLE ${this.escapePath(
1979
                    table,
1980
                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
1981
            ),
1982
        )
1983
        downQueries.push(
6✔
1984
            new Query(
1985
                `ALTER TABLE ${this.escapePath(
1986
                    table,
1987
                )} DROP CONSTRAINT "${pkName}"`,
1988
            ),
1989
        )
1990

1991
        // restore referenced foreign keys
1992
        referencedForeignKeys.forEach((foreignKey) => {
6✔
1993
            const mapping = referencedForeignKeyTableMapping.find(
2✔
1994
                (it) => it.fkName === foreignKey.name,
2✔
1995
            )
1996
            upQueries.push(
2✔
1997
                this.createForeignKeySql(mapping!.tableName, foreignKey),
1998
            )
1999
            downQueries.push(
2✔
2000
                this.dropForeignKeySql(mapping!.tableName, foreignKey),
2001
            )
2002
        })
2003

2004
        await this.executeQueries(upQueries, downQueries)
6✔
2005
        this.replaceCachedTable(table, clonedTable)
6✔
2006
    }
2007

2008
    /**
2009
     * Drops a primary key.
2010
     */
2011
    async dropPrimaryKey(tableOrName: Table | string): Promise<void> {
2012
        const table = InstanceChecker.isTable(tableOrName)
6!
2013
            ? tableOrName
2014
            : await this.getCachedTable(tableOrName)
2015
        const parsedTableName = this.driver.parseTableName(table)
6✔
2016

2017
        if (!parsedTableName.schema) {
6!
2018
            parsedTableName.schema = await this.getCurrentSchema()
×
2019
        }
2020

2021
        const upQueries: Query[] = []
6✔
2022
        const downQueries: Query[] = []
6✔
2023

2024
        // SAP HANA does not allow to drop PK's which is referenced by foreign keys.
2025
        // To avoid this, we must drop all referential foreign keys and recreate them later
2026
        const referencedForeignKeySql = `SELECT * FROM "SYS"."REFERENTIAL_CONSTRAINTS" WHERE "REFERENCED_SCHEMA_NAME" = '${parsedTableName.schema}' AND "REFERENCED_TABLE_NAME" = '${parsedTableName.tableName}'`
6✔
2027
        const dbForeignKeys: ObjectLiteral[] = await this.query(
6✔
2028
            referencedForeignKeySql,
2029
        )
2030
        let referencedForeignKeys: TableForeignKey[] = []
6✔
2031
        const referencedForeignKeyTableMapping: {
2032
            tableName: string
2033
            fkName: string
2034
        }[] = []
6✔
2035
        if (dbForeignKeys.length > 0) {
6!
2036
            referencedForeignKeys = dbForeignKeys.map((dbForeignKey) => {
×
2037
                const foreignKeys = dbForeignKeys.filter(
×
2038
                    (dbFk) =>
2039
                        dbFk["CONSTRAINT_NAME"] ===
×
2040
                        dbForeignKey["CONSTRAINT_NAME"],
2041
                )
2042

2043
                referencedForeignKeyTableMapping.push({
×
2044
                    tableName: `${dbForeignKey["SCHEMA_NAME"]}.${dbForeignKey["TABLE_NAME"]}`,
2045
                    fkName: dbForeignKey["CONSTRAINT_NAME"],
2046
                })
2047
                return new TableForeignKey({
×
2048
                    name: dbForeignKey["CONSTRAINT_NAME"],
2049
                    columnNames: foreignKeys.map((dbFk) => dbFk["COLUMN_NAME"]),
×
2050
                    referencedDatabase: table.database,
2051
                    referencedSchema: table.schema,
2052
                    referencedTableName: table.name,
2053
                    referencedColumnNames: foreignKeys.map(
2054
                        (dbFk) => dbFk["REFERENCED_COLUMN_NAME"],
×
2055
                    ),
2056
                    onDelete:
2057
                        dbForeignKey["DELETE_RULE"] === "RESTRICT"
×
2058
                            ? "NO ACTION"
2059
                            : dbForeignKey["DELETE_RULE"],
2060
                    onUpdate:
2061
                        dbForeignKey["UPDATE_RULE"] === "RESTRICT"
×
2062
                            ? "NO ACTION"
2063
                            : dbForeignKey["UPDATE_RULE"],
2064
                    deferrable: dbForeignKey["CHECK_TIME"].replace("_", " "),
2065
                })
2066
            })
2067

2068
            // drop referenced foreign keys
2069
            referencedForeignKeys.forEach((foreignKey) => {
×
2070
                const mapping = referencedForeignKeyTableMapping.find(
×
2071
                    (it) => it.fkName === foreignKey.name,
×
2072
                )
2073
                upQueries.push(
×
2074
                    this.dropForeignKeySql(mapping!.tableName, foreignKey),
2075
                )
2076
                downQueries.push(
×
2077
                    this.createForeignKeySql(mapping!.tableName, foreignKey),
2078
                )
2079
            })
2080
        }
2081

2082
        upQueries.push(this.dropPrimaryKeySql(table))
6✔
2083
        downQueries.push(
6✔
2084
            this.createPrimaryKeySql(
2085
                table,
2086
                table.primaryColumns.map((column) => column.name),
6✔
2087
            ),
2088
        )
2089

2090
        // restore referenced foreign keys
2091
        referencedForeignKeys.forEach((foreignKey) => {
6✔
2092
            const mapping = referencedForeignKeyTableMapping.find(
×
2093
                (it) => it.fkName === foreignKey.name,
×
2094
            )
2095
            upQueries.push(
×
2096
                this.createForeignKeySql(mapping!.tableName, foreignKey),
2097
            )
2098
            downQueries.push(
×
2099
                this.dropForeignKeySql(mapping!.tableName, foreignKey),
2100
            )
2101
        })
2102

2103
        await this.executeQueries(upQueries, downQueries)
6✔
2104
        table.primaryColumns.forEach((column) => {
6✔
2105
            column.isPrimary = false
6✔
2106
        })
2107
    }
2108

2109
    /**
2110
     * Creates a new unique constraint.
2111
     */
2112
    async createUniqueConstraint(
2113
        tableOrName: Table | string,
2114
        uniqueConstraint: TableUnique,
2115
    ): Promise<void> {
2116
        throw new TypeORMError(
×
2117
            `SAP HANA does not support unique constraints. Use unique index instead.`,
2118
        )
2119
    }
2120

2121
    /**
2122
     * Creates a new unique constraints.
2123
     */
2124
    async createUniqueConstraints(
2125
        tableOrName: Table | string,
2126
        uniqueConstraints: TableUnique[],
2127
    ): Promise<void> {
2128
        throw new TypeORMError(
×
2129
            `SAP HANA does not support unique constraints. Use unique index instead.`,
2130
        )
2131
    }
2132

2133
    /**
2134
     * Drops unique constraint.
2135
     */
2136
    async dropUniqueConstraint(
2137
        tableOrName: Table | string,
2138
        uniqueOrName: TableUnique | string,
2139
    ): Promise<void> {
2140
        throw new TypeORMError(
×
2141
            `SAP HANA does not support unique constraints. Use unique index instead.`,
2142
        )
2143
    }
2144

2145
    /**
2146
     * Drops an unique constraints.
2147
     */
2148
    async dropUniqueConstraints(
2149
        tableOrName: Table | string,
2150
        uniqueConstraints: TableUnique[],
2151
    ): Promise<void> {
2152
        throw new TypeORMError(
×
2153
            `SAP HANA does not support unique constraints. Use unique index instead.`,
2154
        )
2155
    }
2156

2157
    /**
2158
     * Creates a new check constraint.
2159
     */
2160
    async createCheckConstraint(
2161
        tableOrName: Table | string,
2162
        checkConstraint: TableCheck,
2163
    ): Promise<void> {
2164
        const table = InstanceChecker.isTable(tableOrName)
10✔
2165
            ? tableOrName
2166
            : await this.getCachedTable(tableOrName)
2167

2168
        // new unique constraint may be passed without name. In this case we generate unique name manually.
2169
        if (!checkConstraint.name)
10✔
2170
            checkConstraint.name =
6✔
2171
                this.connection.namingStrategy.checkConstraintName(
2172
                    table,
2173
                    checkConstraint.expression!,
2174
                )
2175

2176
        const up = this.createCheckConstraintSql(table, checkConstraint)
10✔
2177
        const down = this.dropCheckConstraintSql(table, checkConstraint)
10✔
2178
        await this.executeQueries(up, down)
10✔
2179
        table.addCheckConstraint(checkConstraint)
10✔
2180
    }
2181

2182
    /**
2183
     * Creates a new check constraints.
2184
     */
2185
    async createCheckConstraints(
2186
        tableOrName: Table | string,
2187
        checkConstraints: TableCheck[],
2188
    ): Promise<void> {
2189
        const promises = checkConstraints.map((checkConstraint) =>
4✔
2190
            this.createCheckConstraint(tableOrName, checkConstraint),
4✔
2191
        )
2192
        await Promise.all(promises)
4✔
2193
    }
2194

2195
    /**
2196
     * Drops check constraint.
2197
     */
2198
    async dropCheckConstraint(
2199
        tableOrName: Table | string,
2200
        checkOrName: TableCheck | string,
2201
    ): Promise<void> {
2202
        const table = InstanceChecker.isTable(tableOrName)
6!
2203
            ? tableOrName
2204
            : await this.getCachedTable(tableOrName)
2205
        const checkConstraint = InstanceChecker.isTableCheck(checkOrName)
6!
2206
            ? checkOrName
2207
            : table.checks.find((c) => c.name === checkOrName)
×
2208
        if (!checkConstraint)
6!
2209
            throw new TypeORMError(
×
2210
                `Supplied check constraint was not found in table ${table.name}`,
2211
            )
2212

2213
        const up = this.dropCheckConstraintSql(table, checkConstraint)
6✔
2214
        const down = this.createCheckConstraintSql(table, checkConstraint)
6✔
2215
        await this.executeQueries(up, down)
6✔
2216
        table.removeCheckConstraint(checkConstraint)
6✔
2217
    }
2218

2219
    /**
2220
     * Drops check constraints.
2221
     */
2222
    async dropCheckConstraints(
2223
        tableOrName: Table | string,
2224
        checkConstraints: TableCheck[],
2225
    ): Promise<void> {
2226
        const promises = checkConstraints.map((checkConstraint) =>
4✔
2227
            this.dropCheckConstraint(tableOrName, checkConstraint),
4✔
2228
        )
2229
        await Promise.all(promises)
4✔
2230
    }
2231

2232
    /**
2233
     * Creates a new exclusion constraint.
2234
     */
2235
    async createExclusionConstraint(
2236
        tableOrName: Table | string,
2237
        exclusionConstraint: TableExclusion,
2238
    ): Promise<void> {
2239
        throw new TypeORMError(
×
2240
            `SAP HANA does not support exclusion constraints.`,
2241
        )
2242
    }
2243

2244
    /**
2245
     * Creates a new exclusion constraints.
2246
     */
2247
    async createExclusionConstraints(
2248
        tableOrName: Table | string,
2249
        exclusionConstraints: TableExclusion[],
2250
    ): Promise<void> {
2251
        throw new TypeORMError(
×
2252
            `SAP HANA does not support exclusion constraints.`,
2253
        )
2254
    }
2255

2256
    /**
2257
     * Drops exclusion constraint.
2258
     */
2259
    async dropExclusionConstraint(
2260
        tableOrName: Table | string,
2261
        exclusionOrName: TableExclusion | string,
2262
    ): Promise<void> {
2263
        throw new TypeORMError(
×
2264
            `SAP HANA does not support exclusion constraints.`,
2265
        )
2266
    }
2267

2268
    /**
2269
     * Drops exclusion constraints.
2270
     */
2271
    async dropExclusionConstraints(
2272
        tableOrName: Table | string,
2273
        exclusionConstraints: TableExclusion[],
2274
    ): Promise<void> {
2275
        throw new TypeORMError(
×
2276
            `SAP HANA does not support exclusion constraints.`,
2277
        )
2278
    }
2279

2280
    /**
2281
     * Creates a new foreign key.
2282
     */
2283
    async createForeignKey(
2284
        tableOrName: Table | string,
2285
        foreignKey: TableForeignKey,
2286
    ): Promise<void> {
2287
        const table = InstanceChecker.isTable(tableOrName)
5,516✔
2288
            ? tableOrName
2289
            : await this.getCachedTable(tableOrName)
2290

2291
        // new FK may be passed without name. In this case we generate FK name manually.
2292
        if (!foreignKey.name)
5,516✔
2293
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
2✔
2294
                table,
2295
                foreignKey.columnNames,
2296
                this.getTablePath(foreignKey),
2297
                foreignKey.referencedColumnNames,
2298
            )
2299

2300
        const up = this.createForeignKeySql(table, foreignKey)
5,516✔
2301
        const down = this.dropForeignKeySql(table, foreignKey)
5,516✔
2302
        await this.executeQueries(up, down)
5,516✔
2303
        table.addForeignKey(foreignKey)
5,516✔
2304
    }
2305

2306
    /**
2307
     * Creates a new foreign keys.
2308
     */
2309
    async createForeignKeys(
2310
        tableOrName: Table | string,
2311
        foreignKeys: TableForeignKey[],
2312
    ): Promise<void> {
2313
        const promises = foreignKeys.map((foreignKey) =>
3,144✔
2314
            this.createForeignKey(tableOrName, foreignKey),
5,514✔
2315
        )
2316
        await Promise.all(promises)
3,144✔
2317
    }
2318

2319
    /**
2320
     * Drops a foreign key from the table.
2321
     */
2322
    async dropForeignKey(
2323
        tableOrName: Table | string,
2324
        foreignKeyOrName: TableForeignKey | string,
2325
    ): Promise<void> {
2326
        const table = InstanceChecker.isTable(tableOrName)
22!
2327
            ? tableOrName
2328
            : await this.getCachedTable(tableOrName)
2329
        const foreignKey = InstanceChecker.isTableForeignKey(foreignKeyOrName)
22!
2330
            ? foreignKeyOrName
2331
            : table.foreignKeys.find((fk) => fk.name === foreignKeyOrName)
×
2332
        if (!foreignKey)
22!
2333
            throw new TypeORMError(
×
2334
                `Supplied foreign key was not found in table ${table.name}`,
2335
            )
2336

2337
        const up = this.dropForeignKeySql(table, foreignKey)
22✔
2338
        const down = this.createForeignKeySql(table, foreignKey)
22✔
2339
        await this.executeQueries(up, down)
22✔
2340
        table.removeForeignKey(foreignKey)
22✔
2341
    }
2342

2343
    /**
2344
     * Drops a foreign keys from the table.
2345
     */
2346
    async dropForeignKeys(
2347
        tableOrName: Table | string,
2348
        foreignKeys: TableForeignKey[],
2349
    ): Promise<void> {
2350
        const promises = foreignKeys.map((foreignKey) =>
20✔
2351
            this.dropForeignKey(tableOrName, foreignKey),
20✔
2352
        )
2353
        await Promise.all(promises)
20✔
2354
    }
2355

2356
    /**
2357
     * Creates a new index.
2358
     */
2359
    async createIndex(
2360
        tableOrName: Table | string,
2361
        index: TableIndex,
2362
    ): Promise<void> {
2363
        const table = InstanceChecker.isTable(tableOrName)
28✔
2364
            ? tableOrName
2365
            : await this.getCachedTable(tableOrName)
2366

2367
        // new index may be passed without name. In this case we generate index name manually.
2368
        if (!index.name) index.name = this.generateIndexName(table, index)
28✔
2369

2370
        const up = this.createIndexSql(table, index)
28✔
2371
        const down = this.dropIndexSql(table, index)
28✔
2372
        await this.executeQueries(up, down)
28✔
2373
        table.addIndex(index)
28✔
2374
    }
2375

2376
    /**
2377
     * Creates a new indices
2378
     */
2379
    async createIndices(
2380
        tableOrName: Table | string,
2381
        indices: TableIndex[],
2382
    ): Promise<void> {
2383
        const promises = indices.map((index) =>
20✔
2384
            this.createIndex(tableOrName, index),
20✔
2385
        )
2386
        await Promise.all(promises)
20✔
2387
    }
2388

2389
    /**
2390
     * Drops an index.
2391
     */
2392
    async dropIndex(
2393
        tableOrName: Table | string,
2394
        indexOrName: TableIndex | string,
2395
    ): Promise<void> {
2396
        const table = InstanceChecker.isTable(tableOrName)
30✔
2397
            ? tableOrName
2398
            : await this.getCachedTable(tableOrName)
2399
        const index = InstanceChecker.isTableIndex(indexOrName)
30!
2400
            ? indexOrName
2401
            : table.indices.find((i) => i.name === indexOrName)
×
2402
        if (!index)
30!
2403
            throw new TypeORMError(
×
2404
                `Supplied index ${indexOrName} was not found in table ${table.name}`,
2405
            )
2406

2407
        // old index may be passed without name. In this case we generate index name manually.
2408
        if (!index.name) index.name = this.generateIndexName(table, index)
30✔
2409

2410
        const up = this.dropIndexSql(table, index)
30✔
2411
        const down = this.createIndexSql(table, index)
30✔
2412
        await this.executeQueries(up, down)
30✔
2413
        table.removeIndex(index)
30✔
2414
    }
2415

2416
    /**
2417
     * Drops an indices from the table.
2418
     */
2419
    async dropIndices(
2420
        tableOrName: Table | string,
2421
        indices: TableIndex[],
2422
    ): Promise<void> {
2423
        const promises = indices.map((index) =>
6✔
2424
            this.dropIndex(tableOrName, index),
6✔
2425
        )
2426
        await Promise.all(promises)
6✔
2427
    }
2428

2429
    /**
2430
     * Clears all table contents.
2431
     * Note: this operation uses SQL's TRUNCATE query which cannot be reverted in transactions.
2432
     */
2433
    async clearTable(tablePath: string): Promise<void> {
2434
        await this.query(`TRUNCATE TABLE ${this.escapePath(tablePath)}`)
4✔
2435
    }
2436

2437
    /**
2438
     * Removes all tables from the currently connected database.
2439
     */
2440
    async clearDatabase(): Promise<void> {
2441
        const schemas: string[] = []
2,414✔
2442
        this.connection.entityMetadatas
2,414✔
2443
            .filter((metadata) => metadata.schema)
8,734✔
2444
            .forEach((metadata) => {
2445
                const isSchemaExist = !!schemas.find(
4✔
2446
                    (schema) => schema === metadata.schema,
2✔
2447
                )
2448
                if (!isSchemaExist) schemas.push(metadata.schema!)
4✔
2449
            })
2450

2451
        schemas.push(this.driver.options.schema || "current_schema")
2,414✔
2452
        const schemaNamesString = schemas
2,414✔
2453
            .map((name) => {
2454
                return name === "current_schema" ? name : "'" + name + "'"
2,418✔
2455
            })
2456
            .join(", ")
2457

2458
        const isAnotherTransactionActive = this.isTransactionActive
2,414✔
2459
        if (!isAnotherTransactionActive) await this.startTransaction()
2,414✔
2460
        try {
2,414✔
2461
            // const selectViewDropsQuery = `SELECT 'DROP VIEW IF EXISTS "' || schemaname || '"."' || viewname || '" CASCADE;' as "query" ` +
2462
            //     `FROM "pg_views" WHERE "schemaname" IN (${schemaNamesString}) AND "viewname" NOT IN ('geography_columns', 'geometry_columns', 'raster_columns', 'raster_overviews')`;
2463
            // const dropViewQueries: ObjectLiteral[] = await this.query(selectViewDropsQuery);
2464
            // await Promise.all(dropViewQueries.map(q => this.query(q["query"])));
2465

2466
            // ignore spatial_ref_sys; it's a special table supporting PostGIS
2467
            const selectTableDropsQuery = `SELECT 'DROP TABLE "' || schema_name || '"."' || table_name || '" CASCADE;' as "query" FROM "SYS"."TABLES" WHERE "SCHEMA_NAME" IN (${schemaNamesString}) AND "TABLE_NAME" NOT IN ('SYS_AFL_GENERATOR_PARAMETERS') AND "IS_COLUMN_TABLE" = 'TRUE'`
2,414✔
2468
            const dropTableQueries: ObjectLiteral[] = await this.query(
2,414✔
2469
                selectTableDropsQuery,
2470
            )
2471
            await Promise.all(
2,414✔
2472
                dropTableQueries.map((q) => this.query(q["query"])),
8,434✔
2473
            )
2474

2475
            if (!isAnotherTransactionActive) await this.commitTransaction()
2,414✔
2476
        } catch (error) {
2477
            try {
×
2478
                // we throw original error even if rollback thrown an error
2479
                if (!isAnotherTransactionActive)
×
2480
                    await this.rollbackTransaction()
×
2481
            } catch (rollbackError) {}
2482
            throw error
×
2483
        }
2484
    }
2485

2486
    // -------------------------------------------------------------------------
2487
    // Protected Methods
2488
    // -------------------------------------------------------------------------
2489

2490
    protected async loadViews(viewNames?: string[]): Promise<View[]> {
2491
        const hasTable = await this.hasTable(this.getTypeormMetadataTableName())
2,514✔
2492
        if (!hasTable) {
2,514✔
2493
            return []
2,506✔
2494
        }
2495

2496
        if (!viewNames) {
8!
2497
            viewNames = []
×
2498
        }
2499

2500
        const currentDatabase = await this.getCurrentDatabase()
8✔
2501
        const currentSchema = await this.getCurrentSchema()
8✔
2502

2503
        const viewsCondition = viewNames
8✔
2504
            .map((viewName) => {
2505
                let { schema, tableName: name } =
2506
                    this.driver.parseTableName(viewName)
16✔
2507

2508
                if (!schema) {
16!
2509
                    schema = currentSchema
×
2510
                }
2511

2512
                return `("t"."schema" = '${schema}' AND "t"."name" = '${name}')`
16✔
2513
            })
2514
            .join(" OR ")
2515

2516
        const query = `SELECT "t".* FROM ${this.escapePath(
8✔
2517
            this.getTypeormMetadataTableName(),
2518
        )} "t" WHERE "t"."type" = '${MetadataTableType.VIEW}' ${
2519
            viewsCondition ? `AND (${viewsCondition})` : ""
8!
2520
        }`
2521
        const dbViews = await this.query(query)
8✔
2522
        return dbViews.map((dbView: any) => {
8✔
2523
            const view = new View()
4✔
2524
            const schema =
2525
                dbView["schema"] === currentSchema &&
4!
2526
                !this.driver.options.schema
2527
                    ? undefined
2528
                    : dbView["schema"]
2529
            view.database = currentDatabase
4✔
2530
            view.schema = dbView["schema"]
4✔
2531
            view.name = this.driver.buildTableName(dbView["name"], schema)
4✔
2532
            view.expression = dbView["value"]
4✔
2533
            return view
4✔
2534
        })
2535
    }
2536

2537
    /**
2538
     * Loads all tables (with given names) from the database and creates a Table from them.
2539
     */
2540
    protected async loadTables(tableNames?: string[]): Promise<Table[]> {
2541
        if (tableNames && tableNames.length === 0) {
2,896✔
2542
            return []
10✔
2543
        }
2544

2545
        const currentSchema = await this.getCurrentSchema()
2,886✔
2546
        const currentDatabase = await this.getCurrentDatabase()
2,886✔
2547

2548
        const dbTables: { SCHEMA_NAME: string; TABLE_NAME: string }[] = []
2,886✔
2549

2550
        if (!tableNames) {
2,886!
2551
            const tablesSql = `SELECT "SCHEMA_NAME", "TABLE_NAME" FROM "SYS"."TABLES"`
×
2552

2553
            dbTables.push(...(await this.query(tablesSql)))
×
2554
        } else {
2555
            const tablesCondition = tableNames
2,886✔
2556
                .map((tableName) => {
2557
                    let [schema, name] = tableName.split(".")
9,252✔
2558
                    if (!name) {
9,252✔
2559
                        name = schema
354✔
2560
                        schema = this.driver.options.schema || currentSchema
354✔
2561
                    }
2562
                    return `("SCHEMA_NAME" = '${schema}' AND "TABLE_NAME" = '${name}')`
9,252✔
2563
                })
2564
                .join(" OR ")
2565

2566
            const tablesSql =
2567
                `SELECT "SCHEMA_NAME", "TABLE_NAME" FROM "SYS"."TABLES" WHERE ` +
2,886✔
2568
                tablesCondition
2569

2570
            dbTables.push(...(await this.query(tablesSql)))
2,886✔
2571
        }
2572

2573
        // if tables were not found in the db, no need to proceed
2574
        if (dbTables.length === 0) return []
2,886✔
2575

2576
        const columnsCondition = dbTables
454✔
2577
            .map(({ SCHEMA_NAME, TABLE_NAME }) => {
2578
                return `("SCHEMA_NAME" = '${SCHEMA_NAME}' AND "TABLE_NAME" = '${TABLE_NAME}')`
798✔
2579
            })
2580
            .join(" OR ")
2581
        const columnsSql =
2582
            `SELECT * FROM "SYS"."TABLE_COLUMNS" WHERE ` +
454✔
2583
            columnsCondition +
2584
            ` ORDER BY "POSITION"`
2585

2586
        const constraintsCondition = dbTables
454✔
2587
            .map(({ SCHEMA_NAME, TABLE_NAME }) => {
2588
                return `("SCHEMA_NAME" = '${SCHEMA_NAME}' AND "TABLE_NAME" = '${TABLE_NAME}')`
798✔
2589
            })
2590
            .join(" OR ")
2591
        const constraintsSql = `SELECT * FROM "SYS"."CONSTRAINTS" WHERE (${constraintsCondition}) ORDER BY "POSITION"`
454✔
2592

2593
        const indicesCondition = dbTables
454✔
2594
            .map(({ SCHEMA_NAME, TABLE_NAME }) => {
2595
                return `("I"."SCHEMA_NAME" = '${SCHEMA_NAME}' AND "I"."TABLE_NAME" = '${TABLE_NAME}')`
798✔
2596
            })
2597
            .join(" OR ")
2598
        // excluding primary key and autogenerated fulltext indices
2599
        const indicesSql =
2600
            `SELECT "I"."INDEX_TYPE", "I"."SCHEMA_NAME", "I"."TABLE_NAME", "I"."INDEX_NAME", "IC"."COLUMN_NAME", "I"."CONSTRAINT" ` +
454✔
2601
            `FROM "SYS"."INDEXES" "I" INNER JOIN "SYS"."INDEX_COLUMNS" "IC" ON "IC"."INDEX_OID" = "I"."INDEX_OID" ` +
2602
            `WHERE (${indicesCondition}) AND ("I"."CONSTRAINT" IS NULL OR "I"."CONSTRAINT" != 'PRIMARY KEY') AND "I"."INDEX_NAME" NOT LIKE '%_SYS_FULLTEXT_%' ORDER BY "IC"."POSITION"`
2603

2604
        const foreignKeysCondition = dbTables
454✔
2605
            .map(({ SCHEMA_NAME, TABLE_NAME }) => {
2606
                return `("SCHEMA_NAME" = '${SCHEMA_NAME}' AND "TABLE_NAME" = '${TABLE_NAME}')`
798✔
2607
            })
2608
            .join(" OR ")
2609
        const foreignKeysSql = `SELECT * FROM "SYS"."REFERENTIAL_CONSTRAINTS" WHERE (${foreignKeysCondition}) ORDER BY "POSITION"`
454✔
2610
        const [
2611
            dbColumns,
2612
            dbConstraints,
2613
            dbIndices,
2614
            dbForeignKeys,
2615
        ]: ObjectLiteral[][] = await Promise.all([
454✔
2616
            this.query(columnsSql),
2617
            this.query(constraintsSql),
2618
            this.query(indicesSql),
2619
            this.query(foreignKeysSql),
2620
        ])
2621

2622
        // create tables for loaded tables
2623
        return dbTables.map((dbTable) => {
454✔
2624
            const table = new Table()
798✔
2625
            const getSchemaFromKey = (dbObject: any, key: string) => {
798✔
2626
                return dbObject[key] === currentSchema &&
1,100!
2627
                    (!this.driver.options.schema ||
2628
                        this.driver.options.schema === currentSchema)
2629
                    ? undefined
2630
                    : dbObject[key]
2631
            }
2632

2633
            // We do not need to join schema name, when database is by default.
2634
            const schema = getSchemaFromKey(dbTable, "SCHEMA_NAME")
798✔
2635
            table.database = currentDatabase
798✔
2636
            table.schema = dbTable["SCHEMA_NAME"]
798✔
2637
            table.name = this.driver.buildTableName(
798✔
2638
                dbTable["TABLE_NAME"],
2639
                schema,
2640
            )
2641

2642
            // create columns from the loaded columns
2643
            table.columns = dbColumns
798✔
2644
                .filter(
2645
                    (dbColumn) =>
2646
                        dbColumn["TABLE_NAME"] === dbTable["TABLE_NAME"] &&
10,292✔
2647
                        dbColumn["SCHEMA_NAME"] === dbTable["SCHEMA_NAME"],
2648
                )
2649
                .map((dbColumn) => {
2650
                    const columnConstraints = dbConstraints.filter(
2,772✔
2651
                        (dbConstraint) =>
2652
                            dbConstraint["TABLE_NAME"] ===
18,268✔
2653
                                dbColumn["TABLE_NAME"] &&
2654
                            dbConstraint["SCHEMA_NAME"] ===
2655
                                dbColumn["SCHEMA_NAME"] &&
2656
                            dbConstraint["COLUMN_NAME"] ===
2657
                                dbColumn["COLUMN_NAME"],
2658
                    )
2659

2660
                    const columnUniqueIndices = dbIndices.filter((dbIndex) => {
2,772✔
2661
                        return (
7,542✔
2662
                            dbIndex["TABLE_NAME"] === dbTable["TABLE_NAME"] &&
16,076✔
2663
                            dbIndex["SCHEMA_NAME"] === dbTable["SCHEMA_NAME"] &&
2664
                            dbIndex["COLUMN_NAME"] ===
2665
                                dbColumn["COLUMN_NAME"] &&
2666
                            dbIndex["CONSTRAINT"] &&
2667
                            dbIndex["CONSTRAINT"].indexOf("UNIQUE") !== -1
2668
                        )
2669
                    })
2670

2671
                    const tableMetadata = this.connection.entityMetadatas.find(
2,772✔
2672
                        (metadata) =>
2673
                            this.getTablePath(table) ===
8,894✔
2674
                            this.getTablePath(metadata),
2675
                    )
2676
                    const hasIgnoredIndex =
2677
                        columnUniqueIndices.length > 0 &&
2,772✔
2678
                        tableMetadata &&
2679
                        tableMetadata.indices.some((index) => {
2680
                            return columnUniqueIndices.some((uniqueIndex) => {
1,118✔
2681
                                return (
1,130✔
2682
                                    index.name === uniqueIndex["INDEX_NAME"] &&
1,674✔
2683
                                    index.synchronize === false
2684
                                )
2685
                            })
2686
                        })
2687

2688
                    const isConstraintComposite = columnUniqueIndices.every(
2,772✔
2689
                        (uniqueIndex) => {
2690
                            return dbIndices.some(
604✔
2691
                                (dbIndex) =>
2692
                                    dbIndex["INDEX_NAME"] ===
1,711✔
2693
                                        uniqueIndex["INDEX_NAME"] &&
2694
                                    dbIndex["COLUMN_NAME"] !==
2695
                                        dbColumn["COLUMN_NAME"],
2696
                            )
2697
                        },
2698
                    )
2699

2700
                    const tableColumn = new TableColumn()
2,772✔
2701
                    tableColumn.name = dbColumn["COLUMN_NAME"]
2,772✔
2702
                    tableColumn.type = dbColumn["DATA_TYPE_NAME"].toLowerCase()
2,772✔
2703

2704
                    if (
2,772✔
2705
                        tableColumn.type === "dec" ||
5,544✔
2706
                        tableColumn.type === "decimal"
2707
                    ) {
2708
                        // If one of these properties was set, and another was not, Postgres sets '0' in to unspecified property
2709
                        // we set 'undefined' in to unspecified property to avoid changing column on sync
2710
                        if (
8!
2711
                            dbColumn["LENGTH"] !== null &&
16✔
2712
                            !this.isDefaultColumnPrecision(
2713
                                table,
2714
                                tableColumn,
2715
                                dbColumn["LENGTH"],
2716
                            )
2717
                        ) {
2718
                            tableColumn.precision = dbColumn["LENGTH"]
8✔
NEW
2719
                        } else if (
×
2720
                            dbColumn["SCALE"] !== null &&
×
2721
                            !this.isDefaultColumnScale(
2722
                                table,
2723
                                tableColumn,
2724
                                dbColumn["SCALE"],
2725
                            )
2726
                        ) {
NEW
2727
                            tableColumn.precision = undefined
×
2728
                        }
2729
                        if (
8✔
2730
                            dbColumn["SCALE"] !== null &&
12✔
2731
                            !this.isDefaultColumnScale(
2732
                                table,
2733
                                tableColumn,
2734
                                dbColumn["SCALE"],
2735
                            )
2736
                        ) {
2737
                            tableColumn.scale = dbColumn["SCALE"]
4✔
2738
                        } else if (
4✔
2739
                            dbColumn["LENGTH"] !== null &&
8✔
2740
                            !this.isDefaultColumnPrecision(
2741
                                table,
2742
                                tableColumn,
2743
                                dbColumn["LENGTH"],
2744
                            )
2745
                        ) {
2746
                            tableColumn.scale = undefined
4✔
2747
                        }
2748
                    }
2749

2750
                    if (dbColumn["DATA_TYPE_NAME"].toLowerCase() === "array") {
2,772!
NEW
2751
                        tableColumn.isArray = true
×
NEW
2752
                        tableColumn.type =
×
2753
                            dbColumn["CS_DATA_TYPE_NAME"].toLowerCase()
2754
                    }
2755

2756
                    // check only columns that have length property
2757
                    if (
2,772✔
2758
                        this.driver.withLengthColumnTypes.indexOf(
4,272✔
2759
                            tableColumn.type as ColumnType,
2760
                        ) !== -1 &&
2761
                        dbColumn["LENGTH"]
2762
                    ) {
2763
                        const length = dbColumn["LENGTH"].toString()
1,500✔
2764
                        tableColumn.length = !this.isDefaultColumnLength(
1,500✔
2765
                            table,
2766
                            tableColumn,
2767
                            length,
2768
                        )
2769
                            ? length
2770
                            : ""
2771
                    }
2772
                    tableColumn.isUnique =
2,772✔
2773
                        columnUniqueIndices.length > 0 &&
3,970✔
2774
                        !hasIgnoredIndex &&
2775
                        !isConstraintComposite
2776
                    tableColumn.isNullable = dbColumn["IS_NULLABLE"] === "TRUE"
2,772✔
2777
                    tableColumn.isPrimary = !!columnConstraints.find(
2,772✔
2778
                        (constraint) => constraint["IS_PRIMARY_KEY"] === "TRUE",
1,412✔
2779
                    )
2780
                    tableColumn.isGenerated =
2,772✔
2781
                        dbColumn["GENERATION_TYPE"] === "ALWAYS AS IDENTITY"
2782
                    if (tableColumn.isGenerated)
2,772✔
2783
                        tableColumn.generationStrategy = "increment"
400✔
2784

2785
                    if (
2,772✔
2786
                        dbColumn["DEFAULT_VALUE"] === null ||
2,942✔
2787
                        dbColumn["DEFAULT_VALUE"] === undefined
2788
                    ) {
2789
                        tableColumn.default = undefined
2,602✔
2790
                    } else {
2791
                        if (
170✔
2792
                            tableColumn.type === "char" ||
684✔
2793
                            tableColumn.type === "nchar" ||
2794
                            tableColumn.type === "varchar" ||
2795
                            tableColumn.type === "nvarchar" ||
2796
                            tableColumn.type === "alphanum" ||
2797
                            tableColumn.type === "shorttext"
2798
                        ) {
2799
                            tableColumn.default = `'${dbColumn["DEFAULT_VALUE"]}'`
166✔
2800
                        } else if (tableColumn.type === "boolean") {
4!
NEW
2801
                            tableColumn.default =
×
2802
                                dbColumn["DEFAULT_VALUE"] === "1"
×
2803
                                    ? "true"
2804
                                    : "false"
2805
                        } else {
2806
                            tableColumn.default = dbColumn["DEFAULT_VALUE"]
4✔
2807
                        }
2808
                    }
2809
                    if (dbColumn["COMMENTS"]) {
2,772✔
2810
                        tableColumn.comment = dbColumn["COMMENTS"]
92✔
2811
                    }
2812
                    return tableColumn
2,772✔
2813
                })
2814

2815
            // find check constraints of table, group them by constraint name and build TableCheck.
2816
            const tableCheckConstraints = OrmUtils.uniq(
798✔
2817
                dbConstraints.filter(
2818
                    (dbConstraint) =>
2819
                        dbConstraint["TABLE_NAME"] === dbTable["TABLE_NAME"] &&
5,298✔
2820
                        dbConstraint["SCHEMA_NAME"] ===
2821
                            dbTable["SCHEMA_NAME"] &&
2822
                        dbConstraint["CHECK_CONDITION"] !== null &&
2823
                        dbConstraint["CHECK_CONDITION"] !== undefined,
2824
                ),
2825
                (dbConstraint) => dbConstraint["CONSTRAINT_NAME"],
166✔
2826
            )
2827

2828
            table.checks = tableCheckConstraints.map((constraint) => {
798✔
2829
                const checks = dbConstraints.filter(
160✔
2830
                    (dbC) =>
2831
                        dbC["CONSTRAINT_NAME"] ===
1,164✔
2832
                        constraint["CONSTRAINT_NAME"],
2833
                )
2834
                return new TableCheck({
160✔
2835
                    name: constraint["CONSTRAINT_NAME"],
2836
                    columnNames: checks.map((c) => c["COLUMN_NAME"]),
160✔
2837
                    expression: constraint["CHECK_CONDITION"],
2838
                })
2839
            })
2840

2841
            // find foreign key constraints of table, group them by constraint name and build TableForeignKey.
2842
            const tableForeignKeyConstraints = OrmUtils.uniq(
798✔
2843
                dbForeignKeys.filter(
2844
                    (dbForeignKey) =>
2845
                        dbForeignKey["TABLE_NAME"] === dbTable["TABLE_NAME"] &&
1,290✔
2846
                        dbForeignKey["SCHEMA_NAME"] === dbTable["SCHEMA_NAME"],
2847
                ),
2848
                (dbForeignKey) => dbForeignKey["CONSTRAINT_NAME"],
484✔
2849
            )
2850

2851
            table.foreignKeys = tableForeignKeyConstraints.map(
798✔
2852
                (dbForeignKey) => {
2853
                    const foreignKeys = dbForeignKeys.filter(
302✔
2854
                        (dbFk) =>
2855
                            dbFk["CONSTRAINT_NAME"] ===
834✔
2856
                            dbForeignKey["CONSTRAINT_NAME"],
2857
                    )
2858

2859
                    // if referenced table located in currently used schema, we don't need to concat schema name to table name.
2860
                    const schema = getSchemaFromKey(
302✔
2861
                        dbForeignKey,
2862
                        "REFERENCED_SCHEMA_NAME",
2863
                    )
2864
                    const referencedTableName = this.driver.buildTableName(
302✔
2865
                        dbForeignKey["REFERENCED_TABLE_NAME"],
2866
                        schema,
2867
                    )
2868

2869
                    return new TableForeignKey({
302✔
2870
                        name: dbForeignKey["CONSTRAINT_NAME"],
2871
                        columnNames: foreignKeys.map(
2872
                            (dbFk) => dbFk["COLUMN_NAME"],
310✔
2873
                        ),
2874
                        referencedDatabase: table.database,
2875
                        referencedSchema:
2876
                            dbForeignKey["REFERENCED_SCHEMA_NAME"],
2877
                        referencedTableName: referencedTableName,
2878
                        referencedColumnNames: foreignKeys.map(
2879
                            (dbFk) => dbFk["REFERENCED_COLUMN_NAME"],
310✔
2880
                        ),
2881
                        onDelete:
2882
                            dbForeignKey["DELETE_RULE"] === "RESTRICT"
302✔
2883
                                ? "NO ACTION"
2884
                                : dbForeignKey["DELETE_RULE"],
2885
                        onUpdate:
2886
                            dbForeignKey["UPDATE_RULE"] === "RESTRICT"
302✔
2887
                                ? "NO ACTION"
2888
                                : dbForeignKey["UPDATE_RULE"],
2889
                        deferrable: dbForeignKey["CHECK_TIME"].replace(
2890
                            "_",
2891
                            " ",
2892
                        ),
2893
                    })
2894
                },
2895
            )
2896

2897
            // find index constraints of table, group them by constraint name and build TableIndex.
2898
            const tableIndexConstraints = OrmUtils.uniq(
798✔
2899
                dbIndices.filter(
2900
                    (dbIndex) =>
2901
                        dbIndex["TABLE_NAME"] === dbTable["TABLE_NAME"] &&
2,128✔
2902
                        dbIndex["SCHEMA_NAME"] === dbTable["SCHEMA_NAME"],
2903
                ),
2904
                (dbIndex) => dbIndex["INDEX_NAME"],
1,316✔
2905
            )
2906

2907
            table.indices = tableIndexConstraints.map((constraint) => {
798✔
2908
                const indices = dbIndices.filter((index) => {
562✔
2909
                    return (
1,756✔
2910
                        index["SCHEMA_NAME"] === constraint["SCHEMA_NAME"] &&
4,924✔
2911
                        index["TABLE_NAME"] === constraint["TABLE_NAME"] &&
2912
                        index["INDEX_NAME"] === constraint["INDEX_NAME"]
2913
                    )
2914
                })
2915
                return new TableIndex(<TableIndexOptions>{
562✔
2916
                    table: table,
2917
                    name: constraint["INDEX_NAME"],
2918
                    columnNames: indices.map((i) => i["COLUMN_NAME"]),
760✔
2919
                    isUnique:
2920
                        constraint["CONSTRAINT"] &&
988✔
2921
                        constraint["CONSTRAINT"].indexOf("UNIQUE") !== -1,
2922
                    isFulltext: constraint["INDEX_TYPE"] === "FULLTEXT",
2923
                })
2924
            })
2925

2926
            return table
798✔
2927
        })
2928
    }
2929

2930
    /**
2931
     * Builds and returns SQL for create table.
2932
     */
2933
    protected createTableSql(table: Table, createForeignKeys?: boolean): Query {
2934
        const columnDefinitions = table.columns
8,494✔
2935
            .map((column) => this.buildCreateColumnSql(column))
26,382✔
2936
            .join(", ")
2937
        let sql = `CREATE TABLE ${this.escapePath(table)} (${columnDefinitions}`
8,494✔
2938

2939
        // we create unique indexes instead of unique constraints, because SAP HANA does not have unique constraints.
2940
        // if we mark column as Unique, it means that we create UNIQUE INDEX.
2941
        table.columns
8,494✔
2942
            .filter((column) => column.isUnique)
26,382✔
2943
            .forEach((column) => {
2944
                const isUniqueIndexExist = table.indices.some((index) => {
1,322✔
2945
                    return (
2,042✔
2946
                        index.columnNames.length === 1 &&
6,088✔
2947
                        !!index.isUnique &&
2948
                        index.columnNames.indexOf(column.name) !== -1
2949
                    )
2950
                })
2951
                const isUniqueConstraintExist = table.uniques.some((unique) => {
1,322✔
2952
                    return (
×
2953
                        unique.columnNames.length === 1 &&
×
2954
                        unique.columnNames.indexOf(column.name) !== -1
2955
                    )
2956
                })
2957
                if (!isUniqueIndexExist && !isUniqueConstraintExist)
1,322✔
2958
                    table.indices.push(
10✔
2959
                        new TableIndex({
2960
                            name: this.connection.namingStrategy.uniqueConstraintName(
2961
                                table,
2962
                                [column.name],
2963
                            ),
2964
                            columnNames: [column.name],
2965
                            isUnique: true,
2966
                        }),
2967
                    )
2968
            })
2969

2970
        // as SAP HANA does not have unique constraints, we must create table indices from table uniques and mark them as unique.
2971
        if (table.uniques.length > 0) {
8,494✔
2972
            table.uniques.forEach((unique) => {
50✔
2973
                const uniqueExist = table.indices.some(
50✔
2974
                    (index) => index.name === unique.name,
×
2975
                )
2976
                if (!uniqueExist) {
50✔
2977
                    table.indices.push(
50✔
2978
                        new TableIndex({
2979
                            name: unique.name,
2980
                            columnNames: unique.columnNames,
2981
                            isUnique: true,
2982
                        }),
2983
                    )
2984
                }
2985
            })
2986
        }
2987

2988
        if (table.checks.length > 0) {
8,494✔
2989
            const checksSql = table.checks
124✔
2990
                .map((check) => {
2991
                    const checkName = check.name
124!
2992
                        ? check.name
2993
                        : this.connection.namingStrategy.checkConstraintName(
2994
                              table,
2995
                              check.expression!,
2996
                          )
2997
                    return `CONSTRAINT "${checkName}" CHECK (${check.expression})`
124✔
2998
                })
2999
                .join(", ")
3000

3001
            sql += `, ${checksSql}`
124✔
3002
        }
3003

3004
        if (table.foreignKeys.length > 0 && createForeignKeys) {
8,494✔
3005
            const foreignKeysSql = table.foreignKeys
10✔
3006
                .map((fk) => {
3007
                    const columnNames = fk.columnNames
12✔
3008
                        .map((columnName) => `"${columnName}"`)
14✔
3009
                        .join(", ")
3010
                    if (!fk.name)
12✔
3011
                        fk.name = this.connection.namingStrategy.foreignKeyName(
8✔
3012
                            table,
3013
                            fk.columnNames,
3014
                            this.getTablePath(fk),
3015
                            fk.referencedColumnNames,
3016
                        )
3017
                    const referencedColumnNames = fk.referencedColumnNames
12✔
3018
                        .map((columnName) => `"${columnName}"`)
14✔
3019
                        .join(", ")
3020

3021
                    let constraint = `CONSTRAINT "${
12✔
3022
                        fk.name
3023
                    }" FOREIGN KEY (${columnNames}) REFERENCES ${this.escapePath(
3024
                        this.getTablePath(fk),
3025
                    )} (${referencedColumnNames})`
3026
                    // SAP HANA does not have "NO ACTION" option for FK's
3027
                    if (fk.onDelete) {
12✔
3028
                        const onDelete =
3029
                            fk.onDelete === "NO ACTION"
4!
3030
                                ? "RESTRICT"
3031
                                : fk.onDelete
3032
                        constraint += ` ON DELETE ${onDelete}`
4✔
3033
                    }
3034
                    if (fk.onUpdate) {
12✔
3035
                        const onUpdate =
3036
                            fk.onUpdate === "NO ACTION"
4!
3037
                                ? "RESTRICT"
3038
                                : fk.onUpdate
3039
                        constraint += ` ON UPDATE ${onUpdate}`
4✔
3040
                    }
3041
                    if (fk.deferrable) {
12✔
3042
                        constraint += ` ${fk.deferrable}`
4✔
3043
                    }
3044

3045
                    return constraint
12✔
3046
                })
3047
                .join(", ")
3048

3049
            sql += `, ${foreignKeysSql}`
10✔
3050
        }
3051

3052
        const primaryColumns = table.columns.filter(
8,494✔
3053
            (column) => column.isPrimary,
26,382✔
3054
        )
3055
        if (primaryColumns.length > 0) {
8,494✔
3056
            const primaryKeyName =
3057
                this.connection.namingStrategy.primaryKeyName(
8,486✔
3058
                    table,
3059
                    primaryColumns.map((column) => column.name),
10,648✔
3060
                )
3061
            const columnNames = primaryColumns
8,486✔
3062
                .map((column) => `"${column.name}"`)
10,648✔
3063
                .join(", ")
3064
            sql += `, CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNames})`
8,486✔
3065
        }
3066

3067
        sql += `)`
8,494✔
3068

3069
        return new Query(sql)
8,494✔
3070
    }
3071

3072
    /**
3073
     * Builds drop table sql.
3074
     */
3075
    protected dropTableSql(
3076
        tableOrName: Table | string,
3077
        ifExist?: boolean,
3078
    ): Query {
3079
        const query = ifExist
8,494!
3080
            ? `DROP TABLE IF EXISTS ${this.escapePath(tableOrName)}`
3081
            : `DROP TABLE ${this.escapePath(tableOrName)}`
3082
        return new Query(query)
8,494✔
3083
    }
3084

3085
    protected createViewSql(view: View): Query {
3086
        if (typeof view.expression === "string") {
12!
3087
            return new Query(
×
3088
                `CREATE VIEW ${this.escapePath(view)} AS ${view.expression}`,
3089
            )
3090
        } else {
3091
            return new Query(
12✔
3092
                `CREATE VIEW ${this.escapePath(view)} AS ${view
3093
                    .expression(this.connection)
3094
                    .getQuery()}`,
3095
            )
3096
        }
3097
    }
3098

3099
    protected async insertViewDefinitionSql(view: View): Promise<Query> {
3100
        let { schema, tableName: name } = this.driver.parseTableName(view)
12✔
3101

3102
        if (!schema) {
12!
3103
            schema = await this.getCurrentSchema()
×
3104
        }
3105

3106
        const expression =
3107
            typeof view.expression === "string"
12!
3108
                ? view.expression.trim()
3109
                : view.expression(this.connection).getQuery()
3110
        return this.insertTypeormMetadataSql({
12✔
3111
            type: MetadataTableType.VIEW,
3112
            schema: schema,
3113
            name: name,
3114
            value: expression,
3115
        })
3116
    }
3117

3118
    /**
3119
     * Builds drop view sql.
3120
     */
3121
    protected dropViewSql(viewOrPath: View | string): Query {
3122
        return new Query(`DROP VIEW ${this.escapePath(viewOrPath)}`)
12✔
3123
    }
3124

3125
    /**
3126
     * Builds remove view sql.
3127
     */
3128
    protected async deleteViewDefinitionSql(
3129
        viewOrPath: View | string,
3130
    ): Promise<Query> {
3131
        let { schema, tableName: name } = this.driver.parseTableName(viewOrPath)
12✔
3132

3133
        if (!schema) {
12!
3134
            schema = await this.getCurrentSchema()
×
3135
        }
3136

3137
        return this.deleteTypeormMetadataSql({
12✔
3138
            type: MetadataTableType.VIEW,
3139
            schema,
3140
            name,
3141
        })
3142
    }
3143

3144
    protected addColumnSql(table: Table, column: TableColumn): string {
3145
        return `ALTER TABLE ${this.escapePath(
110✔
3146
            table,
3147
        )} ADD (${this.buildCreateColumnSql(column)})`
3148
    }
3149

3150
    protected dropColumnSql(table: Table, column: TableColumn): string {
3151
        return `ALTER TABLE ${this.escapePath(table)} DROP ("${column.name}")`
110✔
3152
    }
3153

3154
    /**
3155
     * Builds create index sql.
3156
     */
3157
    protected createIndexSql(table: Table, index: TableIndex): Query {
3158
        const columns = index.columnNames
4,798✔
3159
            .map((columnName) => `"${columnName}"`)
5,794✔
3160
            .join(", ")
3161
        let indexType = ""
4,798✔
3162
        if (index.isUnique) {
4,798✔
3163
            indexType += "UNIQUE "
1,844✔
3164
        }
3165
        if (index.isFulltext && this.driver.isFullTextColumnTypeSupported()) {
4,798✔
3166
            indexType += "FULLTEXT "
4✔
3167
        }
3168

3169
        return new Query(
4,798✔
3170
            `CREATE ${indexType}INDEX "${index.name}" ON ${this.escapePath(
3171
                table,
3172
            )} (${columns}) ${index.where ? "WHERE " + index.where : ""}`,
4,798!
3173
        )
3174
    }
3175

3176
    /**
3177
     * Builds drop index sql.
3178
     */
3179
    protected dropIndexSql(
3180
        table: Table,
3181
        indexOrName: TableIndex | string,
3182
    ): Query {
3183
        const indexName = InstanceChecker.isTableIndex(indexOrName)
4,798!
3184
            ? indexOrName.name
3185
            : indexOrName
3186
        const parsedTableName = this.driver.parseTableName(table)
4,798✔
3187

3188
        if (!parsedTableName.schema) {
4,798!
3189
            return new Query(`DROP INDEX "${indexName}"`)
×
3190
        } else {
3191
            return new Query(
4,798✔
3192
                `DROP INDEX "${parsedTableName.schema}"."${indexName}"`,
3193
            )
3194
        }
3195
    }
3196

3197
    /**
3198
     * Builds create primary key sql.
3199
     */
3200
    protected createPrimaryKeySql(table: Table, columnNames: string[]): Query {
3201
        const primaryKeyName = this.connection.namingStrategy.primaryKeyName(
10✔
3202
            table,
3203
            columnNames,
3204
        )
3205
        const columnNamesString = columnNames
10✔
3206
            .map((columnName) => `"${columnName}"`)
12✔
3207
            .join(", ")
3208
        return new Query(
10✔
3209
            `ALTER TABLE ${this.escapePath(
3210
                table,
3211
            )} ADD CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNamesString})`,
3212
        )
3213
    }
3214

3215
    /**
3216
     * Builds drop primary key sql.
3217
     */
3218
    protected dropPrimaryKeySql(table: Table): Query {
3219
        const columnNames = table.primaryColumns.map((column) => column.name)
12✔
3220
        const primaryKeyName = this.connection.namingStrategy.primaryKeyName(
10✔
3221
            table,
3222
            columnNames,
3223
        )
3224
        return new Query(
10✔
3225
            `ALTER TABLE ${this.escapePath(
3226
                table,
3227
            )} DROP CONSTRAINT "${primaryKeyName}"`,
3228
        )
3229
    }
3230

3231
    /**
3232
     * Builds create check constraint sql.
3233
     */
3234
    protected createCheckConstraintSql(
3235
        table: Table,
3236
        checkConstraint: TableCheck,
3237
    ): Query {
3238
        return new Query(
16✔
3239
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
3240
                checkConstraint.name
3241
            }" CHECK (${checkConstraint.expression})`,
3242
        )
3243
    }
3244

3245
    /**
3246
     * Builds drop check constraint sql.
3247
     */
3248
    protected dropCheckConstraintSql(
3249
        table: Table,
3250
        checkOrName: TableCheck | string,
3251
    ): Query {
3252
        const checkName = InstanceChecker.isTableCheck(checkOrName)
16!
3253
            ? checkOrName.name
3254
            : checkOrName
3255
        return new Query(
16✔
3256
            `ALTER TABLE ${this.escapePath(
3257
                table,
3258
            )} DROP CONSTRAINT "${checkName}"`,
3259
        )
3260
    }
3261

3262
    /**
3263
     * Builds create foreign key sql.
3264
     */
3265
    protected createForeignKeySql(
3266
        tableOrName: Table | string,
3267
        foreignKey: TableForeignKey,
3268
    ): Query {
3269
        const columnNames = foreignKey.columnNames
5,574✔
3270
            .map((column) => `"` + column + `"`)
6,360✔
3271
            .join(", ")
3272
        const referencedColumnNames = foreignKey.referencedColumnNames
5,574✔
3273
            .map((column) => `"` + column + `"`)
6,360✔
3274
            .join(",")
3275
        let sql =
3276
            `ALTER TABLE ${this.escapePath(tableOrName)} ADD CONSTRAINT "${
5,574✔
3277
                foreignKey.name
3278
            }" FOREIGN KEY (${columnNames}) ` +
3279
            `REFERENCES ${this.escapePath(
3280
                this.getTablePath(foreignKey),
3281
            )}(${referencedColumnNames})`
3282

3283
        // SAP HANA does not have "NO ACTION" option for FK's
3284
        if (foreignKey.onDelete) {
5,574✔
3285
            const onDelete =
3286
                foreignKey.onDelete === "NO ACTION"
5,574✔
3287
                    ? "RESTRICT"
3288
                    : foreignKey.onDelete
3289
            sql += ` ON DELETE ${onDelete}`
5,574✔
3290
        }
3291
        if (foreignKey.onUpdate) {
5,574✔
3292
            const onUpdate =
3293
                foreignKey.onUpdate === "NO ACTION"
5,572✔
3294
                    ? "RESTRICT"
3295
                    : foreignKey.onUpdate
3296
            sql += ` ON UPDATE ${onUpdate}`
5,572✔
3297
        }
3298

3299
        if (foreignKey.deferrable) {
5,574✔
3300
            sql += ` ${foreignKey.deferrable}`
66✔
3301
        }
3302

3303
        return new Query(sql)
5,574✔
3304
    }
3305

3306
    /**
3307
     * Builds drop foreign key sql.
3308
     */
3309
    protected dropForeignKeySql(
3310
        tableOrName: Table | string,
3311
        foreignKeyOrName: TableForeignKey | string,
3312
    ): Query {
3313
        const foreignKeyName = InstanceChecker.isTableForeignKey(
5,586!
3314
            foreignKeyOrName,
3315
        )
3316
            ? foreignKeyOrName.name
3317
            : foreignKeyOrName
3318
        return new Query(
5,586✔
3319
            `ALTER TABLE ${this.escapePath(
3320
                tableOrName,
3321
            )} DROP CONSTRAINT "${foreignKeyName}"`,
3322
        )
3323
    }
3324

3325
    /**
3326
     * Escapes a given comment so it's safe to include in a query.
3327
     */
3328
    protected escapeComment(comment?: string) {
3329
        if (!comment) {
90✔
3330
            return "NULL"
8✔
3331
        }
3332

3333
        comment = comment.replace(/'/g, "''").replace(/\u0000/g, "") // Null bytes aren't allowed in comments
82✔
3334

3335
        return `'${comment}'`
82✔
3336
    }
3337

3338
    /**
3339
     * Escapes given table or view path.
3340
     */
3341
    protected escapePath(target: Table | View | string): string {
3342
        const { schema, tableName } = this.driver.parseTableName(target)
39,096✔
3343

3344
        if (schema) {
39,096✔
3345
            return `"${schema}"."${tableName}"`
39,096✔
3346
        }
3347

3348
        return `"${tableName}"`
×
3349
    }
3350

3351
    /**
3352
     * Builds a query for create column.
3353
     */
3354
    protected buildCreateColumnSql(
3355
        column: TableColumn,
3356
        explicitDefault?: boolean,
3357
        explicitNullable?: boolean,
3358
    ) {
3359
        let c =
3360
            `"${column.name}" ` + this.connection.driver.createFullType(column)
26,508✔
3361
        if (column.default !== undefined && column.default !== null) {
26,508✔
3362
            c += " DEFAULT " + column.default
1,204✔
3363
        } else if (explicitDefault) {
25,304✔
3364
            c += " DEFAULT NULL"
2✔
3365
        }
3366
        if (!column.isGenerated) {
26,508✔
3367
            // NOT NULL is not supported with GENERATED
3368
            if (column.isNullable !== true) c += " NOT NULL"
21,388✔
3369
            else if (explicitNullable) c += " NULL"
4,144✔
3370
        }
3371
        if (
26,508✔
3372
            column.isGenerated === true &&
31,628✔
3373
            column.generationStrategy === "increment"
3374
        ) {
3375
            c += " GENERATED ALWAYS AS IDENTITY"
4,864✔
3376
        }
3377
        if (column.comment) {
26,508✔
3378
            c += ` COMMENT ${this.escapeComment(column.comment)}`
78✔
3379
        }
3380

3381
        return c
26,508✔
3382
    }
3383

3384
    /**
3385
     * Change table comment.
3386
     */
3387
    changeTableComment(
3388
        tableOrName: Table | string,
3389
        comment?: string,
3390
    ): Promise<void> {
3391
        throw new TypeORMError(
×
3392
            `spa driver does not support change table comment.`,
3393
        )
3394
    }
3395
}
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