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

typeorm / typeorm / 14796576772

02 May 2025 01:52PM UTC coverage: 45.367% (-30.9%) from 76.309%
14796576772

Pull #11434

github

web-flow
Merge ec4ce2d00 into fadad1a74
Pull Request #11434: feat: release PR releases using pkg.pr.new

5216 of 12761 branches covered (40.87%)

Branch coverage included in aggregate %.

11439 of 23951 relevant lines covered (47.76%)

15712.55 hits per line

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

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

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

198
        let statement: any
199
        const result = new QueryResult()
×
200

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

204
        const broadcasterResult = new BroadcasterResult()
×
205

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

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

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

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

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

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

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

268
            result.raw = raw
×
269

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

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

317
            await broadcasterResult.wait()
×
318

319
            // Always release the lock.
320
            release()
×
321
        }
322

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

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

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

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

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

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

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

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

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

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

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

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

436
        return currentDBQuery[0].dbName
×
437
    }
438

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

451
        return currentDBQuery[0]
×
452
    }
453

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

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

470
        return currentSchemaQuery[0].schemaName
×
471
    }
472

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

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

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

486
        return result[0].hasTable > 0
×
487
    }
488

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

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

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

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

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

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

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

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

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

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

593
        upQueries.push(this.createTableSql(table, createForeignKeys))
×
594
        downQueries.push(this.dropTableSql(table))
×
595

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

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

617
        await this.executeQueries(upQueries, downQueries)
×
618
    }
619

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

892
            // replace constraint name
893
            index.name = newIndexName
×
894

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

900
        await this.executeQueries(upQueries, downQueries)
×
901

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

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

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

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

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

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

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

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

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

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

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

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

1097
        await this.executeQueries(upQueries, downQueries)
×
1098

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

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

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

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

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

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

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

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

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

1201
                if (oldColumn.isPrimary === true) {
×
1202
                    const primaryColumns = clonedTable.primaryColumns
×
1203

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

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

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

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

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

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

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

1280
                    // replace constraint name
1281
                    index.name = newIndexName
×
1282

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

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

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

1313
                        // replace constraint name
1314
                        foreignKey.name = newForeignKeyName
×
1315

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1816
        await this.executeQueries(upQueries, downQueries)
×
1817

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2021
        const upQueries: Query[] = []
×
2022
        const downQueries: Query[] = []
×
2023

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2545
        const currentSchema = await this.getCurrentSchema()
×
2546
        const currentDatabase = await this.getCurrentDatabase()
×
2547

2548
        const dbTables: { SCHEMA_NAME: string; TABLE_NAME: string }[] = []
×
2549

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

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

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

2570
            dbTables.push(...(await this.query(tablesSql)))
×
2571
        }
2572

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

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

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

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

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

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

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

2643
                // create columns from the loaded columns
