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

typeorm / typeorm / 14081280213

26 Mar 2025 10:41AM UTC coverage: 72.384% (+0.004%) from 72.38%
14081280213

push

github

web-flow
refactor: database server version fetching & comparison (#11357)

8658 of 12643 branches covered (68.48%)

Branch coverage included in aggregate %.

37 of 59 new or added lines in 13 files covered. (62.71%)

3 existing lines in 3 files now uncovered.

17889 of 24032 relevant lines covered (74.44%)

159593.23 hits per line

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

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

32
/**
33
 * Runs queries on a single SQL Server database connection.
34
 */
35
export class SapQueryRunner extends BaseQueryRunner implements QueryRunner {
35✔
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()
×
55

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

60
    constructor(driver: SapDriver, mode: ReplicationMode) {
61
        super()
×
62
        this.driver = driver
×
63
        this.connection = driver.connection
×
64
        this.broadcaster = new Broadcaster(this)
×
65
        this.mode = mode
×
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
×
78

79
        this.databaseConnection = await this.driver.obtainMasterConnection()
×
80

81
        return this.databaseConnection
×
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
×
90

91
        if (this.databaseConnection) {
×
92
            return this.driver.master.release(this.databaseConnection)
×
93
        }
94

95
        return Promise.resolve()
×
96
    }
97

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

104
        if (
×
105
            this.isTransactionActive &&
×
106
            this.driver.transactionSupport === "simple"
107
        )
108
            throw new TransactionAlreadyStartedError()
×
109

110
        await this.broadcaster.broadcast("BeforeTransactionStart")
×
111

112
        this.isTransactionActive = true
×
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" })
×
119

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

126
        await this.broadcaster.broadcast("AfterTransactionStart")
×
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()
×
135

136
        if (!this.isTransactionActive) throw new TransactionNotStartedError()
×
137

138
        await this.broadcaster.broadcast("BeforeTransactionCommit")
×
139

140
        await this.query("COMMIT")
×
141
        this.isTransactionActive = false
×
142

143
        await this.setAutoCommit({ status: "on" })
×
144
        await this.broadcaster.broadcast("AfterTransactionCommit")
×
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()
×
153

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

156
        await this.broadcaster.broadcast("BeforeTransactionRollback")
×
157

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

161
        await this.setAutoCommit({ status: "on" })
×
162
        await this.broadcaster.broadcast("AfterTransactionRollback")
×
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()
×
171

172
        const execute = promisify(connection.exec.bind(connection))
×
173

174
        connection.setAutoCommit(options.status === "on")
×
175

176
        const query = `SET TRANSACTION AUTOCOMMIT DDL ${options.status.toUpperCase()};`
×
177
        try {
×
178
            await execute(query)
×
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,
×
191
    ): Promise<any> {
192
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
×
193

194
        const release = await this.lock.acquire()
×
195

196
        let statement: any
197
        const result = new QueryResult()
×
198
        const broadcasterResult = new BroadcasterResult()
×
199

200
        try {
×
201
            const databaseConnection = await this.connect()
×
202

203
            this.driver.connection.logger.logQuery(query, parameters, this)
×
204
            this.broadcaster.broadcastBeforeQueryEvent(
×
205
                broadcasterResult,
206
                query,
207
                parameters,
208
            )
209

210
            const queryStartTime = +new Date()
×
211
            const isInsertQuery = query.substr(0, 11) === "INSERT INTO"
×
212

213
            if (parameters?.some(Array.isArray)) {
×
214
                statement = await promisify(
×
215
                    databaseConnection.prepare.bind(databaseConnection),
216
                )(query)
217
            }
218

219
            let raw: any
220
            try {
×
221
                raw = statement
×
222
                    ? await promisify(statement.exec.bind(statement))(
223
                          parameters,
224
                      )
225
                    : await promisify(
226
                          databaseConnection.exec.bind(databaseConnection),
227
                      )(query, parameters, {})
228
            } catch (err) {
229
                throw new QueryFailedError(query, parameters, err)
×
230
            }
231

232
            // log slow queries if maxQueryExecution time is set
233
            const maxQueryExecutionTime =
234
                this.driver.connection.options.maxQueryExecutionTime
×
235
            const queryEndTime = +new Date()
×
236
            const queryExecutionTime = queryEndTime - queryStartTime
×
237

238
            this.broadcaster.broadcastAfterQueryEvent(
×
239
                broadcasterResult,
240
                query,
241
                parameters,
242
                true,
243
                queryExecutionTime,
244
                raw,
245
                undefined,
246
            )
247

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

260
            if (typeof raw === "number") {
×
261
                result.affected = raw
×
262
            } else if (Array.isArray(raw)) {
×
263
                result.records = raw
×
264
            }
265

266
            result.raw = raw
×
267

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

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

315
            await broadcasterResult.wait()
×
316

317
            // Always release the lock.
318
            release()
×
319
        }
320

321
        if (useStructuredResult) {
×
322
            return result
×
323
        } else {
324
            return result.raw
×
325
        }
326
    }
327

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

339
        const databaseConnection = await this.connect()
×
340
        this.driver.connection.logger.logQuery(query, parameters, this)
×
341

342
        const prepareAsync = promisify(databaseConnection.prepare).bind(
×
343
            databaseConnection,
344
        )
345
        const statement = await prepareAsync(query)
×
346
        const resultSet = statement.executeQuery(parameters)
×
347
        const stream = this.driver.streamClient.createObjectStream(resultSet)
×
348

349
        if (onEnd) stream.on("end", onEnd)
×
350
        if (onError) stream.on("error", onError)
×
351

352
        return stream
×
353
    }
354

355
    /**
356
     * Returns all available database names including system databases.
357
     */
358
    async getDatabases(): Promise<string[]> {
359
        const results: ObjectLiteral[] = await this.query(
×
360
            `SELECT DATABASE_NAME FROM "SYS"."M_DATABASES"`,
361
        )
362
        return results.map((result) => result["DATABASE_NAME"])
×
363
    }
364

365
    /**
366
     * Returns all available schema names including system schemas.
367
     * If database parameter specified, returns schemas of that database.
368
     */
369
    async getSchemas(database?: string): Promise<string[]> {
370
        const query = database
×
371
            ? `SELECT * FROM "${database}"."SYS"."SCHEMAS"`
372
            : `SELECT * FROM "SYS"."SCHEMAS"`
373
        const results: ObjectLiteral[] = await this.query(query)
×
374
        return results.map((result) => result["SCHEMA_NAME"])
×
375
    }
376

377
    /**
378
     * Checks if database with the given name exist.
379
     */
380
    async hasDatabase(database: string): Promise<boolean> {
381
        const databases = await this.getDatabases()
×
382
        return databases.indexOf(database) !== -1
×
383
    }
384

385
    /**
386
     * Returns current database.
387
     */
388
    async getCurrentDatabase(): Promise<string> {
NEW
389
        const currentDBQuery: [{ dbName: string }] = await this.query(
×
390
            `SELECT "DATABASE_NAME" AS "dbName" FROM "SYS"."M_DATABASE"`,
391
        )
392

NEW
393
        return currentDBQuery[0].dbName
×
394
    }
395

396
    /**
397
     * Returns the database server version.
398
     */
399
    async getVersion(): Promise<string> {
NEW
400
        const currentDBQuery: [{ version: string }] = await this.query(
×
401
            `SELECT "VERSION" AS "version" FROM "SYS"."M_DATABASE"`,
402
        )
403

NEW
404
        return currentDBQuery[0].version
×
405
    }
406

407
    /**
408
     * Checks if schema with the given name exist.
409
     */
410
    async hasSchema(schema: string): Promise<boolean> {
411
        const schemas = await this.getSchemas()
×
412
        return schemas.indexOf(schema) !== -1
×
413
    }
414

415
    /**
416
     * Returns current schema.
417
     */
418
    async getCurrentSchema(): Promise<string> {
NEW
419
        const currentSchemaQuery: [{ schemaName: string }] = await this.query(
×
420
            `SELECT CURRENT_SCHEMA AS "schemaName" FROM "SYS"."DUMMY"`,
421
        )
422

NEW
423
        return currentSchemaQuery[0].schemaName
×
424
    }
425

426
    /**
427
     * Checks if table with the given name exist in the database.
428
     */
429
    async hasTable(tableOrName: Table | string): Promise<boolean> {
430
        const parsedTableName = this.driver.parseTableName(tableOrName)
×
431

432
        if (!parsedTableName.schema) {
×
433
            parsedTableName.schema = await this.getCurrentSchema()
×
434
        }
435

NEW
436
        const sql = `SELECT COUNT(*) as "hasTable" FROM "SYS"."TABLES" WHERE "SCHEMA_NAME" = '${parsedTableName.schema}' AND "TABLE_NAME" = '${parsedTableName.tableName}'`
×
NEW
437
        const result: [{ hasTable: number }] = await this.query(sql)
×
438

NEW
439
        return result[0].hasTable > 0
×
440
    }
441

442
    /**
443
     * Checks if column with the given name exist in the given table.
444
     */
445
    async hasColumn(
446
        tableOrName: Table | string,
447
        columnName: string,
448
    ): Promise<boolean> {
449
        const parsedTableName = this.driver.parseTableName(tableOrName)
×
450

451
        if (!parsedTableName.schema) {
×
452
            parsedTableName.schema = await this.getCurrentSchema()
×
453
        }
454

NEW
455
        const sql = `SELECT COUNT(*) as "hasColumn" FROM "SYS"."TABLE_COLUMNS" WHERE "SCHEMA_NAME" = '${parsedTableName.schema}' AND "TABLE_NAME" = '${parsedTableName.tableName}' AND "COLUMN_NAME" = '${columnName}'`
×
NEW
456
        const result: [{ hasColumn: number }] = await this.query(sql)
×
457

NEW
458
        return result[0].hasColumn > 0
×
459
    }
460

461
    /**
462
     * Creates a new database.
463
     */
464
    async createDatabase(
465
        database: string,
466
        ifNotExist?: boolean,
467
    ): Promise<void> {
468
        return Promise.resolve()
×
469
    }
470

471
    /**
472
     * Drops database.
473
     */
474
    async dropDatabase(database: string, ifExist?: boolean): Promise<void> {
475
        return Promise.resolve()
×
476
    }
477

478
    /**
479
     * Creates a new table schema.
480
     */
481
    async createSchema(
482
        schemaPath: string,
483
        ifNotExist?: boolean,
484
    ): Promise<void> {
485
        const schema =
486
            schemaPath.indexOf(".") === -1
×
487
                ? schemaPath
488
                : schemaPath.split(".")[1]
489

490
        let exist = false
×
491
        if (ifNotExist) {
×
492
            const result = await this.query(
×
493
                `SELECT * FROM "SYS"."SCHEMAS" WHERE "SCHEMA_NAME" = '${schema}'`,
494
            )
495
            exist = !!result.length
×
496
        }
497
        if (!ifNotExist || (ifNotExist && !exist)) {
×
498
            const up = `CREATE SCHEMA "${schema}"`
×
499
            const down = `DROP SCHEMA "${schema}" CASCADE`
×
500
            await this.executeQueries(new Query(up), new Query(down))
×
501
        }
502
    }
503

504
    /**
505
     * Drops table schema
506
     */
507
    async dropSchema(
508
        schemaPath: string,
509
        ifExist?: boolean,
510
        isCascade?: boolean,
511
    ): Promise<void> {
512
        const schema =
513
            schemaPath.indexOf(".") === -1
×
514
                ? schemaPath
515
                : schemaPath.split(".")[0]
516
        let exist = false
×
517
        if (ifExist) {
×
518
            const result = await this.query(
×
519
                `SELECT * FROM "SYS"."SCHEMAS" WHERE "SCHEMA_NAME" = '${schema}'`,
520
            )
521
            exist = !!result.length
×
522
        }
523
        if (!ifExist || (ifExist && exist)) {
×
524
            const up = `DROP SCHEMA "${schema}" ${isCascade ? "CASCADE" : ""}`
×
525
            const down = `CREATE SCHEMA "${schema}"`
×
526
            await this.executeQueries(new Query(up), new Query(down))
×
527
        }
528
    }
529

530
    /**
531
     * Creates a new table.
532
     */
533
    async createTable(
534
        table: Table,
535
        ifNotExist: boolean = false,
×
536
        createForeignKeys: boolean = true,
×
537
        createIndices: boolean = true,
×
538
    ): Promise<void> {
539
        if (ifNotExist) {
×
540
            const isTableExist = await this.hasTable(table)
×
541
            if (isTableExist) return Promise.resolve()
×
542
        }
543
        const upQueries: Query[] = []
×
544
        const downQueries: Query[] = []
×
545

546
        upQueries.push(this.createTableSql(table, createForeignKeys))
×
547
        downQueries.push(this.dropTableSql(table))
×
548

549
        // if createForeignKeys is true, we must drop created foreign keys in down query.
550
        // createTable does not need separate method to create foreign keys, because it create fk's in the same query with table creation.
551
        if (createForeignKeys)
×
552
            table.foreignKeys.forEach((foreignKey) =>
×
553
                downQueries.push(this.dropForeignKeySql(table, foreignKey)),
×
554
            )
555

556
        if (createIndices) {
×
557
            table.indices.forEach((index) => {
×
558
                // new index may be passed without name. In this case we generate index name manually.
559
                if (!index.name)
×
560
                    index.name = this.connection.namingStrategy.indexName(
×
561
                        table,
562
                        index.columnNames,
563
                        index.where,
564
                    )
565
                upQueries.push(this.createIndexSql(table, index))
×
566
                downQueries.push(this.dropIndexSql(table, index))
×
567
            })
568
        }
569

570
        await this.executeQueries(upQueries, downQueries)
×
571
    }
572

573
    /**
574
     * Drops the table.
575
     */
576
    async dropTable(
577
        tableOrName: Table | string,
578
        ifExist?: boolean,
579
        dropForeignKeys: boolean = true,
×
580
        dropIndices: boolean = true,
×
581
    ): Promise<void> {
582
        if (ifExist) {
×
583
            const isTableExist = await this.hasTable(tableOrName)
×
584
            if (!isTableExist) return Promise.resolve()
×
585
        }
586

587
        // if dropTable called with dropForeignKeys = true, we must create foreign keys in down query.
588
        const createForeignKeys: boolean = dropForeignKeys
×
589
        const table = InstanceChecker.isTable(tableOrName)
×
590
            ? tableOrName
591
            : await this.getCachedTable(tableOrName)
592
        const upQueries: Query[] = []
×
593
        const downQueries: Query[] = []
×
594

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

598
        if (dropIndices) {
×
599
            table.indices.forEach((index) => {
×
600
                upQueries.push(this.dropIndexSql(table, index))
×
601
                downQueries.push(this.createIndexSql(table, index))
×
602
            })
603
        }
604

605
        // if dropForeignKeys is true, we just drop the table, otherwise we also drop table foreign keys.
606
        // createTable does not need separate method to create foreign keys, because it create fk's in the same query with table creation.
607
        if (dropForeignKeys)
×
608
            table.foreignKeys.forEach((foreignKey) =>
×
609
                upQueries.push(this.dropForeignKeySql(table, foreignKey)),
×
610
            )
611

612
        upQueries.push(this.dropTableSql(table))
×
613
        downQueries.push(this.createTableSql(table, createForeignKeys))
×
614

615
        await this.executeQueries(upQueries, downQueries)
×
616
    }
617

618
    /**
619
     * Creates a new view.
620
     */
621
    async createView(
622
        view: View,
623
        syncWithMetadata: boolean = false,
×
624
    ): Promise<void> {
625
        const upQueries: Query[] = []
×
626
        const downQueries: Query[] = []
×
627
        upQueries.push(this.createViewSql(view))
×
628
        if (syncWithMetadata)
×
629
            upQueries.push(await this.insertViewDefinitionSql(view))
×
630
        downQueries.push(this.dropViewSql(view))
×
631
        if (syncWithMetadata)
×
632
            downQueries.push(await this.deleteViewDefinitionSql(view))
×
633
        await this.executeQueries(upQueries, downQueries)
×
634
    }
635

636
    /**
637
     * Drops the view.
638
     */
639
    async dropView(target: View | string): Promise<void> {
640
        const viewName = InstanceChecker.isView(target) ? target.name : target
×
641
        const view = await this.getCachedView(viewName)
×
642

643
        const upQueries: Query[] = []
×
644
        const downQueries: Query[] = []
×
645
        upQueries.push(await this.deleteViewDefinitionSql(view))
×
646
        upQueries.push(this.dropViewSql(view))
×
647
        downQueries.push(await this.insertViewDefinitionSql(view))
×
648
        downQueries.push(this.createViewSql(view))
×
649
        await this.executeQueries(upQueries, downQueries)
×
650
    }
651

652
    /**
653
     * Renames a table.
654
     */
655
    async renameTable(
656
        oldTableOrName: Table | string,
657
        newTableName: string,
658
    ): Promise<void> {
659
        const upQueries: Query[] = []
×
660
        const downQueries: Query[] = []
×
661
        const oldTable = InstanceChecker.isTable(oldTableOrName)
×
662
            ? oldTableOrName
663
            : await this.getCachedTable(oldTableOrName)
664
        const newTable = oldTable.clone()
×
665

666
        const { schema: schemaName, tableName: oldTableName } =
667
            this.driver.parseTableName(oldTable)
×
668

669
        newTable.name = schemaName
×
670
            ? `${schemaName}.${newTableName}`
671
            : newTableName
672

673
        // rename table
674
        upQueries.push(
×
675
            new Query(
676
                `RENAME TABLE ${this.escapePath(oldTable)} TO ${this.escapePath(
677
                    newTableName,
678
                )}`,
679
            ),
680
        )
681
        downQueries.push(
×
682
            new Query(
683
                `RENAME TABLE ${this.escapePath(newTable)} TO ${this.escapePath(
684
                    oldTableName,
685
                )}`,
686
            ),
687
        )
688

689
        // drop old FK's. Foreign keys must be dropped before the primary keys are dropped
690
        newTable.foreignKeys.forEach((foreignKey) => {
×
691
            upQueries.push(this.dropForeignKeySql(newTable, foreignKey))
×
692
            downQueries.push(this.createForeignKeySql(newTable, foreignKey))
×
693
        })
694

695
        // SAP HANA does not allow to drop PK's which is referenced by foreign keys.
696
        // To avoid this, we must drop all referential foreign keys and recreate them later
697
        const referencedForeignKeySql = `SELECT * FROM "SYS"."REFERENTIAL_CONSTRAINTS" WHERE "REFERENCED_SCHEMA_NAME" = '${schemaName}' AND "REFERENCED_TABLE_NAME" = '${oldTableName}'`
×
698
        const dbForeignKeys: ObjectLiteral[] = await this.query(
×
699
            referencedForeignKeySql,
700
        )
701
        let referencedForeignKeys: TableForeignKey[] = []
×
702
        const referencedForeignKeyTableMapping: {
703
            tableName: string
704
            fkName: string
705
        }[] = []
×
706
        if (dbForeignKeys.length > 0) {
×
707
            referencedForeignKeys = dbForeignKeys.map((dbForeignKey) => {
×
708
                const foreignKeys = dbForeignKeys.filter(
×
709
                    (dbFk) =>
710
                        dbFk["CONSTRAINT_NAME"] ===
×
711
                        dbForeignKey["CONSTRAINT_NAME"],
712
                )
713

714
                referencedForeignKeyTableMapping.push({
×
715
                    tableName: `${dbForeignKey["SCHEMA_NAME"]}.${dbForeignKey["TABLE_NAME"]}`,
716
                    fkName: dbForeignKey["CONSTRAINT_NAME"],
717
                })
718
                return new TableForeignKey({
×
719
                    name: dbForeignKey["CONSTRAINT_NAME"],
720
                    columnNames: foreignKeys.map((dbFk) => dbFk["COLUMN_NAME"]),
×
721
                    referencedDatabase: newTable.database,
722
                    referencedSchema: newTable.schema,
723
                    referencedTableName: newTable.name, // we use renamed table name
724
                    referencedColumnNames: foreignKeys.map(
725
                        (dbFk) => dbFk["REFERENCED_COLUMN_NAME"],
×
726
                    ),
727
                    onDelete:
728
                        dbForeignKey["DELETE_RULE"] === "RESTRICT"
×
729
                            ? "NO ACTION"
730
                            : dbForeignKey["DELETE_RULE"],
731
                    onUpdate:
732
                        dbForeignKey["UPDATE_RULE"] === "RESTRICT"
×
733
                            ? "NO ACTION"
734
                            : dbForeignKey["UPDATE_RULE"],
735
                    deferrable: dbForeignKey["CHECK_TIME"].replace("_", " "), // "CHECK_TIME" is "INITIALLY_IMMEDIATE" or "INITIALLY DEFERRED"
736
                })
737
            })
738

739
            // drop referenced foreign keys
740
            referencedForeignKeys.forEach((foreignKey) => {
×
741
                const mapping = referencedForeignKeyTableMapping.find(
×
742
                    (it) => it.fkName === foreignKey.name,
×
743
                )
744
                upQueries.push(
×
745
                    this.dropForeignKeySql(mapping!.tableName, foreignKey),
746
                )
747
                downQueries.push(
×
748
                    this.createForeignKeySql(mapping!.tableName, foreignKey),
749
                )
750
            })
751
        }
752

753
        // rename primary key constraint
754
        if (newTable.primaryColumns.length > 0) {
×
755
            const columnNames = newTable.primaryColumns.map(
×
756
                (column) => column.name,
×
757
            )
758
            const columnNamesString = columnNames
×
759
                .map((columnName) => `"${columnName}"`)
×
760
                .join(", ")
761

762
            const oldPkName = this.connection.namingStrategy.primaryKeyName(
×
763
                oldTable,
764
                columnNames,
765
            )
766
            const newPkName = this.connection.namingStrategy.primaryKeyName(
×
767
                newTable,
768
                columnNames,
769
            )
770

771
            // drop old PK
772
            upQueries.push(
×
773
                new Query(
774
                    `ALTER TABLE ${this.escapePath(
775
                        newTable,
776
                    )} DROP CONSTRAINT "${oldPkName}"`,
777
                ),
778
            )
779
            downQueries.push(
×
780
                new Query(
781
                    `ALTER TABLE ${this.escapePath(
782
                        newTable,
783
                    )} ADD CONSTRAINT "${oldPkName}" PRIMARY KEY (${columnNamesString})`,
784
                ),
785
            )
786

787
            // create new PK
788
            upQueries.push(
×
789
                new Query(
790
                    `ALTER TABLE ${this.escapePath(
791
                        newTable,
792
                    )} ADD CONSTRAINT "${newPkName}" PRIMARY KEY (${columnNamesString})`,
793
                ),
794
            )
795
            downQueries.push(
×
796
                new Query(
797
                    `ALTER TABLE ${this.escapePath(
798
                        newTable,
799
                    )} DROP CONSTRAINT "${newPkName}"`,
800
                ),
801
            )
802
        }
803

804
        // recreate foreign keys with new constraint names
805
        newTable.foreignKeys.forEach((foreignKey) => {
×
806
            // replace constraint name
807
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
×
808
                newTable,
809
                foreignKey.columnNames,
810
                this.getTablePath(foreignKey),
811
                foreignKey.referencedColumnNames,
812
            )
813

814
            // create new FK's
815
            upQueries.push(this.createForeignKeySql(newTable, foreignKey))
×
816
            downQueries.push(this.dropForeignKeySql(newTable, foreignKey))
×
817
        })
818

819
        // restore referenced foreign keys
820
        referencedForeignKeys.forEach((foreignKey) => {
×
821
            const mapping = referencedForeignKeyTableMapping.find(
×
822
                (it) => it.fkName === foreignKey.name,
×
823
            )
824
            upQueries.push(
×
825
                this.createForeignKeySql(mapping!.tableName, foreignKey),
826
            )
827
            downQueries.push(
×
828
                this.dropForeignKeySql(mapping!.tableName, foreignKey),
829
            )
830
        })
831

832
        // rename index constraints
833
        newTable.indices.forEach((index) => {
×
834
            // build new constraint name
835
            const newIndexName = this.connection.namingStrategy.indexName(
×
836
                newTable,
837
                index.columnNames,
838
                index.where,
839
            )
840

841
            // drop old index
842
            upQueries.push(this.dropIndexSql(newTable, index))
×
843
            downQueries.push(this.createIndexSql(newTable, index))
×
844

845
            // replace constraint name
846
            index.name = newIndexName
×
847

848
            // create new index
849
            upQueries.push(this.createIndexSql(newTable, index))
×
850
            downQueries.push(this.dropIndexSql(newTable, index))
×
851
        })
852

853
        await this.executeQueries(upQueries, downQueries)
×
854

855
        // rename old table and replace it in cached tabled;
856
        oldTable.name = newTable.name
×
857
        this.replaceCachedTable(oldTable, newTable)
×
858
    }