2644
                table.columns = await Promise.all(
×
2645
                    dbColumns
2646
                        .filter(
2647
                            (dbColumn) =>
2648
                                dbColumn["TABLE_NAME"] ===
×
2649
                                    dbTable["TABLE_NAME"] &&
2650
                                dbColumn["SCHEMA_NAME"] ===
2651
                                    dbTable["SCHEMA_NAME"],
2652
                        )
2653
                        .map(async (dbColumn) => {
2654
                            const columnConstraints = dbConstraints.filter(
×
2655
                                (dbConstraint) =>
2656
                                    dbConstraint["TABLE_NAME"] ===
×
2657
                                        dbColumn["TABLE_NAME"] &&
2658
                                    dbConstraint["SCHEMA_NAME"] ===
2659
                                        dbColumn["SCHEMA_NAME"] &&
2660
                                    dbConstraint["COLUMN_NAME"] ===
2661
                                        dbColumn["COLUMN_NAME"],
2662
                            )
2663

2664
                            const columnUniqueIndices = dbIndices.filter(
×
2665
                                (dbIndex) => {
2666
                                    return (
×
2667
                                        dbIndex["TABLE_NAME"] ===
×
2668
                                            dbTable["TABLE_NAME"] &&
2669
                                        dbIndex["SCHEMA_NAME"] ===
2670
                                            dbTable["SCHEMA_NAME"] &&
2671
                                        dbIndex["COLUMN_NAME"] ===
2672
                                            dbColumn["COLUMN_NAME"] &&
2673
                                        dbIndex["CONSTRAINT"] &&
2674
                                        dbIndex["CONSTRAINT"].indexOf(
2675
                                            "UNIQUE",
2676
                                        ) !== -1
2677
                                    )
2678
                                },
2679
                            )
2680

2681
                            const tableMetadata =
2682
                                this.connection.entityMetadatas.find(
×
2683
                                    (metadata) =>
2684
                                        this.getTablePath(table) ===
×
2685
                                        this.getTablePath(metadata),
2686
                                )
2687
                            const hasIgnoredIndex =
2688
                                columnUniqueIndices.length > 0 &&
×
2689
                                tableMetadata &&
2690
                                tableMetadata.indices.some((index) => {
2691
                                    return columnUniqueIndices.some(
×
2692
                                        (uniqueIndex) => {
2693
                                            return (
×
2694
                                                index.name ===
×
2695
                                                    uniqueIndex["INDEX_NAME"] &&
2696
                                                index.synchronize === false
2697
                                            )
2698
                                        },
2699
                                    )
2700
                                })
2701

2702
                            const isConstraintComposite =
2703
                                columnUniqueIndices.every((uniqueIndex) => {
×
2704
                                    return dbIndices.some(
×
2705
                                        (dbIndex) =>
2706
                                            dbIndex["INDEX_NAME"] ===
×
2707
                                                uniqueIndex["INDEX_NAME"] &&
2708
                                            dbIndex["COLUMN_NAME"] !==
2709
                                                dbColumn["COLUMN_NAME"],
2710
                                    )
2711
                                })
2712

2713
                            const tableColumn = new TableColumn()
×
2714
                            tableColumn.name = dbColumn["COLUMN_NAME"]
×
2715
                            tableColumn.type =
×
2716
                                dbColumn["DATA_TYPE_NAME"].toLowerCase()
2717

2718
                            if (
×
2719
                                tableColumn.type === "dec" ||
×
2720
                                tableColumn.type === "decimal"
2721
                            ) {
2722
                                // If one of these properties was set, and another was not, Postgres sets '0' in to unspecified property
2723
                                // we set 'undefined' in to unspecified property to avoid changing column on sync
2724
                                if (
×
2725
                                    dbColumn["LENGTH"] !== null &&
×
2726
                                    !this.isDefaultColumnPrecision(
2727
                                        table,
2728
                                        tableColumn,
2729
                                        dbColumn["LENGTH"],
2730
                                    )
2731
                                ) {
2732
                                    tableColumn.precision = dbColumn["LENGTH"]
×
2733
                                } else if (
×
2734
                                    dbColumn["SCALE"] !== null &&
×
2735
                                    !this.isDefaultColumnScale(
2736
                                        table,
2737
                                        tableColumn,
2738
                                        dbColumn["SCALE"],
2739
                                    )
2740
                                ) {
2741
                                    tableColumn.precision = undefined
×
2742
                                }
2743
                                if (
×
2744
                                    dbColumn["SCALE"] !== null &&
×
2745
                                    !this.isDefaultColumnScale(
2746
                                        table,
2747
                                        tableColumn,
2748
                                        dbColumn["SCALE"],
2749
                                    )
2750
                                ) {
2751
                                    tableColumn.scale = dbColumn["SCALE"]
×
2752
                                } else if (
×
2753
                                    dbColumn["LENGTH"] !== null &&
×
2754
                                    !this.isDefaultColumnPrecision(
2755
                                        table,
2756
                                        tableColumn,
2757
                                        dbColumn["LENGTH"],
2758
                                    )
2759
                                ) {
2760
                                    tableColumn.scale = undefined
×
2761
                                }
2762
                            }
2763

2764
                            if (
×
2765
                                dbColumn["DATA_TYPE_NAME"].toLowerCase() ===
2766
                                "array"
2767
                            ) {
2768
                                tableColumn.isArray = true
×
2769
                                tableColumn.type =
×
2770
                                    dbColumn["CS_DATA_TYPE_NAME"].toLowerCase()
2771
                            }
2772

2773
                            // check only columns that have length property
2774
                            if (
×
2775
                                this.driver.withLengthColumnTypes.indexOf(
×
2776
                                    tableColumn.type as ColumnType,
2777
                                ) !== -1 &&
2778
                                dbColumn["LENGTH"]
2779
                            ) {
2780
                                const length = dbColumn["LENGTH"].toString()
×
2781
                                tableColumn.length =
×
2782
                                    !this.isDefaultColumnLength(
×
2783
                                        table,
2784
                                        tableColumn,
2785
                                        length,
2786
                                    )
2787
                                        ? length
2788
                                        : ""
2789
                            }
2790
                            tableColumn.isUnique =
×
2791
                                columnUniqueIndices.length > 0 &&
×
2792
                                !hasIgnoredIndex &&
2793
                                !isConstraintComposite
2794
                            tableColumn.isNullable =
×
2795
                                dbColumn["IS_NULLABLE"] === "TRUE"
2796
                            tableColumn.isPrimary = !!columnConstraints.find(
×
2797
                                (constraint) =>
2798
                                    constraint["IS_PRIMARY_KEY"] === "TRUE",
×
2799
                            )
2800
                            tableColumn.isGenerated =
×
2801
                                dbColumn["GENERATION_TYPE"] ===
2802
                                "ALWAYS AS IDENTITY"
2803
                            if (tableColumn.isGenerated)
×
2804
                                tableColumn.generationStrategy = "increment"
×
2805

2806
                            if (
×
2807
                                dbColumn["DEFAULT_VALUE"] === null ||
×
2808
                                dbColumn["DEFAULT_VALUE"] === undefined
2809
                            ) {
2810
                                tableColumn.default = undefined
×
2811
                            } else {
2812
                                if (
×
2813
                                    tableColumn.type === "char" ||
×
2814
                                    tableColumn.type === "nchar" ||
2815
                                    tableColumn.type === "varchar" ||
2816
                                    tableColumn.type === "nvarchar" ||
2817
                                    tableColumn.type === "alphanum" ||
2818
                                    tableColumn.type === "shorttext"
2819
                                ) {
2820
                                    tableColumn.default = `'${dbColumn["DEFAULT_VALUE"]}'`
×
2821
                                } else if (tableColumn.type === "boolean") {
×
2822
                                    tableColumn.default =
×
2823
                                        dbColumn["DEFAULT_VALUE"] === "1"
×
2824
                                            ? "true"
2825
                                            : "false"
2826
                                } else {
2827
                                    tableColumn.default =
×
2828
                                        dbColumn["DEFAULT_VALUE"]
2829
                                }
2830
                            }
2831
                            if (dbColumn["COMMENTS"]) {
×
2832
                                tableColumn.comment = dbColumn["COMMENTS"]
×
2833
                            }
2834
                            return tableColumn
×
2835
                        }),
2836
                )
2837

2838
                // find check constraints of table, group them by constraint name and build TableCheck.
2839
                const tableCheckConstraints = OrmUtils.uniq(
×
2840
                    dbConstraints.filter(
2841
                        (dbConstraint) =>
2842
                            dbConstraint["TABLE_NAME"] ===
×
2843
                                dbTable["TABLE_NAME"] &&
2844
                            dbConstraint["SCHEMA_NAME"] ===
2845
                                dbTable["SCHEMA_NAME"] &&
2846
                            dbConstraint["CHECK_CONDITION"] !== null &&
2847
                            dbConstraint["CHECK_CONDITION"] !== undefined,
2848
                    ),
2849
                    (dbConstraint) => dbConstraint["CONSTRAINT_NAME"],
×
2850
                )
2851

2852
                table.checks = tableCheckConstraints.map((constraint) => {
×
2853
                    const checks = dbConstraints.filter(
×
2854
                        (dbC) =>
2855
                            dbC["CONSTRAINT_NAME"] ===
×
2856
                            constraint["CONSTRAINT_NAME"],
2857
                    )
2858
                    return new TableCheck({
×
2859
                        name: constraint["CONSTRAINT_NAME"],
2860
                        columnNames: checks.map((c) => c["COLUMN_NAME"]),
×
2861
                        expression: constraint["CHECK_CONDITION"],
2862
                    })
2863
                })
2864

2865
                // find foreign key constraints of table, group them by constraint name and build TableForeignKey.