859

860
    /**
861
     * Creates a new column from the column in the table.
862
     */
863
    async addColumn(
864
        tableOrName: Table | string,
865
        column: TableColumn,
866
    ): Promise<void> {
867
        const table = InstanceChecker.isTable(tableOrName)
×
868
            ? tableOrName
869
            : await this.getCachedTable(tableOrName)
870
        const parsedTableName = this.driver.parseTableName(table)
×
871

872
        if (!parsedTableName.schema) {
×
873
            parsedTableName.schema = await this.getCurrentSchema()
×
874
        }
875

876
        const clonedTable = table.clone()
×
877
        const upQueries: Query[] = []
×
878
        const downQueries: Query[] = []
×
879

880
        upQueries.push(new Query(this.addColumnSql(table, column)))
×
881
        downQueries.push(new Query(this.dropColumnSql(table, column)))
×
882

883
        // create or update primary key constraint
884
        if (column.isPrimary) {
×
885
            const primaryColumns = clonedTable.primaryColumns
×
886
            // if table already have primary key, me must drop it and recreate again
887
            if (primaryColumns.length > 0) {
×
888
                // SAP HANA does not allow to drop PK's which is referenced by foreign keys.
889
                // To avoid this, we must drop all referential foreign keys and recreate them later
890
                const referencedForeignKeySql = `SELECT * FROM "SYS"."REFERENTIAL_CONSTRAINTS" WHERE "REFERENCED_SCHEMA_NAME" = '${parsedTableName.schema}' AND "REFERENCED_TABLE_NAME" = '${parsedTableName.tableName}'`
×
891
                const dbForeignKeys: ObjectLiteral[] = await this.query(
×
892
                    referencedForeignKeySql,
893
                )
894
                let referencedForeignKeys: TableForeignKey[] = []
×
895
                const referencedForeignKeyTableMapping: {
896
                    tableName: string
897
                    fkName: string
898
                }[] = []
×
899
                if (dbForeignKeys.length > 0) {
×
900
                    referencedForeignKeys = dbForeignKeys.map(
×
901
                        (dbForeignKey) => {
902
                            const foreignKeys = dbForeignKeys.filter(
×
903
                                (dbFk) =>
904
                                    dbFk["CONSTRAINT_NAME"] ===
×
905
                                    dbForeignKey["CONSTRAINT_NAME"],
906
                            )
907

908
                            referencedForeignKeyTableMapping.push({
×
909
                                tableName: `${dbForeignKey["SCHEMA_NAME"]}.${dbForeignKey["TABLE_NAME"]}`,
910
                                fkName: dbForeignKey["CONSTRAINT_NAME"],
911
                            })
912
                            return new TableForeignKey({
×
913
                                name: dbForeignKey["CONSTRAINT_NAME"],
914
                                columnNames: foreignKeys.map(
915
                                    (dbFk) => dbFk["COLUMN_NAME"],
×
916
                                ),
917
                                referencedDatabase: table.database,
918
                                referencedSchema: table.schema,
919
                                referencedTableName: table.name,
920
                                referencedColumnNames: foreignKeys.map(
921
                                    (dbFk) => dbFk["REFERENCED_COLUMN_NAME"],
×
922
                                ),
923
                                onDelete:
924
                                    dbForeignKey["DELETE_RULE"] === "RESTRICT"
×
925
                                        ? "NO ACTION"
926
                                        : dbForeignKey["DELETE_RULE"],
927
                                onUpdate:
928
                                    dbForeignKey["UPDATE_RULE"] === "RESTRICT"
×
929
                                        ? "NO ACTION"
930
                                        : dbForeignKey["UPDATE_RULE"],
931
                                deferrable: dbForeignKey["CHECK_TIME"].replace(
932
                                    "_",
933
                                    " ",
934
                                ),
935
                            })
936
                        },
937
                    )
938

939
                    // drop referenced foreign keys
940
                    referencedForeignKeys.forEach((foreignKey) => {
×
941
                        const mapping = referencedForeignKeyTableMapping.find(
×
942
                            (it) => it.fkName === foreignKey.name,
×
943
                        )
944
                        upQueries.push(
×
945
                            this.dropForeignKeySql(
946
                                mapping!.tableName,
947
                                foreignKey,
948
                            ),
949
                        )
950
                        downQueries.push(
×
951
                            this.createForeignKeySql(
952
                                mapping!.tableName,
953
                                foreignKey,
954
                            ),
955
                        )
956
                    })
957
                }
958

959
                const pkName = this.connection.namingStrategy.primaryKeyName(
×
960
                    clonedTable,
961
                    primaryColumns.map((column) => column.name),
×
962
                )
963
                const columnNames = primaryColumns
×
964
                    .map((column) => `"${column.name}"`)
×
965
                    .join(", ")
966
                upQueries.push(
×
967
                    new Query(
968
                        `ALTER TABLE ${this.escapePath(
969
                            table,
970
                        )} DROP CONSTRAINT "${pkName}"`,
971
                    ),
972
                )
973
                downQueries.push(
×
974
                    new Query(
975
                        `ALTER TABLE ${this.escapePath(
976
                            table,
977
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
978
                    ),
979
                )
980

981
                // restore referenced foreign keys
982
                referencedForeignKeys.forEach((foreignKey) => {
×
983
                    const mapping = referencedForeignKeyTableMapping.find(
×
984
                        (it) => it.fkName === foreignKey.name,
×
985
                    )
986
                    upQueries.push(
×
987
                        this.createForeignKeySql(
988
                            mapping!.tableName,
989
                            foreignKey,
990
                        ),
991
                    )
992
                    downQueries.push(
×
993
                        this.dropForeignKeySql(mapping!.tableName, foreignKey),
994
                    )
995
                })
996
            }
997

998
            primaryColumns.push(column)
×
999
            const pkName = this.connection.namingStrategy.primaryKeyName(
×
1000
                clonedTable,
1001
                primaryColumns.map((column) => column.name),
×
1002
            )
1003
            const columnNames = primaryColumns
×
1004
                .map((column) => `"${column.name}"`)
×
1005
                .join(", ")
1006
            upQueries.push(
×
1007
                new Query(
1008
                    `ALTER TABLE ${this.escapePath(
1009
                        table,
1010
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1011
                ),
1012
            )
1013
            downQueries.push(
×
1014
                new Query(
1015
                    `ALTER TABLE ${this.escapePath(
1016
                        table,
1017
                    )} DROP CONSTRAINT "${pkName}"`,
1018
                ),
1019
            )
1020
        }
1021

1022
        // create column index
1023
        const columnIndex = clonedTable.indices.find(
×
1024
            (index) =>
1025
                index.columnNames.length === 1 &&
×
1026
                index.columnNames[0] === column.name,
1027
        )
1028
        if (columnIndex) {
×
1029
            upQueries.push(this.createIndexSql(table, columnIndex))
×
1030
            downQueries.push(this.dropIndexSql(table, columnIndex))
×
1031
        } else if (column.isUnique) {
×
1032
            const uniqueIndex = new TableIndex({
×
1033
                name: this.connection.namingStrategy.indexName(table, [
1034
                    column.name,
1035
                ]),
1036
                columnNames: [column.name],
1037
                isUnique: true,
1038
            })
1039
            clonedTable.indices.push(uniqueIndex)
×
1040
            clonedTable.uniques.push(
×
1041
                new TableUnique({
1042
                    name: uniqueIndex.name,
1043
                    columnNames: uniqueIndex.columnNames,
1044
                }),
1045
            )
1046
            upQueries.push(this.createIndexSql(table, uniqueIndex))
×
1047
            downQueries.push(this.dropIndexSql(table, uniqueIndex))
×
1048
        }
1049

1050
        await this.executeQueries(upQueries, downQueries)
×
1051

1052
        clonedTable.addColumn(column)
×
1053
        this.replaceCachedTable(table, clonedTable)
×
1054
    }
1055

1056
    /**
1057
     * Creates a new columns from the column in the table.
1058
     */
1059
    async addColumns(
1060
        tableOrName: Table | string,
1061
        columns: TableColumn[],
1062
    ): Promise<void> {
1063
        for (const column of columns) {
×
1064
            await this.addColumn(tableOrName, column)
×
1065
        }
1066
    }
1067

1068
    /**
1069
     * Renames column in the given table.
1070
     */
1071
    async renameColumn(
1072
        tableOrName: Table | string,
1073
        oldTableColumnOrName: TableColumn | string,
1074
        newTableColumnOrName: TableColumn | string,
1075
    ): Promise<void> {
1076
        const table = InstanceChecker.isTable(tableOrName)
×
1077
            ? tableOrName
1078
            : await this.getCachedTable(tableOrName)
1079
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
×
1080
            ? oldTableColumnOrName
1081
            : table.columns.find((c) => c.name === oldTableColumnOrName)
×
1082
        if (!oldColumn)
×
1083
            throw new TypeORMError(
×
1084
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
1085
            )
1086

1087
        let newColumn: TableColumn | undefined = undefined
×
1088
        if (InstanceChecker.isTableColumn(newTableColumnOrName)) {
×
1089
            newColumn = newTableColumnOrName
×
1090
        } else {
1091
            newColumn = oldColumn.clone()
×
1092
            newColumn.name = newTableColumnOrName
×
1093
        }
1094

1095
        await this.changeColumn(table, oldColumn, newColumn)
×
1096
    }
1097

1098
    /**
1099
     * Changes a column in the table.
1100
     */
1101
    async changeColumn(
1102
        tableOrName: Table | string,
1103
        oldTableColumnOrName: TableColumn | string,
1104
        newColumn: TableColumn,
1105
    ): Promise<void> {
1106
        const table = InstanceChecker.isTable(tableOrName)
×
1107
            ? tableOrName
1108
            : await this.getCachedTable(tableOrName)
1109
        let clonedTable = table.clone()
×
1110
        const upQueries: Query[] = []
×
1111
        const downQueries: Query[] = []
×
1112

1113
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
×
1114
            ? oldTableColumnOrName
1115
            : table.columns.find(
1116
                  (column) => column.name === oldTableColumnOrName,
×
1117
              )
1118
        if (!oldColumn)
×
1119
            throw new TypeORMError(
×
1120
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
1121
            )
1122

1123
        if (
×
1124
            (newColumn.isGenerated !== oldColumn.isGenerated &&
×
1125
                newColumn.generationStrategy !== "uuid") ||
1126
            newColumn.type !== oldColumn.type ||
1127
            newColumn.length !== oldColumn.length
1128
        ) {
1129
            // SQL Server does not support changing of IDENTITY column, so we must drop column and recreate it again.
1130
            // Also, we recreate column if column type changed
1131
            await this.dropColumn(table, oldColumn)
×
1132
            await this.addColumn(table, newColumn)
×
1133

1134
            // update cloned table
1135
            clonedTable = table.clone()
×
1136
        } else {
1137
            if (newColumn.name !== oldColumn.name) {
×
1138
                // rename column
1139
                upQueries.push(
×
1140
                    new Query(
1141
                        `RENAME COLUMN ${this.escapePath(table)}."${
1142
                            oldColumn.name
1143
                        }" TO "${newColumn.name}"`,
1144
                    ),
1145
                )
1146
                downQueries.push(
×
1147
                    new Query(
1148
                        `RENAME COLUMN ${this.escapePath(table)}."${
1149
                            newColumn.name
1150
                        }" TO "${oldColumn.name}"`,
1151
                    ),
1152
                )
1153

1154
                if (oldColumn.isPrimary === true) {
×
1155
                    const primaryColumns = clonedTable.primaryColumns
×
1156

1157
                    // build old primary constraint name
1158
                    const columnNames = primaryColumns.map(
×
1159
                        (column) => column.name,
×
1160
                    )
1161
                    const oldPkName =
1162
                        this.connection.namingStrategy.primaryKeyName(
×
1163
                            clonedTable,
1164
                            columnNames,
1165
                        )
1166

1167
                    // replace old column name with new column name
1168
                    columnNames.splice(columnNames.indexOf(oldColumn.name), 1)
×
1169
                    columnNames.push(newColumn.name)
×
1170
                    const columnNamesString = columnNames
×
1171
                        .map((columnName) => `"${columnName}"`)
×
1172
                        .join(", ")
1173

1174
                    // drop old PK
1175
                    upQueries.push(
×
1176
                        new Query(
1177
                            `ALTER TABLE ${this.escapePath(
1178
                                clonedTable,
1179
                            )} DROP CONSTRAINT "${oldPkName}"`,
1180
                        ),
1181
                    )
1182
                    downQueries.push(
×
1183
                        new Query(
1184
                            `ALTER TABLE ${this.escapePath(
1185
                                clonedTable,
1186
                            )} ADD CONSTRAINT "${oldPkName}" PRIMARY KEY (${columnNamesString})`,
1187
                        ),
1188
                    )
1189

1190
                    // build new primary constraint name
1191
                    const newPkName =
1192
                        this.connection.namingStrategy.primaryKeyName(
×
1193
                            clonedTable,
1194
                            columnNames,
1195
                        )
1196

1197
                    // create new PK
1198
                    upQueries.push(
×
1199
                        new Query(
1200
                            `ALTER TABLE ${this.escapePath(
1201
                                clonedTable,
1202
                            )} ADD CONSTRAINT "${newPkName}" PRIMARY KEY (${columnNamesString})`,
1203
                        ),
1204
                    )
1205
                    downQueries.push(
×
1206
                        new Query(
1207
                            `ALTER TABLE ${this.escapePath(
1208
                                clonedTable,
1209
                            )} DROP CONSTRAINT "${newPkName}"`,
1210
                        ),
1211
                    )
1212
                }
1213

1214
                // rename index constraints
1215
                clonedTable.findColumnIndices(oldColumn).forEach((index) => {
×
1216
                    // build new constraint name
1217
                    index.columnNames.splice(
×
1218
                        index.columnNames.indexOf(oldColumn.name),
1219
                        1,
1220
                    )
1221
                    index.columnNames.push(newColumn.name)
×
1222
                    const newIndexName =
1223
                        this.connection.namingStrategy.indexName(
×
1224
                            clonedTable,
1225
                            index.columnNames,
1226
                            index.where,
1227
                        )
1228

1229
                    // drop old index
1230
                    upQueries.push(this.dropIndexSql(clonedTable, index))
×
1231
                    downQueries.push(this.createIndexSql(clonedTable, index))
×
1232

1233
                    // replace constraint name
1234
                    index.name = newIndexName
×
1235

1236
                    // create new index
1237
                    upQueries.push(this.createIndexSql(clonedTable, index))
×
1238
                    downQueries.push(this.dropIndexSql(clonedTable, index))
×
1239
                })
1240

1241
                // rename foreign key constraints
1242
                clonedTable
×
1243
                    .findColumnForeignKeys(oldColumn)
1244
                    .forEach((foreignKey) => {
1245
                        // build new constraint name
1246
                        foreignKey.columnNames.splice(
×
1247
                            foreignKey.columnNames.indexOf(oldColumn.name),
1248
                            1,
1249
                        )
1250
                        foreignKey.columnNames.push(newColumn.name)
×
1251
                        const newForeignKeyName =
1252
                            this.connection.namingStrategy.foreignKeyName(
×
1253
                                clonedTable,
1254
                                foreignKey.columnNames,
1255
                                this.getTablePath(foreignKey),
1256
                                foreignKey.referencedColumnNames,
1257
                            )
1258

1259
                        upQueries.push(
×
1260
                            this.dropForeignKeySql(clonedTable, foreignKey),
1261
                        )
1262
                        downQueries.push(
×
1263
                            this.createForeignKeySql(clonedTable, foreignKey),
1264
                        )
1265

1266
                        // replace constraint name
1267
                        foreignKey.name = newForeignKeyName
×
1268

1269
                        // create new FK's
1270
                        upQueries.push(
×
1271
                            this.createForeignKeySql(clonedTable, foreignKey),
1272
                        )
1273
                        downQueries.push(
×
1274
                            this.dropForeignKeySql(clonedTable, foreignKey),
1275
                        )
1276
                    })
1277

1278
                // rename check constraints
1279
                clonedTable.findColumnChecks(oldColumn).forEach((check) => {
×
1280
                    // build new constraint name
1281
                    check.columnNames!.splice(
×
1282
                        check.columnNames!.indexOf(oldColumn.name),
1283
                        1,
1284
                    )
1285
                    check.columnNames!.push(newColumn.name)
×
1286
                    const newCheckName =
1287
                        this.connection.namingStrategy.checkConstraintName(
×
1288
                            clonedTable,
1289
                            check.expression!,
1290
                        )
1291

1292
                    upQueries.push(
×
1293
                        this.dropCheckConstraintSql(clonedTable, check),
1294
                    )
1295
                    downQueries.push(
×
1296
                        this.createCheckConstraintSql(clonedTable, check),
1297
                    )
1298

1299
                    // replace constraint name
1300
                    check.name = newCheckName
×
1301

1302
                    upQueries.push(
×
1303
                        this.createCheckConstraintSql(clonedTable, check),
1304
                    )
1305
                    downQueries.push(
×
1306
                        this.dropCheckConstraintSql(clonedTable, check),
1307
                    )
1308
                })
1309

1310
                // rename old column in the Table object
1311
                const oldTableColumn = clonedTable.columns.find(
×
1312
                    (column) => column.name === oldColumn.name,
×
1313
                )
1314
                clonedTable.columns[
×
1315
                    clonedTable.columns.indexOf(oldTableColumn!)
1316
                ].name = newColumn.name
1317
                oldColumn.name = newColumn.name
×
1318
            }
1319

1320
            if (this.isColumnChanged(oldColumn, newColumn, true)) {
×
1321
                upQueries.push(
×
1322
                    new Query(
1323
                        `ALTER TABLE ${this.escapePath(
1324
                            table,
1325
                        )} ALTER (${this.buildCreateColumnSql(
1326
                            newColumn,
1327
                            !(
1328
                                oldColumn.default === null ||
×
1329
                                oldColumn.default === undefined
1330
                            ),
1331
                            !oldColumn.isNullable,
1332
                        )})`,
1333
                    ),
1334
                )
1335
                downQueries.push(
×
1336
                    new Query(
1337
                        `ALTER TABLE ${this.escapePath(
1338
                            table,
1339
                        )} ALTER (${this.buildCreateColumnSql(
1340
                            oldColumn,
1341
                            !(
1342
                                newColumn.default === null ||
×
1343
                                newColumn.default === undefined
1344
                            ),
1345
                            !newColumn.isNullable,
1346
                        )})`,
1347
                    ),
1348
                )
1349
            } else if (oldColumn.comment !== newColumn.comment) {
×
1350
                upQueries.push(
×
1351
                    new Query(
1352
                        `COMMENT ON COLUMN ${this.escapePath(table)}."${
1353
                            oldColumn.name
1354
                        }" IS ${this.escapeComment(newColumn.comment)}`,
1355
                    ),
1356
                )
1357
                downQueries.push(
×
1358
                    new Query(
1359
                        `COMMENT ON COLUMN ${this.escapePath(table)}."${
1360
                            newColumn.name
1361
                        }" IS ${this.escapeComment(oldColumn.comment)}`,
1362
                    ),
1363
                )
1364
            }
1365

1366
            if (newColumn.isPrimary !== oldColumn.isPrimary) {
×
1367
                const primaryColumns = clonedTable.primaryColumns
×
1368

1369
                // if primary column state changed, we must always drop existed constraint.
1370
                if (primaryColumns.length > 0) {
×
1371
                    const pkName =
1372
                        this.connection.namingStrategy.primaryKeyName(
×
1373
                            clonedTable,
1374
                            primaryColumns.map((column) => column.name),
×
1375
                        )
1376
                    const columnNames = primaryColumns
×
1377
                        .map((column) => `"${column.name}"`)
×
1378
                        .join(", ")
1379
                    upQueries.push(
×
1380
                        new Query(
1381
                            `ALTER TABLE ${this.escapePath(
1382
                                table,
1383
                            )} DROP CONSTRAINT "${pkName}"`,
1384
                        ),
1385
                    )
1386
                    downQueries.push(
×
1387
                        new Query(
1388
                            `ALTER TABLE ${this.escapePath(
1389
                                table,
1390
                            )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1391
                        ),
1392
                    )
1393
                }
1394

1395
                if (newColumn.isPrimary === true) {
×
1396
                    primaryColumns.push(newColumn)
×
1397
                    // update column in table
1398
                    const column = clonedTable.columns.find(
×
1399
                        (column) => column.name === newColumn.name,
×
1400
                    )
1401
                    column!.isPrimary = true
×
1402
                    const pkName =
1403
                        this.connection.namingStrategy.primaryKeyName(
×
1404
                            clonedTable,
1405
                            primaryColumns.map((column) => column.name),
×
1406
                        )
1407
                    const columnNames = primaryColumns
×
1408
                        .map((column) => `"${column.name}"`)
×
1409
                        .join(", ")
1410
                    upQueries.push(
×
1411
                        new Query(
1412
                            `ALTER TABLE ${this.escapePath(
1413
                                table,
1414
                            )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1415
                        ),
1416
                    )
1417
                    downQueries.push(
×
1418
                        new Query(
1419
                            `ALTER TABLE ${this.escapePath(
1420
                                table,
1421
                            )} DROP CONSTRAINT "${pkName}"`,
1422
                        ),
1423
                    )
1424
                } else {
1425
                    const primaryColumn = primaryColumns.find(
×
1426
                        (c) => c.name === newColumn.name,
×
1427
                    )
1428
                    primaryColumns.splice(
×
1429
                        primaryColumns.indexOf(primaryColumn!),
1430
                        1,
1431
                    )
1432

1433
                    // update column in table
1434
                    const column = clonedTable.columns.find(
×
1435
                        (column) => column.name === newColumn.name,
×
1436
                    )
1437
                    column!.isPrimary = false
×
1438

1439
                    // if we have another primary keys, we must recreate constraint.
1440
                    if (primaryColumns.length > 0) {
×
1441
                        const pkName =
1442
                            this.connection.namingStrategy.primaryKeyName(
×
1443
                                clonedTable,
1444
                                primaryColumns.map((column) => column.name),
×
1445
                            )
1446
                        const columnNames = primaryColumns
×
1447
                            .map((column) => `"${column.name}"`)
×
1448
                            .join(", ")
1449
                        upQueries.push(
×
1450
                            new Query(
1451
                                `ALTER TABLE ${this.escapePath(
1452
                                    table,
1453
                                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1454
                            ),
1455
                        )
1456
                        downQueries.push(
×
1457
                            new Query(
1458
                                `ALTER TABLE ${this.escapePath(
1459
                                    table,
1460
                                )} DROP CONSTRAINT "${pkName}"`,
1461
                            ),
1462
                        )
1463
                    }
1464
                }
1465
            }
1466

1467
            if (newColumn.isUnique !== oldColumn.isUnique) {
×
1468
                if (newColumn.isUnique === true) {
×
1469
                    const uniqueIndex = new TableIndex({
×
1470
                        name: this.connection.namingStrategy.indexName(table, [
1471
                            newColumn.name,
1472
                        ]),
1473
                        columnNames: [newColumn.name],
1474
                        isUnique: true,
1475
                    })
1476
                    clonedTable.indices.push(uniqueIndex)
×
1477
                    clonedTable.uniques.push(
×
1478
                        new TableUnique({
1479
                            name: uniqueIndex.name,
1480
                            columnNames: uniqueIndex.columnNames,
1481
                        }),
1482
                    )
1483
                    upQueries.push(this.createIndexSql(table, uniqueIndex))
×
1484
                    downQueries.push(this.dropIndexSql(table, uniqueIndex))
×
1485
                } else {
1486
                    const uniqueIndex = clonedTable.indices.find((index) => {
×
1487
                        return (
×
1488
                            index.columnNames.length === 1 &&
×
1489
                            index.isUnique === true &&
1490
                            !!index.columnNames.find(
1491
                                (columnName) => columnName === newColumn.name,
×
1492
                            )
1493
                        )
1494
                    })
1495
                    clonedTable.indices.splice(
×
1496
                        clonedTable.indices.indexOf(uniqueIndex!),
1497
                        1,
1498
                    )
1499

1500
                    const tableUnique = clonedTable.uniques.find(
×
1501
                        (unique) => unique.name === uniqueIndex!.name,
×
1502
                    )
1503
                    clonedTable.uniques.splice(
×
1504
                        clonedTable.uniques.indexOf(tableUnique!),
1505
                        1,
1506
                    )
1507

1508
                    upQueries.push(this.dropIndexSql(table, uniqueIndex!))
×
1509
                    downQueries.push(this.createIndexSql(table, uniqueIndex!))
×
1510
                }
1511
            }
1512

1513
            await this.executeQueries(upQueries, downQueries)
×
1514
            this.replaceCachedTable(table, clonedTable)
×
1515
        }
1516
    }
1517

1518
    /**
1519
     * Changes a column in the table.
1520
     */
1521
    async changeColumns(
1522
        tableOrName: Table | string,
1523
        changedColumns: { newColumn: TableColumn; oldColumn: TableColumn }[],
1524
    ): Promise<void> {
1525
        for (const { oldColumn, newColumn } of changedColumns) {
×
1526
            await this.changeColumn(tableOrName, oldColumn, newColumn)
×
1527
        }
1528
    }
1529

1530
    /**
1531
     * Drops column in the table.
1532
     */
1533
    async dropColumn(
1534
        tableOrName: Table | string,
1535
        columnOrName: TableColumn | string,
1536
    ): Promise<void> {
1537
        const table = InstanceChecker.isTable(tableOrName)
×
1538
            ? tableOrName
1539
            : await this.getCachedTable(tableOrName)
1540
        const parsedTableName = this.driver.parseTableName(table)
×
1541

1542
        if (!parsedTableName.schema) {
×
1543
            parsedTableName.schema = await this.getCurrentSchema()
×
1544
        }
1545

1546
        const column = InstanceChecker.isTableColumn(columnOrName)
×
1547
            ? columnOrName
1548
            : table.findColumnByName(columnOrName)
1549
        if (!column)
×
1550
            throw new TypeORMError(
×
1551
                `Column "${columnOrName}" was not found in table "${table.name}"`,
1552
            )
1553

1554
        const clonedTable = table.clone()
×
1555
        const upQueries: Query[] = []
×
1556
        const downQueries: Query[] = []
×
1557

1558
        // drop primary key constraint
1559
        if (column.isPrimary) {
×
1560
            // SAP HANA does not allow to drop PK's which is referenced by foreign keys.
1561
            // To avoid this, we must drop all referential foreign keys and recreate them later
1562
            const referencedForeignKeySql = `SELECT * FROM "SYS"."REFERENTIAL_CONSTRAINTS" WHERE "REFERENCED_SCHEMA_NAME" = '${parsedTableName.schema}' AND "REFERENCED_TABLE_NAME" = '${parsedTableName.tableName}'`
×
1563
            const dbForeignKeys: ObjectLiteral[] = await this.query(
×
1564
                referencedForeignKeySql,
1565
            )
1566
            let referencedForeignKeys: TableForeignKey[] = []
×
1567
            const referencedForeignKeyTableMapping: {
1568
                tableName: string
1569
                fkName: string
1570
            }[] = []
×
1571
            if (dbForeignKeys.length > 0) {
×
1572
                referencedForeignKeys = dbForeignKeys.map((dbForeignKey) => {
×
1573
                    const foreignKeys = dbForeignKeys.filter(
×
1574
                        (dbFk) =>
1575
                            dbFk["CONSTRAINT_NAME"] ===
×
1576
                            dbForeignKey["CONSTRAINT_NAME"],
1577
                    )
1578

1579
                    referencedForeignKeyTableMapping.push({
×
1580
                        tableName: `${dbForeignKey["SCHEMA_NAME"]}.${dbForeignKey["TABLE_NAME"]}`,
1581
                        fkName: dbForeignKey["CONSTRAINT_NAME"],
1582
                    })
1583
                    return new TableForeignKey({
×
1584
                        name: dbForeignKey["CONSTRAINT_NAME"],
1585
                        columnNames: foreignKeys.map(
1586
                            (dbFk) => dbFk["COLUMN_NAME"],
×
1587
                        ),
1588
                        referencedDatabase: table.database,
1589
                        referencedSchema: table.schema,
1590
                        referencedTableName: table.name,
1591
                        referencedColumnNames: foreignKeys.map(
1592
                            (dbFk) => dbFk["REFERENCED_COLUMN_NAME"],
×
1593
                        ),
1594
                        onDelete:
1595
                            dbForeignKey["DELETE_RULE"] === "RESTRICT"
×
1596
                                ? "NO ACTION"
1597
                                : dbForeignKey["DELETE_RULE"],
1598
                        onUpdate:
1599
                            dbForeignKey["UPDATE_RULE"] === "RESTRICT"
×
1600
                                ? "NO ACTION"
1601
                                : dbForeignKey["UPDATE_RULE"],
1602
                        deferrable: dbForeignKey["CHECK_TIME"].replace(
1603
                            "_",
1604
                            " ",
1605
                        ),
1606
                    })
1607
                })
1608

1609
                // drop referenced foreign keys
1610
                referencedForeignKeys.forEach((foreignKey) => {
×
1611
                    const mapping = referencedForeignKeyTableMapping.find(
×
1612
                        (it) => it.fkName === foreignKey.name,
×
1613
                    )
1614
                    upQueries.push(
×
1615
                        this.dropForeignKeySql(mapping!.tableName, foreignKey),
1616
                    )
1617
                    downQueries.push(
×
1618
                        this.createForeignKeySql(
1619
                            mapping!.tableName,
1620
                            foreignKey,
1621
                        ),
1622
                    )
1623
                })
1624
            }
1625

1626
            const pkName = this.connection.namingStrategy.primaryKeyName(
×
1627
                clonedTable,
1628
                clonedTable.primaryColumns.map((column) => column.name),
×
1629
            )
1630
            const columnNames = clonedTable.primaryColumns
×
1631
                .map((primaryColumn) => `"${primaryColumn.name}"`)
×
1632
                .join(", ")
1633
            upQueries.push(
×
1634
                new Query(
1635
                    `ALTER TABLE ${this.escapePath(
1636
                        clonedTable,
1637
                    )} DROP CONSTRAINT "${pkName}"`,
1638
                ),
1639
            )
1640
            downQueries.push(
×
1641
                new Query(
1642
                    `ALTER TABLE ${this.escapePath(
1643
                        clonedTable,
1644
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1645
                ),
1646
            )
1647

1648
            // update column in table
1649
            const tableColumn = clonedTable.findColumnByName(column.name)
×
1650
            tableColumn!.isPrimary = false
×
1651

1652
            // if primary key have multiple columns, we must recreate it without dropped column
1653
            if (clonedTable.primaryColumns.length > 0) {
×
1654
                const pkName = this.connection.namingStrategy.primaryKeyName(
×
1655
                    clonedTable,
1656
                    clonedTable.primaryColumns.map((column) => column.name),
×
1657
                )
1658
                const columnNames = clonedTable.primaryColumns
×
1659
                    .map((primaryColumn) => `"${primaryColumn.name}"`)
×
1660
                    .join(", ")
1661
                upQueries.push(
×
1662
                    new Query(
1663
                        `ALTER TABLE ${this.escapePath(
1664
                            clonedTable,
1665
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1666
                    ),
1667
                )
1668
                downQueries.push(
×
1669
                    new Query(
1670
                        `ALTER TABLE ${this.escapePath(
1671
                            clonedTable,
1672
                        )} DROP CONSTRAINT "${pkName}"`,
1673
                    ),
1674
                )
1675
            }
1676

1677
            // restore referenced foreign keys
1678
            referencedForeignKeys.forEach((foreignKey) => {
×
1679
                const mapping = referencedForeignKeyTableMapping.find(
×
1680
                    (it) => it.fkName === foreignKey.name,
×
1681
                )
1682
                upQueries.push(
×
1683
                    this.createForeignKeySql(mapping!.tableName, foreignKey),
1684
                )
1685
                downQueries.push(
×
1686
                    this.dropForeignKeySql(mapping!.tableName, foreignKey),
1687
                )
1688
            })
1689
        }
1690

1691
        // drop column index
1692
        const columnIndex = clonedTable.indices.find(
×
1693
            (index) =>
1694
                index.columnNames.length === 1 &&
×
1695
                index.columnNames[0] === column.name,
1696
        )
1697
        if (columnIndex) {
×
1698
            clonedTable.indices.splice(
×
1699
                clonedTable.indices.indexOf(columnIndex),
1700
                1,
1701
            )
1702
            upQueries.push(this.dropIndexSql(table, columnIndex))
×
1703
            downQueries.push(this.createIndexSql(table, columnIndex))
×
1704
        } else if (column.isUnique) {
×
1705
            // we splice constraints both from table uniques and indices.
1706
            const uniqueName =
1707
                this.connection.namingStrategy.uniqueConstraintName(table, [
×
1708
                    column.name,
1709
                ])
1710
            const foundUnique = clonedTable.uniques.find(
×
1711
                (unique) => unique.name === uniqueName,
×
1712
            )
1713
            if (foundUnique) {
×
1714
                clonedTable.uniques.splice(
×
1715
                    clonedTable.uniques.indexOf(foundUnique),
1716
                    1,
1717
                )
1718
                upQueries.push(this.dropIndexSql(table, uniqueName))
×
1719
                downQueries.push(
×
1720
                    new Query(
1721
                        `CREATE UNIQUE INDEX "${uniqueName}" ON ${this.escapePath(
1722
                            table,
1723
                        )} ("${column.name}")`,
1724
                    ),
1725
                )
1726
            }
1727

1728
            const indexName = this.connection.namingStrategy.indexName(table, [
×
1729
                column.name,
1730
            ])
1731
            const foundIndex = clonedTable.indices.find(
×
1732
                (index) => index.name === indexName,
×
1733
            )
1734
            if (foundIndex) {
×
1735
                clonedTable.indices.splice(
×
1736
                    clonedTable.indices.indexOf(foundIndex),
1737
                    1,
1738
                )
1739
                upQueries.push(this.dropIndexSql(table, indexName))
×
1740
                downQueries.push(
×
1741
                    new Query(
1742
                        `CREATE UNIQUE INDEX "${indexName}" ON ${this.escapePath(
1743
                            table,
1744
                        )} ("${column.name}")`,
1745
                    ),
1746
                )
1747
            }
1748
        }
1749

1750
        // drop column check
1751
        const columnCheck = clonedTable.checks.find(
×
1752
            (check) =>
1753
                !!check.columnNames &&
×
1754
                check.columnNames.length === 1 &&
1755
                check.columnNames[0] === column.name,
1756
        )
1757
        if (columnCheck) {
×
1758
            clonedTable.checks.splice(
×
1759
                clonedTable.checks.indexOf(columnCheck),
1760
                1,
1761
            )
1762
            upQueries.push(this.dropCheckConstraintSql(table, columnCheck))
×
1763
            downQueries.push(this.createCheckConstraintSql(table, columnCheck))
×
1764
        }
1765

1766
        upQueries.push(new Query(this.dropColumnSql(table, column)))
×
1767
        downQueries.push(new Query(this.addColumnSql(table, column)))
×
1768

1769
        await this.executeQueries(upQueries, downQueries)
×
1770

1771
        clonedTable.removeColumn(column)
×
1772
        this.replaceCachedTable(table, clonedTable)
×
1773
    }
1774

1775
    /**
1776
     * Drops the columns in the table.
1777
     */
1778
    async dropColumns(
1779
        tableOrName: Table | string,
1780
        columns: TableColumn[] | string[],
1781
    ): Promise<void> {
1782
        for (const column of columns) {
×
1783
            await this.dropColumn(tableOrName, column)
×
1784
        }
1785
    }
1786

1787
    /**
1788
     * Creates a new primary key.
1789
     */
1790
    async createPrimaryKey(
1791
        tableOrName: Table | string,
1792
        columnNames: string[],
1793
    ): Promise<void> {
1794
        const table = InstanceChecker.isTable(tableOrName)
×
1795
            ? tableOrName
1796
            : await this.getCachedTable(tableOrName)
1797
        const clonedTable = table.clone()
×
1798

1799
        const up = this.createPrimaryKeySql(table, columnNames)
×
1800

1801
        // mark columns as primary, because dropPrimaryKeySql build constraint name from table primary column names.
1802
        clonedTable.columns.forEach((column) => {
×
1803
            if (columnNames.find((columnName) => columnName === column.name))
×
1804
                column.isPrimary = true
×
1805
        })
1806
        const down = this.dropPrimaryKeySql(clonedTable)
×
1807

1808
        await this.executeQueries(up, down)
×
1809
        this.replaceCachedTable(table, clonedTable)
×
1810
    }
1811

1812
    /**
1813
     * Updates composite primary keys.
1814
     */
1815
    async updatePrimaryKeys(
1816
        tableOrName: Table | string,
1817
        columns: TableColumn[],
1818
    ): Promise<void> {
1819
        const table = InstanceChecker.isTable(tableOrName)
×
1820
            ? tableOrName
1821
            : await this.getCachedTable(tableOrName)
1822
        const parsedTableName = this.driver.parseTableName(table)
×
1823

1824
        if (!parsedTableName.schema) {
×
1825
            parsedTableName.schema = await this.getCurrentSchema()
×
1826
        }
1827

1828
        const clonedTable = table.clone()
×
1829
        const columnNames = columns.map((column) => column.name)
×
1830
        const upQueries: Query[] = []
×
1831
        const downQueries: Query[] = []
×
1832

1833
        // SAP HANA does not allow to drop PK's which is referenced by foreign keys.
1834
        // To avoid this, we must drop all referential foreign keys and recreate them later
1835
        const referencedForeignKeySql = `SELECT * FROM "SYS"."REFERENTIAL_CONSTRAINTS" WHERE "REFERENCED_SCHEMA_NAME" = '${parsedTableName.schema}' AND "REFERENCED_TABLE_NAME" = '${parsedTableName.tableName}'`
×
1836
        const dbForeignKeys: ObjectLiteral[] = await this.query(
×
1837
            referencedForeignKeySql,
1838
        )
1839
        let referencedForeignKeys: TableForeignKey[] = []
×
1840
        const referencedForeignKeyTableMapping: {
1841
            tableName: string
1842
            fkName: string
1843
        }[] = []
×
1844
        if (dbForeignKeys.length > 0) {
×
1845
            referencedForeignKeys = dbForeignKeys.map((dbForeignKey) => {
×
1846
                const foreignKeys = dbForeignKeys.filter(
×
1847
                    (dbFk) =>
1848
                        dbFk["CONSTRAINT_NAME"] ===
×
1849
                        dbForeignKey["CONSTRAINT_NAME"],
1850
                )
1851

1852
                referencedForeignKeyTableMapping.push({
×
1853
                    tableName: `${dbForeignKey["SCHEMA_NAME"]}.${dbForeignKey["TABLE_NAME"]}`,
1854
                    fkName: dbForeignKey["CONSTRAINT_NAME"],
1855
                })
1856
                return new TableForeignKey({
×
1857
                    name: dbForeignKey["CONSTRAINT_NAME"],
1858
                    columnNames: foreignKeys.map((dbFk) => dbFk["COLUMN_NAME"]),
×
1859
                    referencedDatabase: table.database,
1860
                    referencedSchema: table.schema,
1861
                    referencedTableName: table.name,
1862
                    referencedColumnNames: foreignKeys.map(
1863
                        (dbFk) => dbFk["REFERENCED_COLUMN_NAME"],
×
1864
                    ),
1865
                    onDelete:
1866
                        dbForeignKey["DELETE_RULE"] === "RESTRICT"
×
1867
                            ? "NO ACTION"
1868
                            : dbForeignKey["DELETE_RULE"],
1869
                    onUpdate:
1870
                        dbForeignKey["UPDATE_RULE"] === "RESTRICT"
×
1871
                            ? "NO ACTION"
1872
                            : dbForeignKey["UPDATE_RULE"],
1873
                    deferrable: dbForeignKey["CHECK_TIME"].replace("_", " "),
1874
                })
1875
            })
1876

1877
            // drop referenced foreign keys
1878
            referencedForeignKeys.forEach((foreignKey) => {
×
1879
                const mapping = referencedForeignKeyTableMapping.find(
×
1880
                    (it) => it.fkName === foreignKey.name,
×
1881
                )
1882
                upQueries.push(
×
1883
                    this.dropForeignKeySql(mapping!.tableName, foreignKey),
1884
                )
1885
                downQueries.push(
×
1886
                    this.createForeignKeySql(mapping!.tableName, foreignKey),
1887
                )
1888
            })
1889
        }
1890

1891
        // if table already have primary columns, we must drop them.
1892
        const primaryColumns = clonedTable.primaryColumns
×
1893
        if (primaryColumns.length > 0) {
×
1894
            const pkName = this.connection.namingStrategy.primaryKeyName(
×
1895
                clonedTable,
1896
                primaryColumns.map((column) => column.name),
×
1897
            )
1898
            const columnNamesString = primaryColumns
×
1899
                .map((column) => `"${column.name}"`)
×
1900
                .join(", ")
1901
            upQueries.push(
×
1902
                new Query(
1903
                    `ALTER TABLE ${this.escapePath(
1904
                        table,
1905
                    )} DROP CONSTRAINT "${pkName}"`,
1906
                ),
1907
            )
1908
            downQueries.push(
×
1909
                new Query(
1910
                    `ALTER TABLE ${this.escapePath(
1911
                        table,
1912
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
1913
                ),
1914
            )
1915
        }
1916

1917
        // update columns in table.
1918
        clonedTable.columns
×
1919
            .filter((column) => columnNames.indexOf(column.name) !== -1)
×
1920
            .forEach((column) => (column.isPrimary = true))
×
1921

1922
        const pkName = this.connection.namingStrategy.primaryKeyName(
×
1923
            clonedTable,
1924
            columnNames,
1925
        )
1926
        const columnNamesString = columnNames
×
1927
            .map((columnName) => `"${columnName}"`)
×
1928
            .join(", ")
1929
        upQueries.push(
×
1930
            new Query(
1931
                `ALTER TABLE ${this.escapePath(
1932
                    table,
1933
                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
1934
            ),
1935
        )
1936
        downQueries.push(
×
1937
            new Query(
1938
                `ALTER TABLE ${this.escapePath(
1939
                    table,
1940
                )} DROP CONSTRAINT "${pkName}"`,
1941
            ),
1942
        )
1943

1944
        // restore referenced foreign keys
1945
        referencedForeignKeys.forEach((foreignKey) => {
×
1946
            const mapping = referencedForeignKeyTableMapping.find(
×
1947
                (it) => it.fkName === foreignKey.name,
×
1948
            )
1949
            upQueries.push(
×
1950
                this.createForeignKeySql(mapping!.tableName, foreignKey),
1951
            )
1952
            downQueries.push(
×
1953
                this.dropForeignKeySql(mapping!.tableName, foreignKey),
1954
            )
1955
        })
1956

1957
        await this.executeQueries(upQueries, downQueries)
×
1958
        this.replaceCachedTable(table, clonedTable)
×
1959
    }
1960

1961
    /**
1962
     * Drops a primary key.
1963
     */
1964
    async dropPrimaryKey(tableOrName: Table | string): Promise<void> {
1965
        const table = InstanceChecker.isTable(tableOrName)
×
1966
            ? tableOrName
1967
            : await this.getCachedTable(tableOrName)
1968
        const parsedTableName = this.driver.parseTableName(table)
×
1969

1970
        if (!parsedTableName.schema) {
×
1971
            parsedTableName.schema = await this.getCurrentSchema()
×
1972
        }
1973

1974
        const upQueries: Query[] = []
×
1975
        const downQueries: Query[] = []
×
1976

1977
        // SAP HANA does not allow to drop PK's which is referenced by foreign keys.
1978
        // To avoid this, we must drop all referential foreign keys and recreate them later
1979
        const referencedForeignKeySql = `SELECT * FROM "SYS"."REFERENTIAL_CONSTRAINTS" WHERE "REFERENCED_SCHEMA_NAME" = '${parsedTableName.schema}' AND "REFERENCED_TABLE_NAME" = '${parsedTableName.tableName}'`
×
1980
        const dbForeignKeys: ObjectLiteral[] = await this.query(
×
1981
            referencedForeignKeySql,
1982
        )
1983
        let referencedForeignKeys: TableForeignKey[] = []
×
1984
        const referencedForeignKeyTableMapping: {
1985
            tableName: string
1986
            fkName: string
1987
        }[] = []
×
1988
        if (dbForeignKeys.length > 0) {
×
1989
            referencedForeignKeys = dbForeignKeys.map((dbForeignKey) => {
×
1990
                const foreignKeys = dbForeignKeys.filter(
×
1991
                    (dbFk) =>
1992
                        dbFk["CONSTRAINT_NAME"] ===
×
1993
                        dbForeignKey["CONSTRAINT_NAME"],
1994
                )
1995

1996
                referencedForeignKeyTableMapping.push({
×
1997
                    tableName: `${dbForeignKey["SCHEMA_NAME"]}.${dbForeignKey["TABLE_NAME"]}`,
1998
                    fkName: dbForeignKey["CONSTRAINT_NAME"],
1999
                })
2000
                return new TableForeignKey({
×
2001
                    name: dbForeignKey["CONSTRAINT_NAME"],
2002
                    columnNames: foreignKeys.map((dbFk) => dbFk["COLUMN_NAME"]),
×
2003
                    referencedDatabase: table.database,
2004
                    referencedSchema: table.schema,
2005
                    referencedTableName: table.name,
2006
                    referencedColumnNames: foreignKeys.map(
2007
                        (dbFk) => dbFk["REFERENCED_COLUMN_NAME"],
×
2008
                    ),
2009
                    onDelete:
2010
                        dbForeignKey["DELETE_RULE"] === "RESTRICT"
×
2011
                            ? "NO ACTION"
2012
                            : dbForeignKey["DELETE_RULE"],
2013
                    onUpdate:
2014
                        dbForeignKey["UPDATE_RULE"] === "RESTRICT"
×
2015
                            ? "NO ACTION"
2016
                            : dbForeignKey["UPDATE_RULE"],
2017
                    deferrable: dbForeignKey["CHECK_TIME"].replace("_", " "),
2018
                })
2019
            })
2020

2021
            // drop referenced foreign keys
2022
            referencedForeignKeys.forEach((foreignKey) => {
×
2023
                const mapping = referencedForeignKeyTableMapping.find(
×
2024
                    (it) => it.fkName === foreignKey.name,
×
2025
                )
2026
                upQueries.push(
×
2027
                    this.dropForeignKeySql(mapping!.tableName, foreignKey),
2028
                )
2029
                downQueries.push(
×
2030
                    this.createForeignKeySql(mapping!.tableName, foreignKey),
2031
                )
2032
            })
2033
        }
2034

2035
        upQueries.push(this.dropPrimaryKeySql(table))
×
2036
        downQueries.push(
×
2037
            this.createPrimaryKeySql(
2038
                table,
2039
                table.primaryColumns.map((column) => column.name),
×
2040
            ),
2041
        )
2042

2043
        // restore referenced foreign keys
2044
        referencedForeignKeys.forEach((foreignKey) => {
×
2045
            const mapping = referencedForeignKeyTableMapping.find(
×
2046
                (it) => it.fkName === foreignKey.name,
×
2047
            )
2048
            upQueries.push(
×
2049
                this.createForeignKeySql(mapping!.tableName, foreignKey),
2050
            )
2051
            downQueries.push(
×
2052
                this.dropForeignKeySql(mapping!.tableName, foreignKey),
2053
            )
2054
        })
2055

2056
        await this.executeQueries(upQueries, downQueries)
×
2057
        table.primaryColumns.forEach((column) => {
×
2058
            column.isPrimary = false
×
2059
        })
2060
    }
2061

2062
    /**
2063
     * Creates a new unique constraint.
2064
     */
2065
    async createUniqueConstraint(
2066
        tableOrName: Table | string,
2067
        uniqueConstraint: TableUnique,
2068
    ): Promise<void> {
2069
        throw new TypeORMError(
×
2070
            `SAP HANA does not support unique constraints. Use unique index instead.`,
2071
        )
2072
    }
2073

2074
    /**
2075
     * Creates a new unique constraints.
2076
     */
2077
    async createUniqueConstraints(
2078
        tableOrName: Table | string,
2079
        uniqueConstraints: TableUnique[],
2080
    ): Promise<void> {
2081
        throw new TypeORMError(
×
2082
            `SAP HANA does not support unique constraints. Use unique index instead.`,
2083
        )
2084
    }
2085

2086
    /**
2087
     * Drops unique constraint.
2088
     */
2089
    async dropUniqueConstraint(
2090
        tableOrName: Table | string,
2091
        uniqueOrName: TableUnique | string,
2092
    ): Promise<void> {
2093
        throw new TypeORMError(
×
2094
            `SAP HANA does not support unique constraints. Use unique index instead.`,
2095
        )
2096
    }
2097

2098
    /**
2099
     * Drops an unique constraints.
2100
     */
2101
    async dropUniqueConstraints(
2102
        tableOrName: Table | string,
2103
        uniqueConstraints: TableUnique[],
2104
    ): Promise<void> {
2105
        throw new TypeORMError(
×
2106
            `SAP HANA does not support unique constraints. Use unique index instead.`,
2107
        )
2108
    }
2109

2110
    /**
2111
     * Creates a new check constraint.
2112
     */
2113
    async createCheckConstraint(
2114
        tableOrName: Table | string,
2115
        checkConstraint: TableCheck,
2116
    ): Promise<void> {
2117
        const table = InstanceChecker.isTable(tableOrName)
×
2118
            ? tableOrName
2119
            : await this.getCachedTable(tableOrName)
2120

2121
        // new unique constraint may be passed without name. In this case we generate unique name manually.
2122
        if (!checkConstraint.name)
×
2123
            checkConstraint.name =
×
2124
                this.connection.namingStrategy.checkConstraintName(
2125
                    table,
2126
                    checkConstraint.expression!,
2127
                )
2128

2129
        const up = this.createCheckConstraintSql(table, checkConstraint)
×
2130
        const down = this.dropCheckConstraintSql(table, checkConstraint)
×
2131
        await this.executeQueries(up, down)
×
2132
        table.addCheckConstraint(checkConstraint)
×
2133
    }
2134

2135
    /**
2136
     * Creates a new check constraints.
2137
     */
2138
    async createCheckConstraints(
2139
        tableOrName: Table | string,
2140
        checkConstraints: TableCheck[],
2141
    ): Promise<void> {
2142
        const promises = checkConstraints.map((checkConstraint) =>
×
2143
            this.createCheckConstraint(tableOrName, checkConstraint),
×
2144
        )
2145
        await Promise.all(promises)
×
2146
    }
2147

2148
    /**
2149
     * Drops check constraint.
2150
     */
2151
    async dropCheckConstraint(
2152
        tableOrName: Table | string,
2153
        checkOrName: TableCheck | string,
2154
    ): Promise<void> {
2155
        const table = InstanceChecker.isTable(tableOrName)
×
2156
            ? tableOrName
2157
            : await this.getCachedTable(tableOrName)
2158
        const checkConstraint = InstanceChecker.isTableCheck(checkOrName)
×
2159
            ? checkOrName
2160
            : table.checks.find((c) => c.name === checkOrName)
×
2161
        if (!checkConstraint)
×
2162
            throw new TypeORMError(
×
2163
                `Supplied check constraint was not found in table ${table.name}`,
2164
            )
2165

2166
        const up = this.dropCheckConstraintSql(table, checkConstraint)
×
2167
        const down = this.createCheckConstraintSql(table, checkConstraint)
×
2168
        await this.executeQueries(up, down)
×
2169
        table.removeCheckConstraint(checkConstraint)
×
2170
    }
2171

2172
    /**
2173
     * Drops check constraints.
2174
     */
2175
    async dropCheckConstraints(
2176
        tableOrName: Table | string,
2177
        checkConstraints: TableCheck[],
2178
    ): Promise<void> {
2179
        const promises = checkConstraints.map((checkConstraint) =>
×
2180
            this.dropCheckConstraint(tableOrName, checkConstraint),
×
2181
        )
2182
        await Promise.all(promises)
×
2183
    }
2184

2185
    /**
2186
     * Creates a new exclusion constraint.
2187
     */
2188
    async createExclusionConstraint(
2189
        tableOrName: Table | string,
2190
        exclusionConstraint: TableExclusion,
2191
    ): Promise<void> {
2192
        throw new TypeORMError(
×
2193
            `SAP HANA does not support exclusion constraints.`,
2194
        )
2195
    }
2196

2197
    /**
2198
     * Creates a new exclusion constraints.
2199
     */
2200
    async createExclusionConstraints(
2201
        tableOrName: Table | string,
2202
        exclusionConstraints: TableExclusion[],
2203
    ): Promise<void> {
2204
        throw new TypeORMError(
×
2205
            `SAP HANA does not support exclusion constraints.`,
2206
        )
2207
    }
2208

2209
    /**
2210
     * Drops exclusion constraint.
2211
     */
2212
    async dropExclusionConstraint(
2213
        tableOrName: Table | string,
2214
        exclusionOrName: TableExclusion | string,
2215
    ): Promise<void> {
2216
        throw new TypeORMError(
×
2217
            `SAP HANA does not support exclusion constraints.`,
2218
        )
2219
    }
2220

2221
    /**
2222
     * Drops exclusion constraints.
2223
     */
2224
    async dropExclusionConstraints(
2225
        tableOrName: Table | string,
2226
        exclusionConstraints: TableExclusion[],
2227
    ): Promise<void> {
2228
        throw new TypeORMError(
×
2229
            `SAP HANA does not support exclusion constraints.`,
2230
        )
2231
    }
2232

2233
    /**
2234
     * Creates a new foreign key.
2235
     */
2236
    async createForeignKey(
2237
        tableOrName: Table | string,
2238
        foreignKey: TableForeignKey,
2239
    ): Promise<void> {
2240
        const table = InstanceChecker.isTable(tableOrName)
×
2241
            ? tableOrName
2242
            : await this.getCachedTable(tableOrName)
2243

2244
        // new FK may be passed without name. In this case we generate FK name manually.
2245
        if (!foreignKey.name)
×
2246
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
×
2247
                table,
2248
                foreignKey.columnNames,
2249
                this.getTablePath(foreignKey),
2250
                foreignKey.referencedColumnNames,
2251
            )
2252

2253
        const up = this.createForeignKeySql(table, foreignKey)
×
2254
        const down = this.dropForeignKeySql(table, foreignKey)
×
2255
        await this.executeQueries(up, down)
×
2256
        table.addForeignKey(foreignKey)
×
2257
    }
2258

2259
    /**
2260
     * Creates a new foreign keys.
2261
     */
2262
    async createForeignKeys(
2263
        tableOrName: Table | string,
2264
        foreignKeys: TableForeignKey[],
2265
    ): Promise<void> {
2266
        const promises = foreignKeys.map((foreignKey) =>
×
2267
            this.createForeignKey(tableOrName, foreignKey),
×
2268
        )
2269
        await Promise.all(promises)
×
2270
    }
2271

2272
    /**
2273
     * Drops a foreign key from the table.
2274
     */
2275
    async dropForeignKey(
2276
        tableOrName: Table | string,
2277
        foreignKeyOrName: TableForeignKey | string,
2278
    ): Promise<void> {
2279
        const table = InstanceChecker.isTable(tableOrName)
×
2280
            ? tableOrName
2281
            : await this.getCachedTable(tableOrName)
2282
        const foreignKey = InstanceChecker.isTableForeignKey(foreignKeyOrName)
×
2283
            ? foreignKeyOrName
2284
            : table.foreignKeys.find((fk) => fk.name === foreignKeyOrName)
×
2285
        if (!foreignKey)
×
2286
            throw new TypeORMError(
×
2287
                `Supplied foreign key was not found in table ${table.name}`,
2288
            )
2289

2290
        const up = this.dropForeignKeySql(table, foreignKey)
×
2291
        const down = this.createForeignKeySql(table, foreignKey)
×
2292
        await this.executeQueries(up, down)
×
2293
        table.removeForeignKey(foreignKey)
×
2294
    }
2295

2296
    /**
2297
     * Drops a foreign keys from the table.
2298
     */
2299
    async dropForeignKeys(
2300
        tableOrName: Table | string,
2301
        foreignKeys: TableForeignKey[],
2302
    ): Promise<void> {
2303
        const promises = foreignKeys.map((foreignKey) =>
×
2304
            this.dropForeignKey(tableOrName, foreignKey),
×
2305
        )
2306
        await Promise.all(promises)
×
2307
    }
2308

2309
    /**
2310
     * Creates a new index.
2311
     */
2312
    async createIndex(
2313
        tableOrName: Table | string,
2314
        index: TableIndex,
2315
    ): Promise<void> {
2316
        const table = InstanceChecker.isTable(tableOrName)
×
2317
            ? tableOrName
2318
            : await this.getCachedTable(tableOrName)
2319

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

2323
        const up = this.createIndexSql(table, index)
×
2324
        const down = this.dropIndexSql(table, index)
×
2325
        await this.executeQueries(up, down)
×
2326
        table.addIndex(index)
×
2327
    }
2328

2329
    /**
2330
     * Creates a new indices
2331
     */
2332
    async createIndices(
2333
        tableOrName: Table | string,
2334
        indices: TableIndex[],
2335
    ): Promise<void> {
2336
        const promises = indices.map((index) =>
×
2337
            this.createIndex(tableOrName, index),
×
2338
        )
2339
        await Promise.all(promises)
×
2340
    }
2341

2342
    /**
2343
     * Drops an index.
2344
     */
2345
    async dropIndex(
2346
        tableOrName: Table | string,
2347
        indexOrName: TableIndex | string,
2348
    ): Promise<void> {
2349
        const table = InstanceChecker.isTable(tableOrName)
×
2350
            ? tableOrName
2351
            : await this.getCachedTable(tableOrName)
2352
        const index = InstanceChecker.isTableIndex(indexOrName)
×
2353
            ? indexOrName
2354
            : table.indices.find((i) => i.name === indexOrName)
×
2355
        if (!index)
×
2356
            throw new TypeORMError(
×
2357
                `Supplied index ${indexOrName} was not found in table ${table.name}`,
2358
            )
2359

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

2363
        const up = this.dropIndexSql(table, index)
×
2364
        const down = this.createIndexSql(table, index)
×
2365
        await this.executeQueries(up, down)
×
2366
        table.removeIndex(index)
×
2367
    }
2368

2369
    /**
2370
     * Drops an indices from the table.
2371
     */
2372
    async dropIndices(
2373
        tableOrName: Table | string,
2374
        indices: TableIndex[],
2375
    ): Promise<void> {
2376
        const promises = indices.map((index) =>
×
2377
            this.dropIndex(tableOrName, index),
×
2378
        )
2379
        await Promise.all(promises)
×
2380
    }
2381

2382
    /**
2383
     * Clears all table contents.
2384
     * Note: this operation uses SQL's TRUNCATE query which cannot be reverted in transactions.
2385
     */
2386
    async clearTable(tablePath: string): Promise<void> {
2387
        await this.query(`TRUNCATE TABLE ${this.escapePath(tablePath)}`)
×
2388
    }
2389

2390
    /**
2391
     * Removes all tables from the currently connected database.
2392
     */
2393
    async clearDatabase(): Promise<void> {
2394
        const schemas: string[] = []
×
2395
        this.connection.entityMetadatas
×
2396
            .filter((metadata) => metadata.schema)
×
2397
            .forEach((metadata) => {
2398
                const isSchemaExist = !!schemas.find(
×
2399
                    (schema) => schema === metadata.schema,
×
2400
                )
2401
                if (!isSchemaExist) schemas.push(metadata.schema!)
×
2402
            })
2403

2404
        schemas.push(this.driver.options.schema || "current_schema")
×
2405
        const schemaNamesString = schemas
×
2406
            .map((name) => {
2407
                return name === "current_schema" ? name : "'" + name + "'"
×
2408
            })
2409
            .join(", ")
2410

2411
        const isAnotherTransactionActive = this.isTransactionActive
×
2412
        if (!isAnotherTransactionActive) await this.startTransaction()
×
2413
        try {
×
2414
            // const selectViewDropsQuery = `SELECT 'DROP VIEW IF EXISTS "' || schemaname || '"."' || viewname || '" CASCADE;' as "query" ` +
2415
            //     `FROM "pg_views" WHERE "schemaname" IN (${schemaNamesString}) AND "viewname" NOT IN ('geography_columns', 'geometry_columns', 'raster_columns', 'raster_overviews')`;
2416
            // const dropViewQueries: ObjectLiteral[] = await this.query(selectViewDropsQuery);
2417
            // await Promise.all(dropViewQueries.map(q => this.query(q["query"])));
2418

2419
            // ignore spatial_ref_sys; it's a special table supporting PostGIS
2420
            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'`
×
2421
            const dropTableQueries: ObjectLiteral[] = await this.query(
×
2422
                selectTableDropsQuery,
2423
            )
2424
            await Promise.all(
×
2425
                dropTableQueries.map((q) => this.query(q["query"])),
×
2426
            )
2427

2428
            if (!isAnotherTransactionActive) await this.commitTransaction()
×
2429
        } catch (error) {
2430
            try {
×
2431
                // we throw original error even if rollback thrown an error
2432
                if (!isAnotherTransactionActive)
×
2433
                    await this.rollbackTransaction()
×
2434
            } catch (rollbackError) {}
2435
            throw error
×
2436
        }
2437
    }
2438

2439
    // -------------------------------------------------------------------------
2440
    // Protected Methods
2441
    // -------------------------------------------------------------------------
2442

2443
    protected async loadViews(viewNames?: string[]): Promise<View[]> {
2444
        const hasTable = await this.hasTable(this.getTypeormMetadataTableName())
×
2445
        if (!hasTable) {
×
2446
            return []
×
2447
        }
2448

2449
        if (!viewNames) {
×
2450
            viewNames = []
×
2451
        }
2452

2453
        const currentDatabase = await this.getCurrentDatabase()
×
2454
        const currentSchema = await this.getCurrentSchema()
×
2455

2456
        const viewsCondition = viewNames
×
2457
            .map((viewName) => {
2458
                let { schema, tableName: name } =
2459
                    this.driver.parseTableName(viewName)
×
2460

2461
                if (!schema) {
×
2462
                    schema = currentSchema
×
2463
                }
2464

2465
                return `("t"."schema" = '${schema}' AND "t"."name" = '${name}')`
×
2466
            })
2467
            .join(" OR ")
2468

2469
        const query = `SELECT "t".* FROM ${this.escapePath(
×
2470
            this.getTypeormMetadataTableName(),
2471
        )} "t" WHERE "t"."type" = '${MetadataTableType.VIEW}' ${
2472
            viewsCondition ? `AND (${viewsCondition})` : ""
×
2473
        }`
2474
        const dbViews = await this.query(query)
×
2475
        return dbViews.map((dbView: any) => {
×
2476
            const view = new View()
×
2477
            const schema =
2478
                dbView["schema"] === currentSchema &&
×
2479
                !this.driver.options.schema
2480
                    ? undefined
2481
                    : dbView["schema"]
2482
            view.database = currentDatabase
×
2483
            view.schema = dbView["schema"]
×
2484
            view.name = this.driver.buildTableName(dbView["name"], schema)
×
2485
            view.expression = dbView["value"]
×
2486
            return view
×
2487
        })
2488
    }
2489

2490
    /**
2491
     * Loads all tables (with given names) from the database and creates a Table from them.
2492
     */
2493
    protected async loadTables(tableNames?: string[]): Promise<Table[]> {
2494
        if (tableNames && tableNames.length === 0) {
×
2495
            return []
×
2496
        }
2497

2498
        const currentSchema = await this.getCurrentSchema()
×
2499
        const currentDatabase = await this.getCurrentDatabase()
×
2500

2501
        const dbTables: { SCHEMA_NAME: string; TABLE_NAME: string }[] = []
×
2502

2503
        if (!tableNames) {
×
2504
            const tablesSql = `SELECT "SCHEMA_NAME", "TABLE_NAME" FROM "SYS"."TABLES"`
×
2505

2506
            dbTables.push(...(await this.query(tablesSql)))
×
2507
        } else {
2508
            const tablesCondition = tableNames
×
2509
                .map((tableName) => {
2510
                    let [schema, name] = tableName.split(".")
×
2511
                    if (!name) {
×
2512
                        name = schema
×
2513
                        schema = this.driver.options.schema || currentSchema
×
2514
                    }
2515
                    return `("SCHEMA_NAME" = '${schema}' AND "TABLE_NAME" = '${name}')`
×
2516
                })
2517
                .join(" OR ")
2518

2519
            const tablesSql =
2520
                `SELECT "SCHEMA_NAME", "TABLE_NAME" FROM "SYS"."TABLES" WHERE ` +
×
2521
                tablesCondition
2522

2523
            dbTables.push(...(await this.query(tablesSql)))
×
2524
        }
2525

2526
        // if tables were not found in the db, no need to proceed
2527
        if (dbTables.length === 0) return []
×
2528

2529
        const columnsCondition = dbTables
×
2530
            .map(({ SCHEMA_NAME, TABLE_NAME }) => {
2531
                return `("SCHEMA_NAME" = '${SCHEMA_NAME}' AND "TABLE_NAME" = '${TABLE_NAME}')`
×
2532
            })
2533
            .join(" OR ")
2534
        const columnsSql =
2535
            `SELECT * FROM "SYS"."TABLE_COLUMNS" WHERE ` +
×
2536
            columnsCondition +
2537
            ` ORDER BY "POSITION"`
2538

2539
        const constraintsCondition = dbTables
×
2540
            .map(({ SCHEMA_NAME, TABLE_NAME }) => {
2541
                return `("SCHEMA_NAME" = '${SCHEMA_NAME}' AND "TABLE_NAME" = '${TABLE_NAME}')`
×
2542
            })
2543
            .join(" OR ")
2544
        const constraintsSql = `SELECT * FROM "SYS"."CONSTRAINTS" WHERE (${constraintsCondition}) ORDER BY "POSITION"`
×
2545

2546
        const indicesCondition = dbTables
×
2547
            .map(({ SCHEMA_NAME, TABLE_NAME }) => {
2548
                return `("I"."SCHEMA_NAME" = '${SCHEMA_NAME}' AND "I"."TABLE_NAME" = '${TABLE_NAME}')`
×
2549
            })
2550
            .join(" OR ")
2551
        // excluding primary key and autogenerated fulltext indices
2552
        const indicesSql =
2553
            `SELECT "I"."INDEX_TYPE", "I"."SCHEMA_NAME", "I"."TABLE_NAME", "I"."INDEX_NAME", "IC"."COLUMN_NAME", "I"."CONSTRAINT" ` +
×
2554
            `FROM "SYS"."INDEXES" "I" INNER JOIN "SYS"."INDEX_COLUMNS" "IC" ON "IC"."INDEX_OID" = "I"."INDEX_OID" ` +
2555
            `WHERE (${indicesCondition}) AND ("I"."CONSTRAINT" IS NULL OR "I"."CONSTRAINT" != 'PRIMARY KEY') AND "I"."INDEX_NAME" NOT LIKE '%_SYS_FULLTEXT_%' ORDER BY "IC"."POSITION"`
2556

2557
        const foreignKeysCondition = dbTables
×
2558
            .map(({ SCHEMA_NAME, TABLE_NAME }) => {
2559
                return `("SCHEMA_NAME" = '${SCHEMA_NAME}' AND "TABLE_NAME" = '${TABLE_NAME}')`
×
2560
            })
2561
            .join(" OR ")
2562
        const foreignKeysSql = `SELECT * FROM "SYS"."REFERENTIAL_CONSTRAINTS" WHERE (${foreignKeysCondition}) ORDER BY "POSITION"`
×
2563
        const [
2564
            dbColumns,
2565
            dbConstraints,
2566
            dbIndices,
2567
            dbForeignKeys,
2568
        ]: ObjectLiteral[][] = await Promise.all([
×
2569
            this.query(columnsSql),
2570
            this.query(constraintsSql),
2571
            this.query(indicesSql),
2572
            this.query(foreignKeysSql),
2573
        ])
2574

2575
        // create tables for loaded tables
2576
        return Promise.all(
×
2577
            dbTables.map(async (dbTable) => {
2578
                const table = new Table()
×
2579
                const getSchemaFromKey = (dbObject: any, key: string) => {
×
2580
                    return dbObject[key] === currentSchema &&
×
2581
                        (!this.driver.options.schema ||
2582
                            this.driver.options.schema === currentSchema)
2583
                        ? undefined
2584
                        : dbObject[key]
2585
                }
2586

2587
                // We do not need to join schema name, when database is by default.
2588
                const schema = getSchemaFromKey(dbTable, "SCHEMA_NAME")
×
2589
                table.database = currentDatabase
×
2590
                table.schema = dbTable["SCHEMA_NAME"]
×
2591
                table.name = this.driver.buildTableName(
×
2592
                    dbTable["TABLE_NAME"],
2593
                    schema,
2594
                )
2595

2596
                // create columns from the loaded columns
2597
                table.columns = await Promise.all(
×
2598
                    dbColumns
2599
                        .filter(
2600
                            (dbColumn) =>
2601
                                dbColumn["TABLE_NAME"] ===
×
2602
                                    dbTable["TABLE_NAME"] &&
2603
                                dbColumn["SCHEMA_NAME"] ===
2604
                                    dbTable["SCHEMA_NAME"],
2605
                        )
2606
                        .map(async (dbColumn) => {
2607
                            const columnConstraints = dbConstraints.filter(
×
2608
                                (dbConstraint) =>
2609
                                    dbConstraint["TABLE_NAME"] ===
×
2610
                                        dbColumn["TABLE_NAME"] &&
2611
                                    dbConstraint["SCHEMA_NAME"] ===
2612
                                        dbColumn["SCHEMA_NAME"] &&
2613
                                    dbConstraint["COLUMN_NAME"] ===
2614
                                        dbColumn["COLUMN_NAME"],
2615
                            )
2616

2617
                            const columnUniqueIndices = dbIndices.filter(
×
2618
                                (dbIndex) => {
2619
                                    return (
×
2620
                                        dbIndex["TABLE_NAME"] ===
×
2621
                                            dbTable["TABLE_NAME"] &&
2622
                                        dbIndex["SCHEMA_NAME"] ===
2623
                                            dbTable["SCHEMA_NAME"] &&
2624
                                        dbIndex["COLUMN_NAME"] ===
2625
                                            dbColumn["COLUMN_NAME"] &&
2626
                                        dbIndex["CONSTRAINT"] &&
2627
                                        dbIndex["CONSTRAINT"].indexOf(
2628
                                            "UNIQUE",
2629
                                        ) !== -1
2630
                                    )
2631
                                },
2632
                            )
2633

2634
                            const tableMetadata =
2635
                                this.connection.entityMetadatas.find(
×
2636
                                    (metadata) =>
2637
                                        this.getTablePath(table) ===
×
2638
                                        this.getTablePath(metadata),
2639
                                )
2640
                            const hasIgnoredIndex =
2641
                                columnUniqueIndices.length > 0 &&
×
2642
                                tableMetadata &&
2643
                                tableMetadata.indices.some((index) => {
2644
                                    return columnUniqueIndices.some(
×
2645
                                        (uniqueIndex) => {
2646
                                            return (
×
2647
                                                index.name ===
×
2648
                                                    uniqueIndex["INDEX_NAME"] &&
2649
                                                index.synchronize === false
2650
                                            )
2651
                                        },
2652
                                    )
2653
                                })
2654

2655
                            const isConstraintComposite =
2656
                                columnUniqueIndices.every((uniqueIndex) => {
×
2657
                                    return dbIndices.some(
×
2658
                                        (dbIndex) =>
2659
                                            dbIndex["INDEX_NAME"] ===
×
2660
                                                uniqueIndex["INDEX_NAME"] &&
2661
                                            dbIndex["COLUMN_NAME"] !==
2662
                                                dbColumn["COLUMN_NAME"],
2663
                                    )
2664
                                })
2665

2666
                            const tableColumn = new TableColumn()
×
2667
                            tableColumn.name = dbColumn["COLUMN_NAME"]
×
2668
                            tableColumn.type =
×
2669
                                dbColumn["DATA_TYPE_NAME"].toLowerCase()
2670

2671
                            if (
×
2672
                                tableColumn.type === "dec" ||
×
2673
                                tableColumn.type === "decimal"
2674
                            ) {
2675
                                // If one of these properties was set, and another was not, Postgres sets '0' in to unspecified property
2676
                                // we set 'undefined' in to unspecified property to avoid changing column on sync
2677
                                if (
×
2678
                                    dbColumn["LENGTH"] !== null &&
×
2679
                                    !this.isDefaultColumnPrecision(
2680
                                        table,
2681
                                        tableColumn,
2682
                                        dbColumn["LENGTH"],
2683
                                    )
2684
                                ) {
2685
                                    tableColumn.precision = dbColumn["LENGTH"]
×
2686
                                } else if (
×
2687
                                    dbColumn["SCALE"] !== null &&
×
2688
                                    !this.isDefaultColumnScale(
2689
                                        table,
2690
                                        tableColumn,
2691
                                        dbColumn["SCALE"],
2692
                                    )
2693
                                ) {
2694
                                    tableColumn.precision = undefined
×
2695
                                }
2696
                                if (
×
2697
                                    dbColumn["SCALE"] !== null &&
×
2698
                                    !this.isDefaultColumnScale(
2699
                                        table,
2700
                                        tableColumn,
2701
                                        dbColumn["SCALE"],
2702
                                    )
2703
                                ) {
2704
                                    tableColumn.scale = dbColumn["SCALE"]
×
2705
                                } else if (
×
2706
                                    dbColumn["LENGTH"] !== null &&
×
2707
                                    !this.isDefaultColumnPrecision(
2708
                                        table,
2709
                                        tableColumn,
2710
                                        dbColumn["LENGTH"],
2711
                                    )
2712
                                ) {
2713
                                    tableColumn.scale = undefined
×
2714
                                }
2715
                            }
2716

2717
                            if (
×
2718
                                dbColumn["DATA_TYPE_NAME"].toLowerCase() ===
2719
                                "array"
2720
                            ) {
2721
                                tableColumn.isArray = true
×
2722
                                tableColumn.type =
×
2723
                                    dbColumn["CS_DATA_TYPE_NAME"].toLowerCase()
2724
                            }
2725

2726
                            // check only columns that have length property
2727
                            if (
×
2728
                                this.driver.withLengthColumnTypes.indexOf(
×
2729
                                    tableColumn.type as ColumnType,
2730
                                ) !== -1 &&
2731
                                dbColumn["LENGTH"]
2732
                            ) {
2733
                                const length = dbColumn["LENGTH"].toString()
×
2734
                                tableColumn.length =
×
2735
                                    !this.isDefaultColumnLength(
×
2736
                                        table,
2737
                                        tableColumn,
2738
                                        length,
2739
                                    )
2740
                                        ? length
2741
                                        : ""
2742
                            }
2743
                            tableColumn.isUnique =
×
2744
                                columnUniqueIndices.length > 0 &&
×
2745
                                !hasIgnoredIndex &&
2746
                                !isConstraintComposite
2747
                            tableColumn.isNullable =
×
2748
                                dbColumn["IS_NULLABLE"] === "TRUE"
2749
                            tableColumn.isPrimary = !!columnConstraints.find(
×
2750
                                (constraint) =>
2751
                                    constraint["IS_PRIMARY_KEY"] === "TRUE",
×
2752
                            )
2753
                            tableColumn.isGenerated =
×
2754
                                dbColumn["GENERATION_TYPE"] ===
2755
                                "ALWAYS AS IDENTITY"
2756
                            if (tableColumn.isGenerated)
×
2757
                                tableColumn.generationStrategy = "increment"
×
2758

2759
                            if (
×
2760
                                dbColumn["DEFAULT_VALUE"] === null ||
×
2761
                                dbColumn["DEFAULT_VALUE"] === undefined
2762
                            ) {
2763
                                tableColumn.default = undefined
×
2764
                            } else {
2765
                                if (
×
2766
                                    tableColumn.type === "char" ||
×
2767
                                    tableColumn.type === "nchar" ||
2768
                                    tableColumn.type === "varchar" ||
2769
                                    tableColumn.type === "nvarchar" ||
2770
                                    tableColumn.type === "alphanum" ||
2771
                                    tableColumn.type === "shorttext"
2772
                                ) {
2773
                                    tableColumn.default = `'${dbColumn["DEFAULT_VALUE"]}'`
×
2774
                                } else if (tableColumn.type === "boolean") {
×
2775
                                    tableColumn.default =
×
2776
                                        dbColumn["DEFAULT_VALUE"] === "1"
×
2777
                                            ? "true"
2778
                                            : "false"
2779
                                } else {
2780
                                    tableColumn.default =
×
2781
                                        dbColumn["DEFAULT_VALUE"]
2782
                                }
2783
                            }
2784
                            if (dbColumn["COMMENTS"]) {
×
2785
                                tableColumn.comment = dbColumn["COMMENTS"]
×
2786
                            }
2787
                            if (dbColumn["character_set_name"])
×
2788
                                tableColumn.charset =
×
2789
                                    dbColumn["character_set_name"]
2790
                            if (dbColumn["collation_name"])
×
2791
                                tableColumn.collation =
×
2792
                                    dbColumn["collation_name"]
2793
                            return tableColumn
×
2794
                        }),
2795
                )
2796

2797
                // find check constraints of table, group them by constraint name and build TableCheck.
2798
                const tableCheckConstraints = OrmUtils.uniq(
×
2799
                    dbConstraints.filter(
2800
                        (dbConstraint) =>
2801
                            dbConstraint["TABLE_NAME"] ===
×
2802
                                dbTable["TABLE_NAME"] &&
2803
                            dbConstraint["SCHEMA_NAME"] ===
2804
                                dbTable["SCHEMA_NAME"] &&
2805
                            dbConstraint["CHECK_CONDITION"] !== null &&
2806
                            dbConstraint["CHECK_CONDITION"] !== undefined,
2807
                    ),
2808
                    (dbConstraint) => dbConstraint["CONSTRAINT_NAME"],
×
2809
                )
2810

2811
                table.checks = tableCheckConstraints.map((constraint) => {
×
2812
                    const checks = dbConstraints.filter(
×
2813
                        (dbC) =>
2814
                            dbC["CONSTRAINT_NAME"] ===
×
2815
                            constraint["CONSTRAINT_NAME"],
2816
                    )
2817
                    return new TableCheck({
×
2818
                        name: constraint["CONSTRAINT_NAME"],
2819
                        columnNames: checks.map((c) => c["COLUMN_NAME"]),
×
2820
                        expression: constraint["CHECK_CONDITION"],
2821
                    })
2822
                })
2823

2824
                // find foreign key constraints of table, group them by constraint name and build TableForeignKey.
2825
                const tableForeignKeyConstraints = OrmUtils.uniq(
×
2826
                    dbForeignKeys.filter(
2827
                        (dbForeignKey) =>
2828
                            dbForeignKey["TABLE_NAME"] ===
×
2829
                                dbTable["TABLE_NAME"] &&
2830
                            dbForeignKey["SCHEMA_NAME"] ===
2831
                                dbTable["SCHEMA_NAME"],
2832
                    ),
2833
                    (dbForeignKey) => dbForeignKey["CONSTRAINT_NAME"],
×
2834
                )
2835

2836
                table.foreignKeys = tableForeignKeyConstraints.map(
×
2837
                    (dbForeignKey) => {
2838
                        const foreignKeys = dbForeignKeys.filter(
×
2839
                            (dbFk) =>
2840
                                dbFk["CONSTRAINT_NAME"] ===
×
2841
                                dbForeignKey["CONSTRAINT_NAME"],
2842
                        )
2843

2844
                        // if referenced table located in currently used schema, we don't need to concat schema name to table name.
2845
                        const schema = getSchemaFromKey(
×
2846
                            dbForeignKey,
2847
                            "REFERENCED_SCHEMA_NAME",
2848
                        )
2849
                        const referencedTableName = this.driver.buildTableName(
×
2850
                            dbForeignKey["REFERENCED_TABLE_NAME"],
2851
                            schema,
2852
                        )
2853

2854
                        return new TableForeignKey({
×
2855
                            name: dbForeignKey["CONSTRAINT_NAME"],
2856
                            columnNames: foreignKeys.map(
2857
                                (dbFk) => dbFk["COLUMN_NAME"],
×
2858
                            ),
2859
                            referencedDatabase: table.database,
2860
                            referencedSchema:
2861
                                dbForeignKey["REFERENCED_SCHEMA_NAME"],
2862
                            referencedTableName: referencedTableName,
2863
                            referencedColumnNames: foreignKeys.map(
2864
                                (dbFk) => dbFk["REFERENCED_COLUMN_NAME"],
×
2865
                            ),
2866
                            onDelete:
2867
                                dbForeignKey["DELETE_RULE"] === "RESTRICT"
×
2868
                                    ? "NO ACTION"
2869
                                    : dbForeignKey["DELETE_RULE"],
2870
                            onUpdate:
2871
                                dbForeignKey["UPDATE_RULE"] === "RESTRICT"
×
2872
                                    ? "NO ACTION"
2873
                                    : dbForeignKey["UPDATE_RULE"],
2874
                            deferrable: dbForeignKey["CHECK_TIME"].replace(
2875
                                "_",
2876
                                " ",
2877
                            ),
2878
                        })
2879
                    },
2880
                )
2881

2882
                // find index constraints of table, group them by constraint name and build TableIndex.
2883
                const tableIndexConstraints = OrmUtils.uniq(
×
2884
                    dbIndices.filter(
2885
                        (dbIndex) =>
2886
                            dbIndex["TABLE_NAME"] === dbTable["TABLE_NAME"] &&
×
2887
                            dbIndex["SCHEMA_NAME"] === dbTable["SCHEMA_NAME"],
2888
                    ),
2889
                    (dbIndex) => dbIndex["INDEX_NAME"],
×
2890
                )
2891

2892
                table.indices = tableIndexConstraints.map((constraint) => {
×
2893
                    const indices = dbIndices.filter((index) => {
×
2894
                        return (
×
2895
                            index["SCHEMA_NAME"] ===
×
2896
                                constraint["SCHEMA_NAME"] &&
2897
                            index["TABLE_NAME"] === constraint["TABLE_NAME"] &&
2898
                            index["INDEX_NAME"] === constraint["INDEX_NAME"]
2899
                        )
2900
                    })
2901
                    return new TableIndex(<TableIndexOptions>{
×
2902
                        table: table,
2903
                        name: constraint["INDEX_NAME"],
2904
                        columnNames: indices.map((i) => i["COLUMN_NAME"]),
×
2905
                        isUnique:
2906
                            constraint["CONSTRAINT"] &&
×
2907
                            constraint["CONSTRAINT"].indexOf("UNIQUE") !== -1,
2908
                        isFulltext: constraint["INDEX_TYPE"] === "FULLTEXT",
2909
                    })
2910
                })
2911

2912
                return table
×
2913
            }),
2914
        )
2915
    }
2916

2917
    /**
2918
     * Builds and returns SQL for create table.
2919
     */
2920
    protected createTableSql(table: Table, createForeignKeys?: boolean): Query {
2921
        const columnDefinitions = table.columns
×
2922
            .map((column) => this.buildCreateColumnSql(column))
×
2923
            .join(", ")
2924
        let sql = `CREATE TABLE ${this.escapePath(table)} (${columnDefinitions}`
×
2925

2926
        // we create unique indexes instead of unique constraints, because SAP HANA does not have unique constraints.
2927
        // if we mark column as Unique, it means that we create UNIQUE INDEX.
2928
        table.columns
×
2929
            .filter((column) => column.isUnique)
×
2930
            .forEach((column) => {
2931
                const isUniqueIndexExist = table.indices.some((index) => {
×
2932
                    return (
×
2933
                        index.columnNames.length === 1 &&
×
2934
                        !!index.isUnique &&
2935
                        index.columnNames.indexOf(column.name) !== -1
2936
                    )
2937
                })
2938
                const isUniqueConstraintExist = table.uniques.some((unique) => {
×
2939
                    return (
×
2940
                        unique.columnNames.length === 1 &&
×
2941
                        unique.columnNames.indexOf(column.name) !== -1
2942
                    )
2943
                })
2944
                if (!isUniqueIndexExist && !isUniqueConstraintExist)
×
2945
                    table.indices.push(
×
2946
                        new TableIndex({
2947
                            name: this.connection.namingStrategy.uniqueConstraintName(
2948
                                table,
2949
                                [column.name],
2950
                            ),
2951
                            columnNames: [column.name],
2952
                            isUnique: true,
2953
                        }),
2954
                    )
2955
            })
2956

2957
        // as SAP HANA does not have unique constraints, we must create table indices from table uniques and mark them as unique.
2958
        if (table.uniques.length > 0) {
×
2959
            table.uniques.forEach((unique) => {
×
2960
                const uniqueExist = table.indices.some(
×
2961
                    (index) => index.name === unique.name,
×
2962
                )
2963
                if (!uniqueExist) {
×
2964
                    table.indices.push(
×
2965
                        new TableIndex({
2966
                            name: unique.name,
2967
                            columnNames: unique.columnNames,
2968
                            isUnique: true,
2969
                        }),
2970
                    )
2971
                }
2972
            })
2973
        }
2974

2975
        if (table.checks.length > 0) {
×
2976
            const checksSql = table.checks
×
2977
                .map((check) => {
2978
                    const checkName = check.name
×
2979
                        ? check.name
2980
                        : this.connection.namingStrategy.checkConstraintName(
2981
                              table,
2982
                              check.expression!,
2983
                          )
2984
                    return `CONSTRAINT "${checkName}" CHECK (${check.expression})`
×
2985
                })
2986
                .join(", ")
2987

2988
            sql += `, ${checksSql}`
×
2989
        }
2990

2991
        if (table.foreignKeys.length > 0 && createForeignKeys) {
×
2992
            const foreignKeysSql = table.foreignKeys
×
2993
                .map((fk) => {
2994
                    const columnNames = fk.columnNames
×
2995
                        .map((columnName) => `"${columnName}"`)
×
2996
                        .join(", ")
2997
                    if (!fk.name)
×
2998
                        fk.name = this.connection.namingStrategy.foreignKeyName(
×
2999
                            table,
3000
                            fk.columnNames,
3001
                            this.getTablePath(fk),
3002
                            fk.referencedColumnNames,
3003
                        )
3004
                    const referencedColumnNames = fk.referencedColumnNames
×
3005
                        .map((columnName) => `"${columnName}"`)
×
3006
                        .join(", ")
3007

3008
                    let constraint = `CONSTRAINT "${
×
3009
                        fk.name
3010
                    }" FOREIGN KEY (${columnNames}) REFERENCES ${this.escapePath(
3011
                        this.getTablePath(fk),
3012
                    )} (${referencedColumnNames})`
3013
                    // SAP HANA does not have "NO ACTION" option for FK's
3014
                    if (fk.onDelete) {
×
3015
                        const onDelete =
3016
                            fk.onDelete === "NO ACTION"
×
3017
                                ? "RESTRICT"
3018
                                : fk.onDelete
3019
                        constraint += ` ON DELETE ${onDelete}`
×
3020
                    }
3021
                    if (fk.onUpdate) {
×
3022
                        const onUpdate =
3023
                            fk.onUpdate === "NO ACTION"
×
3024
                                ? "RESTRICT"
3025
                                : fk.onUpdate
3026
                        constraint += ` ON UPDATE ${onUpdate}`
×
3027
                    }
3028
                    if (fk.deferrable) {
×
3029
                        constraint += ` ${fk.deferrable}`
×
3030
                    }
3031

3032
                    return constraint
×
3033
                })
3034
                .join(", ")
3035

3036
            sql += `, ${foreignKeysSql}`
×
3037
        }
3038

3039
        const primaryColumns = table.columns.filter(
×
3040
            (column) => column.isPrimary,
×
3041
        )
3042
        if (primaryColumns.length > 0) {
×
3043
            const primaryKeyName =
3044
                this.connection.namingStrategy.primaryKeyName(
×
3045
                    table,
3046
                    primaryColumns.map((column) => column.name),
×
3047
                )
3048
            const columnNames = primaryColumns
×
3049
                .map((column) => `"${column.name}"`)
×
3050
                .join(", ")
3051
            sql += `, CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNames})`
×
3052
        }
3053

3054
        sql += `)`
×
3055

3056
        return new Query(sql)
×
3057
    }
3058

3059
    /**
3060
     * Builds drop table sql.
3061
     */
3062
    protected dropTableSql(
3063
        tableOrName: Table | string,
3064
        ifExist?: boolean,
3065
    ): Query {
3066
        const query = ifExist
×
3067
            ? `DROP TABLE IF EXISTS ${this.escapePath(tableOrName)}`
3068
            : `DROP TABLE ${this.escapePath(tableOrName)}`
3069
        return new Query(query)
×
3070
    }
3071

3072
    protected createViewSql(view: View): Query {
3073
        if (typeof view.expression === "string") {
×
3074
            return new Query(
×
3075
                `CREATE VIEW ${this.escapePath(view)} AS ${view.expression}`,
3076
            )
3077
        } else {
3078
            return new Query(
×
3079
                `CREATE VIEW ${this.escapePath(view)} AS ${view
3080
                    .expression(this.connection)
3081
                    .getQuery()}`,
3082
            )
3083
        }
3084
    }
3085

3086
    protected async insertViewDefinitionSql(view: View): Promise<Query> {
3087
        let { schema, tableName: name } = this.driver.parseTableName(view)
×
3088

3089
        if (!schema) {
×
3090
            schema = await this.getCurrentSchema()
×
3091
        }
3092

3093
        const expression =
3094
            typeof view.expression === "string"
×
3095
                ? view.expression.trim()
3096
                : view.expression(this.connection).getQuery()
3097
        return this.insertTypeormMetadataSql({
×
3098
            type: MetadataTableType.VIEW,
3099
            schema: schema,
3100
            name: name,
3101
            value: expression,
3102
        })
3103
    }
3104

3105
    /**
3106
     * Builds drop view sql.
3107
     */
3108
    protected dropViewSql(viewOrPath: View | string): Query {
3109
        return new Query(`DROP VIEW ${this.escapePath(viewOrPath)}`)
×
3110
    }
3111

3112
    /**
3113
     * Builds remove view sql.
3114
     */
3115
    protected async deleteViewDefinitionSql(
3116
        viewOrPath: View | string,
3117
    ): Promise<Query> {
3118
        let { schema, tableName: name } = this.driver.parseTableName(viewOrPath)
×
3119

3120
        if (!schema) {
×
3121
            schema = await this.getCurrentSchema()
×
3122
        }
3123

3124
        return this.deleteTypeormMetadataSql({
×
3125
            type: MetadataTableType.VIEW,
3126
            schema,
3127
            name,
3128
        })
3129
    }
3130

3131
    protected addColumnSql(table: Table, column: TableColumn): string {
3132
        return `ALTER TABLE ${this.escapePath(
×
3133
            table,
3134
        )} ADD (${this.buildCreateColumnSql(column)})`
3135
    }
3136

3137
    protected dropColumnSql(table: Table, column: TableColumn): string {
3138
        return `ALTER TABLE ${this.escapePath(table)} DROP ("${column.name}")`
×
3139
    }
3140

3141
    /**
3142
     * Builds create index sql.
3143
     */
3144
    protected createIndexSql(table: Table, index: TableIndex): Query {
3145
        const columns = index.columnNames
×
3146
            .map((columnName) => `"${columnName}"`)
×
3147
            .join(", ")
3148
        let indexType = ""
×
3149
        if (index.isUnique) {
×
3150
            indexType += "UNIQUE "
×
3151
        }
3152
        if (index.isFulltext) {
×
3153
            indexType += "FULLTEXT "
×
3154
        }
3155

3156
        return new Query(
×
3157
            `CREATE ${indexType}INDEX "${index.name}" ON ${this.escapePath(
3158
                table,
3159
            )} (${columns}) ${index.where ? "WHERE " + index.where : ""}`,
×
3160
        )
3161
    }
3162

3163
    /**
3164
     * Builds drop index sql.
3165
     */
3166
    protected dropIndexSql(
3167
        table: Table,
3168
        indexOrName: TableIndex | string,
3169
    ): Query {
3170
        const indexName = InstanceChecker.isTableIndex(indexOrName)
×
3171
            ? indexOrName.name
3172
            : indexOrName
3173
        const parsedTableName = this.driver.parseTableName(table)
×
3174

3175
        if (!parsedTableName.schema) {
×
3176
            return new Query(`DROP INDEX "${indexName}"`)
×
3177
        } else {
3178
            return new Query(
×
3179
                `DROP INDEX "${parsedTableName.schema}"."${indexName}"`,
3180
            )
3181
        }
3182
    }
3183

3184
    /**
3185
     * Builds create primary key sql.
3186
     */
3187
    protected createPrimaryKeySql(table: Table, columnNames: string[]): Query {
3188
        const primaryKeyName = this.connection.namingStrategy.primaryKeyName(
×
3189
            table,
3190
            columnNames,
3191
        )
3192
        const columnNamesString = columnNames
×
3193
            .map((columnName) => `"${columnName}"`)
×
3194
            .join(", ")
3195
        return new Query(
×
3196
            `ALTER TABLE ${this.escapePath(
3197
                table,
3198
            )} ADD CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNamesString})`,
3199
        )
3200
    }
3201

3202
    /**
3203
     * Builds drop primary key sql.
3204
     */
3205
    protected dropPrimaryKeySql(table: Table): Query {
3206
        const columnNames = table.primaryColumns.map((column) => column.name)
×
3207
        const primaryKeyName = this.connection.namingStrategy.primaryKeyName(
×
3208
            table,
3209
            columnNames,
3210
        )
3211
        return new Query(
×
3212
            `ALTER TABLE ${this.escapePath(
3213
                table,
3214
            )} DROP CONSTRAINT "${primaryKeyName}"`,
3215
        )
3216
    }
3217

3218
    /**
3219
     * Builds create check constraint sql.
3220
     */
3221
    protected createCheckConstraintSql(
3222
        table: Table,
3223
        checkConstraint: TableCheck,
3224
    ): Query {
3225
        return new Query(
×
3226
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
3227
                checkConstraint.name
3228
            }" CHECK (${checkConstraint.expression})`,
3229
        )
3230
    }
3231

3232
    /**
3233
     * Builds drop check constraint sql.
3234
     */
3235
    protected dropCheckConstraintSql(
3236
        table: Table,
3237
        checkOrName: TableCheck | string,
3238
    ): Query {
3239
        const checkName = InstanceChecker.isTableCheck(checkOrName)
×
3240
            ? checkOrName.name
3241
            : checkOrName
3242
        return new Query(
×
3243
            `ALTER TABLE ${this.escapePath(
3244
                table,
3245
            )} DROP CONSTRAINT "${checkName}"`,
3246
        )
3247
    }
3248

3249
    /**
3250
     * Builds create foreign key sql.
3251
     */
3252
    protected createForeignKeySql(
3253
        tableOrName: Table | string,
3254
        foreignKey: TableForeignKey,
3255
    ): Query {
3256
        const columnNames = foreignKey.columnNames
×
3257
            .map((column) => `"` + column + `"`)
×
3258
            .join(", ")
3259
        const referencedColumnNames = foreignKey.referencedColumnNames
×
3260
            .map((column) => `"` + column + `"`)
×
3261
            .join(",")
3262
        let sql =
3263
            `ALTER TABLE ${this.escapePath(tableOrName)} ADD CONSTRAINT "${
×
3264
                foreignKey.name
3265
            }" FOREIGN KEY (${columnNames}) ` +
3266
            `REFERENCES ${this.escapePath(
3267
                this.getTablePath(foreignKey),
3268
            )}(${referencedColumnNames})`
3269

3270
        // SAP HANA does not have "NO ACTION" option for FK's
3271
        if (foreignKey.onDelete) {
×
3272
            const onDelete =
3273
                foreignKey.onDelete === "NO ACTION"
×
3274
                    ? "RESTRICT"
3275
                    : foreignKey.onDelete
3276
            sql += ` ON DELETE ${onDelete}`
×
3277
        }
3278
        if (foreignKey.onUpdate) {
×
3279
            const onUpdate =
3280
                foreignKey.onUpdate === "NO ACTION"
×
3281
                    ? "RESTRICT"
3282
                    : foreignKey.onUpdate
3283
            sql += ` ON UPDATE ${onUpdate}`
×
3284
        }
3285

3286
        if (foreignKey.deferrable) {
×
3287
            sql += ` ${foreignKey.deferrable}`
×
3288
        }
3289

3290
        return new Query(sql)
×
3291
    }
3292

3293
    /**
3294
     * Builds drop foreign key sql.
3295
     */
3296
    protected dropForeignKeySql(
3297
        tableOrName: Table | string,
3298
        foreignKeyOrName: TableForeignKey | string,
3299
    ): Query {
3300
        const foreignKeyName = InstanceChecker.isTableForeignKey(
×
3301
            foreignKeyOrName,
3302
        )
3303
            ? foreignKeyOrName.name
3304
            : foreignKeyOrName
3305
        return new Query(
×
3306
            `ALTER TABLE ${this.escapePath(
3307
                tableOrName,
3308
            )} DROP CONSTRAINT "${foreignKeyName}"`,
3309
        )
3310
    }
3311

3312
    /**
3313
     * Escapes a given comment so it's safe to include in a query.
3314
     */
3315
    protected escapeComment(comment?: string) {
3316
        if (!comment) {
×
3317
            return "NULL"
×
3318
        }
3319

3320
        comment = comment.replace(/'/g, "''").replace(/\u0000/g, "") // Null bytes aren't allowed in comments
×
3321

3322
        return `'${comment}'`
×
3323
    }
3324

3325
    /**
3326
     * Escapes given table or view path.
3327
     */
3328
    protected escapePath(target: Table | View | string): string {
3329
        const { schema, tableName } = this.driver.parseTableName(target)
×
3330

3331
        if (schema) {
×
3332
            return `"${schema}"."${tableName}"`
×
3333
        }
3334

3335
        return `"${tableName}"`
×
3336
    }
3337

3338
    /**
3339
     * Builds a query for create column.
3340
     */
3341
    protected buildCreateColumnSql(
3342
        column: TableColumn,
3343
        explicitDefault?: boolean,
3344
        explicitNullable?: boolean,
3345
    ) {
3346
        let c =
3347
            `"${column.name}" ` + this.connection.driver.createFullType(column)
×
3348
        if (column.charset) c += " CHARACTER SET " + column.charset
×
3349
        if (column.collation) c += " COLLATE " + column.collation
×
3350
        if (column.default !== undefined && column.default !== null) {
×
3351
            c += " DEFAULT " + column.default
×
3352
        } else if (explicitDefault) {
×
3353
            c += " DEFAULT NULL"
×
3354
        }
3355
        if (!column.isGenerated) {
×
3356
            // NOT NULL is not supported with GENERATED
3357
            if (column.isNullable !== true) c += " NOT NULL"
×
3358
            else if (explicitNullable) c += " NULL"
×
3359
        }
3360
        if (
×
3361
            column.isGenerated === true &&
×
3362
            column.generationStrategy === "increment"
3363
        ) {
3364
            c += " GENERATED ALWAYS AS IDENTITY"
×
3365
        }
3366
        if (column.comment) {
×
3367
            c += ` COMMENT ${this.escapeComment(column.comment)}`
×
3368
        }
3369

3370
        return c
×
3371
    }
3372

3373
    /**
3374
     * Change table comment.
3375
     */
3376
    changeTableComment(
3377
        tableOrName: Table | string,
3378
        comment?: string,
3379
    ): Promise<void> {
3380
        throw new TypeORMError(
×
3381
            `spa driver does not support change table comment.`,
3382
        )
3383
    }
3384
}
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