2866
                const tableForeignKeyConstraints = OrmUtils.uniq(
×
2867
                    dbForeignKeys.filter(
2868
                        (dbForeignKey) =>
2869
                            dbForeignKey["TABLE_NAME"] ===
×
2870
                                dbTable["TABLE_NAME"] &&
2871
                            dbForeignKey["SCHEMA_NAME"] ===
2872
                                dbTable["SCHEMA_NAME"],
2873
                    ),
2874
                    (dbForeignKey) => dbForeignKey["CONSTRAINT_NAME"],
×
2875
                )
2876

2877
                table.foreignKeys = tableForeignKeyConstraints.map(
×
2878
                    (dbForeignKey) => {
2879
                        const foreignKeys = dbForeignKeys.filter(
×
2880
                            (dbFk) =>
2881
                                dbFk["CONSTRAINT_NAME"] ===
×
2882
                                dbForeignKey["CONSTRAINT_NAME"],
2883
                        )
2884

2885
                        // if referenced table located in currently used schema, we don't need to concat schema name to table name.
2886
                        const schema = getSchemaFromKey(
×
2887
                            dbForeignKey,
2888
                            "REFERENCED_SCHEMA_NAME",
2889
                        )
2890
                        const referencedTableName = this.driver.buildTableName(
×
2891
                            dbForeignKey["REFERENCED_TABLE_NAME"],
2892
                            schema,
2893
                        )
2894

2895
                        return new TableForeignKey({
×
2896
                            name: dbForeignKey["CONSTRAINT_NAME"],
2897
                            columnNames: foreignKeys.map(
2898
                                (dbFk) => dbFk["COLUMN_NAME"],
×
2899
                            ),
2900
                            referencedDatabase: table.database,
2901
                            referencedSchema:
2902
                                dbForeignKey["REFERENCED_SCHEMA_NAME"],
2903
                            referencedTableName: referencedTableName,
2904
                            referencedColumnNames: foreignKeys.map(
2905
                                (dbFk) => dbFk["REFERENCED_COLUMN_NAME"],
×
2906
                            ),
2907
                            onDelete:
2908
                                dbForeignKey["DELETE_RULE"] === "RESTRICT"
×
2909
                                    ? "NO ACTION"
2910
                                    : dbForeignKey["DELETE_RULE"],
2911
                            onUpdate:
2912
                                dbForeignKey["UPDATE_RULE"] === "RESTRICT"
×
2913
                                    ? "NO ACTION"
2914
                                    : dbForeignKey["UPDATE_RULE"],
2915
                            deferrable: dbForeignKey["CHECK_TIME"].replace(
2916
                                "_",
2917
                                " ",
2918
                            ),
2919
                        })
2920
                    },
2921
                )
2922

2923
                // find index constraints of table, group them by constraint name and build TableIndex.
2924
                const tableIndexConstraints = OrmUtils.uniq(
×
2925
                    dbIndices.filter(
2926
                        (dbIndex) =>
2927
                            dbIndex["TABLE_NAME"] === dbTable["TABLE_NAME"] &&
×
2928
                            dbIndex["SCHEMA_NAME"] === dbTable["SCHEMA_NAME"],
2929
                    ),
2930
                    (dbIndex) => dbIndex["INDEX_NAME"],
×
2931
                )
2932

2933
                table.indices = tableIndexConstraints.map((constraint) => {
×
2934
                    const indices = dbIndices.filter((index) => {
×
2935
                        return (
×
2936
                            index["SCHEMA_NAME"] ===
×
2937
                                constraint["SCHEMA_NAME"] &&
2938
                            index["TABLE_NAME"] === constraint["TABLE_NAME"] &&
2939
                            index["INDEX_NAME"] === constraint["INDEX_NAME"]
2940
                        )
2941
                    })
2942
                    return new TableIndex(<TableIndexOptions>{
×
2943
                        table: table,
2944
                        name: constraint["INDEX_NAME"],
2945
                        columnNames: indices.map((i) => i["COLUMN_NAME"]),
×
2946
                        isUnique:
2947
                            constraint["CONSTRAINT"] &&
×
2948
                            constraint["CONSTRAINT"].indexOf("UNIQUE") !== -1,
2949
                        isFulltext: constraint["INDEX_TYPE"] === "FULLTEXT",
2950
                    })
2951
                })
2952

2953
                return table
×
2954
            }),
2955
        )
2956
    }
2957

2958
    /**
2959
     * Builds and returns SQL for create table.
2960
     */
2961
    protected createTableSql(table: Table, createForeignKeys?: boolean): Query {
2962
        const columnDefinitions = table.columns
×
2963
            .map((column) => this.buildCreateColumnSql(column))
×
2964
            .join(", ")
2965
        let sql = `CREATE TABLE ${this.escapePath(table)} (${columnDefinitions}`
×
2966

2967
        // we create unique indexes instead of unique constraints, because SAP HANA does not have unique constraints.
2968
        // if we mark column as Unique, it means that we create UNIQUE INDEX.
2969
        table.columns
×
2970
            .filter((column) => column.isUnique)
×
2971
            .forEach((column) => {
2972
                const isUniqueIndexExist = table.indices.some((index) => {
×
2973
                    return (
×
2974
                        index.columnNames.length === 1 &&
×
2975
                        !!index.isUnique &&
2976
                        index.columnNames.indexOf(column.name) !== -1
2977
                    )
2978
                })
2979
                const isUniqueConstraintExist = table.uniques.some((unique) => {
×
2980
                    return (
×
2981
                        unique.columnNames.length === 1 &&
×
2982
                        unique.columnNames.indexOf(column.name) !== -1
2983
                    )
2984
                })
2985
                if (!isUniqueIndexExist && !isUniqueConstraintExist)
×
2986
                    table.indices.push(
×
2987
                        new TableIndex({
2988
                            name: this.connection.namingStrategy.uniqueConstraintName(
2989
                                table,
2990
                                [column.name],
2991
                            ),
2992
                            columnNames: [column.name],
2993
                            isUnique: true,
2994
                        }),
2995
                    )
2996
            })
2997

2998
        // as SAP HANA does not have unique constraints, we must create table indices from table uniques and mark them as unique.
2999
        if (table.uniques.length > 0) {
×
3000
            table.uniques.forEach((unique) => {
×
3001
                const uniqueExist = table.indices.some(
×
3002
                    (index) => index.name === unique.name,
×
3003
                )
3004
                if (!uniqueExist) {
×
3005
                    table.indices.push(
×
3006
                        new TableIndex({
3007
                            name: unique.name,
3008
                            columnNames: unique.columnNames,
3009
                            isUnique: true,
3010
                        }),
3011
                    )
3012
                }
3013
            })
3014
        }
3015

3016
        if (table.checks.length > 0) {
×
3017
            const checksSql = table.checks
×
3018
                .map((check) => {
3019
                    const checkName = check.name
×
3020
                        ? check.name
3021
                        : this.connection.namingStrategy.checkConstraintName(
3022
                              table,
3023
                              check.expression!,
3024
                          )
3025
                    return `CONSTRAINT "${checkName}" CHECK (${check.expression})`
×
3026
                })
3027
                .join(", ")
3028

3029
            sql += `, ${checksSql}`
×
3030
        }
3031

3032
        if (table.foreignKeys.length > 0 && createForeignKeys) {
×
3033
            const foreignKeysSql = table.foreignKeys
×
3034
                .map((fk) => {
3035
                    const columnNames = fk.columnNames
×
3036
                        .map((columnName) => `"${columnName}"`)
×
3037
                        .join(", ")
3038
                    if (!fk.name)
×
3039
                        fk.name = this.connection.namingStrategy.foreignKeyName(
×
3040
                            table,
3041
                            fk.columnNames,
3042
                            this.getTablePath(fk),
3043
                            fk.referencedColumnNames,
3044
                        )
3045
                    const referencedColumnNames = fk.referencedColumnNames
×
3046
                        .map((columnName) => `"${columnName}"`)
×
3047
                        .join(", ")
3048

3049
                    let constraint = `CONSTRAINT "${
×
3050
                        fk.name
3051
                    }" FOREIGN KEY (${columnNames}) REFERENCES ${this.escapePath(
3052
                        this.getTablePath(fk),
3053
                    )} (${referencedColumnNames})`
3054
                    // SAP HANA does not have "NO ACTION" option for FK's
3055
                    if (fk.onDelete) {
×
3056
                        const onDelete =
3057
                            fk.onDelete === "NO ACTION"
×
3058
                                ? "RESTRICT"
3059
                                : fk.onDelete
3060
                        constraint += ` ON DELETE ${onDelete}`
×
3061
                    }
3062
                    if (fk.onUpdate) {
×
3063
                        const onUpdate =
3064
                            fk.onUpdate === "NO ACTION"
×
3065
                                ? "RESTRICT"
3066
                                : fk.onUpdate
3067
                        constraint += ` ON UPDATE ${onUpdate}`
×
3068
                    }
3069
                    if (fk.deferrable) {
×
3070
                        constraint += ` ${fk.deferrable}`
×
3071
                    }
3072

3073
                    return constraint
×
3074
                })
3075
                .join(", ")
3076

3077
            sql += `, ${foreignKeysSql}`
×
3078
        }
3079

3080
        const primaryColumns = table.columns.filter(
×
3081
            (column) => column.isPrimary,
×
3082
        )
3083
        if (primaryColumns.length > 0) {
×
3084
            const primaryKeyName =
3085
                this.connection.namingStrategy.primaryKeyName(
×
3086
                    table,
3087
                    primaryColumns.map((column) => column.name),
×
3088
                )
3089
            const columnNames = primaryColumns
×
3090
                .map((column) => `"${column.name}"`)
×
3091
                .join(", ")
3092
            sql += `, CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNames})`
×
3093
        }
3094

3095
        sql += `)`
×
3096

3097
        return new Query(sql)
×
3098
    }
3099

3100
    /**
3101
     * Builds drop table sql.
3102
     */
3103
    protected dropTableSql(
3104
        tableOrName: Table | string,
3105
        ifExist?: boolean,
3106
    ): Query {
3107
        const query = ifExist
×
3108
            ? `DROP TABLE IF EXISTS ${this.escapePath(tableOrName)}`
3109
            : `DROP TABLE ${this.escapePath(tableOrName)}`
3110
        return new Query(query)
×
3111
    }
3112

3113
    protected createViewSql(view: View): Query {
3114
        if (typeof view.expression === "string") {
×
3115
            return new Query(
×
3116
                `CREATE VIEW ${this.escapePath(view)} AS ${view.expression}`,
3117
            )
3118
        } else {
3119
            return new Query(
×
3120
                `CREATE VIEW ${this.escapePath(view)} AS ${view
3121
                    .expression(this.connection)
3122
                    .getQuery()}`,
3123
            )
3124
        }
3125
    }
3126

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

3130
        if (!schema) {
×
3131
            schema = await this.getCurrentSchema()
×
3132
        }
3133

3134
        const expression =
3135
            typeof view.expression === "string"
×
3136
                ? view.expression.trim()
3137
                : view.expression(this.connection).getQuery()
3138
        return this.insertTypeormMetadataSql({
×
3139
            type: MetadataTableType.VIEW,
3140
            schema: schema,
3141
            name: name,
3142
            value: expression,
3143
        })
3144
    }
3145

3146
    /**
3147
     * Builds drop view sql.
3148
     */
3149
    protected dropViewSql(viewOrPath: View | string): Query {
3150
        return new Query(`DROP VIEW ${this.escapePath(viewOrPath)}`)
×
3151
    }
3152

3153
    /**
3154
     * Builds remove view sql.
3155
     */
3156
    protected async deleteViewDefinitionSql(
3157
        viewOrPath: View | string,
3158
    ): Promise<Query> {
3159
        let { schema, tableName: name } = this.driver.parseTableName(viewOrPath)
×
3160

3161
        if (!schema) {
×
3162
            schema = await this.getCurrentSchema()
×
3163
        }
3164

3165
        return this.deleteTypeormMetadataSql({
×
3166
            type: MetadataTableType.VIEW,
3167
            schema,
3168
            name,
3169
        })
3170
    }
3171

3172
    protected addColumnSql(table: Table, column: TableColumn): string {
3173
        return `ALTER TABLE ${this.escapePath(
×
3174
            table,
3175
        )} ADD (${this.buildCreateColumnSql(column)})`
3176
    }
3177

3178
    protected dropColumnSql(table: Table, column: TableColumn): string {
3179
        return `ALTER TABLE ${this.escapePath(table)} DROP ("${column.name}")`
×
3180
    }
3181

3182
    /**
3183
     * Builds create index sql.
3184
     */
3185
    protected createIndexSql(table: Table, index: TableIndex): Query {
3186
        const columns = index.columnNames
×
3187
            .map((columnName) => `"${columnName}"`)
×
3188
            .join(", ")
3189
        let indexType = ""
×
3190
        if (index.isUnique) {
×
3191
            indexType += "UNIQUE "
×
3192
        }
3193
        if (index.isFulltext && this.driver.isFullTextColumnTypeSupported()) {
×
3194
            indexType += "FULLTEXT "
×
3195
        }
3196

3197
        return new Query(
×
3198
            `CREATE ${indexType}INDEX "${index.name}" ON ${this.escapePath(
3199
                table,
3200
            )} (${columns}) ${index.where ? "WHERE " + index.where : ""}`,
×
3201
        )
3202
    }
3203

3204
    /**
3205
     * Builds drop index sql.
3206
     */
3207
    protected dropIndexSql(
3208
        table: Table,
3209
        indexOrName: TableIndex | string,
3210
    ): Query {
3211
        const indexName = InstanceChecker.isTableIndex(indexOrName)
×
3212
            ? indexOrName.name
3213
            : indexOrName
3214
        const parsedTableName = this.driver.parseTableName(table)
×
3215

3216
        if (!parsedTableName.schema) {
×
3217
            return new Query(`DROP INDEX "${indexName}"`)
×
3218
        } else {
3219
            return new Query(
×
3220
                `DROP INDEX "${parsedTableName.schema}"."${indexName}"`,
3221
            )
3222
        }
3223
    }
3224

3225
    /**
3226
     * Builds create primary key sql.
3227
     */
3228
    protected createPrimaryKeySql(table: Table, columnNames: string[]): Query {
3229
        const primaryKeyName = this.connection.namingStrategy.primaryKeyName(
×
3230
            table,
3231
            columnNames,
3232
        )
3233
        const columnNamesString = columnNames
×
3234
            .map((columnName) => `"${columnName}"`)
×
3235
            .join(", ")
3236
        return new Query(
×
3237
            `ALTER TABLE ${this.escapePath(
3238
                table,
3239
            )} ADD CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNamesString})`,
3240
        )
3241
    }
3242

3243
    /**
3244
     * Builds drop primary key sql.
3245
     */
3246
    protected dropPrimaryKeySql(table: Table): Query {
3247
        const columnNames = table.primaryColumns.map((column) => column.name)
×
3248
        const primaryKeyName = this.connection.namingStrategy.primaryKeyName(
×
3249
            table,
3250
            columnNames,
3251
        )
3252
        return new Query(
×
3253
            `ALTER TABLE ${this.escapePath(
3254
                table,
3255
            )} DROP CONSTRAINT "${primaryKeyName}"`,
3256
        )
3257
    }
3258

3259
    /**
3260
     * Builds create check constraint sql.
3261
     */
3262
    protected createCheckConstraintSql(
3263
        table: Table,
3264
        checkConstraint: TableCheck,
3265
    ): Query {
3266
        return new Query(
×
3267
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
3268
                checkConstraint.name
3269
            }" CHECK (${checkConstraint.expression})`,
3270
        )
3271
    }
3272

3273
    /**
3274
     * Builds drop check constraint sql.
3275
     */
3276
    protected dropCheckConstraintSql(
3277
        table: Table,
3278
        checkOrName: TableCheck | string,
3279
    ): Query {
3280
        const checkName = InstanceChecker.isTableCheck(checkOrName)
×
3281
            ? checkOrName.name
3282
            : checkOrName
3283
        return new Query(
×
3284
            `ALTER TABLE ${this.escapePath(
3285
                table,
3286
            )} DROP CONSTRAINT "${checkName}"`,
3287
        )
3288
    }
3289

3290
    /**
3291
     * Builds create foreign key sql.
3292
     */
3293
    protected createForeignKeySql(
3294
        tableOrName: Table | string,
3295
        foreignKey: TableForeignKey,
3296
    ): Query {
3297
        const columnNames = foreignKey.columnNames
×
3298
            .map((column) => `"` + column + `"`)
×
3299
            .join(", ")
3300
        const referencedColumnNames = foreignKey.referencedColumnNames
×
3301
            .map((column) => `"` + column + `"`)
×
3302
            .join(",")
3303
        let sql =
3304
            `ALTER TABLE ${this.escapePath(tableOrName)} ADD CONSTRAINT "${
×
3305
                foreignKey.name
3306
            }" FOREIGN KEY (${columnNames}) ` +
3307
            `REFERENCES ${this.escapePath(
3308
                this.getTablePath(foreignKey),
3309
            )}(${referencedColumnNames})`
3310

3311
        // SAP HANA does not have "NO ACTION" option for FK's
3312
        if (foreignKey.onDelete) {
×
3313
            const onDelete =
3314
                foreignKey.onDelete === "NO ACTION"
×
3315
                    ? "RESTRICT"
3316
                    : foreignKey.onDelete
3317
            sql += ` ON DELETE ${onDelete}`
×
3318
        }
3319
        if (foreignKey.onUpdate) {
×
3320
            const onUpdate =
3321
                foreignKey.onUpdate === "NO ACTION"
×
3322
                    ? "RESTRICT"
3323
                    : foreignKey.onUpdate
3324
            sql += ` ON UPDATE ${onUpdate}`
×
3325
        }
3326

3327
        if (foreignKey.deferrable) {
×
3328
            sql += ` ${foreignKey.deferrable}`
×
3329
        }
3330

3331
        return new Query(sql)
×
3332
    }
3333

3334
    /**
3335
     * Builds drop foreign key sql.
3336
     */
3337
    protected dropForeignKeySql(
3338
        tableOrName: Table | string,
3339
        foreignKeyOrName: TableForeignKey | string,
3340
    ): Query {
3341
        const foreignKeyName = InstanceChecker.isTableForeignKey(
×
3342
            foreignKeyOrName,
3343
        )
3344
            ? foreignKeyOrName.name
3345
            : foreignKeyOrName
3346
        return new Query(
×
3347
            `ALTER TABLE ${this.escapePath(
3348
                tableOrName,
3349
            )} DROP CONSTRAINT "${foreignKeyName}"`,
3350
        )
3351
    }
3352

3353
    /**
3354
     * Escapes a given comment so it's safe to include in a query.
3355
     */
3356
    protected escapeComment(comment?: string) {
3357
        if (!comment) {
×
3358
            return "NULL"
×
3359
        }
3360

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

3363
        return `'${comment}'`
×
3364
    }
3365

3366
    /**
3367
     * Escapes given table or view path.
3368
     */
3369
    protected escapePath(target: Table | View | string): string {
3370
        const { schema, tableName } = this.driver.parseTableName(target)
×
3371

3372
        if (schema) {
×
3373
            return `"${schema}"."${tableName}"`
×
3374
        }
3375

3376
        return `"${tableName}"`
×
3377
    }
3378

3379
    /**
3380
     * Builds a query for create column.
3381
     */
3382
    protected buildCreateColumnSql(
3383
        column: TableColumn,
3384
        explicitDefault?: boolean,
3385
        explicitNullable?: boolean,
3386
    ) {
3387
        let c =
3388
            `"${column.name}" ` + this.connection.driver.createFullType(column)
×
3389
        if (column.default !== undefined && column.default !== null) {
×
3390
            c += " DEFAULT " + column.default
×
3391
        } else if (explicitDefault) {
×
3392
            c += " DEFAULT NULL"
×
3393
        }
3394
        if (!column.isGenerated) {
×
3395
            // NOT NULL is not supported with GENERATED
3396
            if (column.isNullable !== true) c += " NOT NULL"
×
3397
            else if (explicitNullable) c += " NULL"
×
3398
        }
3399
        if (
×
3400
            column.isGenerated === true &&
×
3401
            column.generationStrategy === "increment"
3402
        ) {
3403
            c += " GENERATED ALWAYS AS IDENTITY"
×
3404
        }
3405
        if (column.comment) {
×
3406
            c += ` COMMENT ${this.escapeComment(column.comment)}`
×
3407
        }
3408

3409
        return c
×
3410
    }
3411

3412
    /**
3413
     * Change table comment.
3414
     */
3415
    changeTableComment(
3416
        tableOrName: Table | string,
3417
        comment?: string,
3418
    ): Promise<void> {
3419
        throw new TypeORMError(
×
3420
            `spa driver does not support change table comment.`,
3421
        )
3422
    }
3423
}
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

© 2025 Coveralls, Inc