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

typeorm / typeorm / 19549987525

20 Nov 2025 08:11PM UTC coverage: 80.769% (+4.3%) from 76.433%
19549987525

push

github

web-flow
ci: run tests on commits to master and next (#11783)

Co-authored-by: Oleg "OSA413" Sokolov <OSA413@users.noreply.github.com>

26500 of 32174 branches covered (82.36%)

Branch coverage included in aggregate %.

91252 of 113615 relevant lines covered (80.32%)

88980.79 hits per line

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

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

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

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

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

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

26✔
54
    private lock: QueryLock = new QueryLock()
26✔
55

26✔
56
    // -------------------------------------------------------------------------
26✔
57
    // Constructor
26✔
58
    // -------------------------------------------------------------------------
26✔
59

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

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

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

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

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

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

22,798✔
91
        if (this.databaseConnection) {
22,798✔
92
            // return the connection back to the pool
21,900✔
93
            try {
21,900✔
94
                await promisify(this.databaseConnection.disconnect).call(
21,900✔
95
                    this.databaseConnection,
21,900✔
96
                )
21,900✔
97
            } catch (error) {
21,900!
98
                this.driver.poolErrorHandler(error)
×
99
                throw error
×
100
            }
×
101
        }
21,900✔
102
    }
22,798✔
103

26✔
104
    /**
26✔
105
     * Starts transaction.
26✔
106
     */
26✔
107
    async startTransaction(isolationLevel?: IsolationLevel): Promise<void> {
26✔
108
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
17,212!
109

17,212✔
110
        if (
17,212✔
111
            this.isTransactionActive &&
17,212✔
112
            this.driver.transactionSupport === "simple"
6✔
113
        )
17,212✔
114
            throw new TransactionAlreadyStartedError()
17,212✔
115

17,206✔
116
        await this.broadcaster.broadcast("BeforeTransactionStart")
17,206✔
117

17,206✔
118
        this.isTransactionActive = true
17,206✔
119

17,206✔
120
        /**
17,206✔
121
         * Disable AUTOCOMMIT while running transaction.
17,206✔
122
         *  Otherwise, COMMIT/ROLLBACK doesn't work in autocommit mode.
17,206✔
123
         */
17,206✔
124
        await this.setAutoCommit({ status: "off" })
17,206✔
125

17,206✔
126
        if (isolationLevel) {
17,212✔
127
            await this.query(
10✔
128
                `SET TRANSACTION ISOLATION LEVEL ${isolationLevel || ""}`,
10!
129
            )
10✔
130
        }
10✔
131

17,206✔
132
        await this.broadcaster.broadcast("AfterTransactionStart")
17,206✔
133
    }
17,206✔
134

26✔
135
    /**
26✔
136
     * Commits transaction.
26✔
137
     * Error will be thrown if transaction was not started.
26✔
138
     */
26✔
139
    async commitTransaction(): Promise<void> {
26✔
140
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
17,166!
141

17,166✔
142
        if (!this.isTransactionActive) throw new TransactionNotStartedError()
17,166✔
143

17,164✔
144
        await this.broadcaster.broadcast("BeforeTransactionCommit")
17,164✔
145

17,164✔
146
        await this.query("COMMIT")
17,164✔
147
        this.isTransactionActive = false
17,164✔
148

17,164✔
149
        await this.setAutoCommit({ status: "on" })
17,164✔
150
        await this.broadcaster.broadcast("AfterTransactionCommit")
17,164✔
151
    }
17,164✔
152

26✔
153
    /**
26✔
154
     * Rollbacks transaction.
26✔
155
     * Error will be thrown if transaction was not started.
26✔
156
     */
26✔
157
    async rollbackTransaction(): Promise<void> {
26✔
158
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
48!
159

48✔
160
        if (!this.isTransactionActive) throw new TransactionNotStartedError()
48✔
161

42✔
162
        await this.broadcaster.broadcast("BeforeTransactionRollback")
42✔
163

42✔
164
        await this.query("ROLLBACK")
42✔
165
        this.isTransactionActive = false
42✔
166

42✔
167
        await this.setAutoCommit({ status: "on" })
42✔
168
        await this.broadcaster.broadcast("AfterTransactionRollback")
42✔
169
    }
42✔
170

26✔
171
    /**
26✔
172
     * @description Switches on/off AUTOCOMMIT mode
26✔
173
     * @link https://help.sap.com/docs/HANA_SERVICE_CF/7c78579ce9b14a669c1f3295b0d8ca16/d538d11053bd4f3f847ec5ce817a3d4c.html?locale=en-US
26✔
174
     */
26✔
175
    async setAutoCommit(options: { status: "on" | "off" }) {
26✔
176
        const connection = await this.connect()
34,412✔
177
        connection.setAutoCommit(options.status === "on")
34,412✔
178

34,412✔
179
        const query = `SET TRANSACTION AUTOCOMMIT DDL ${options.status.toUpperCase()}`
34,412✔
180
        this.driver.connection.logger.logQuery(query, [], this)
34,412✔
181
        try {
34,412✔
182
            await promisify(connection.exec).call(connection, query)
34,412✔
183
        } catch (error) {
34,412!
184
            throw new QueryFailedError(query, [], error)
×
185
        }
×
186
    }
34,412✔
187

26✔
188
    /**
26✔
189
     * Executes a given SQL query.
26✔
190
     */
26✔
191
    async query(
26✔
192
        query: string,
98,534✔
193
        parameters?: any[],
98,534✔
194
        useStructuredResult = false,
98,534✔
195
    ): Promise<any> {
98,534✔
196
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
98,534!
197

98,534✔
198
        const release = await this.lock.acquire()
98,534✔
199

98,534✔
200
        const databaseConnection = await this.connect()
98,534✔
201

98,534✔
202
        let statement: any
98,534✔
203
        const result = new QueryResult()
98,534✔
204

98,534✔
205
        this.driver.connection.logger.logQuery(query, parameters, this)
98,534✔
206
        await this.broadcaster.broadcast("BeforeQuery", query, parameters)
98,534✔
207

98,534✔
208
        const broadcasterResult = new BroadcasterResult()
98,534✔
209

98,534✔
210
        try {
98,534✔
211
            const queryStartTime = Date.now()
98,534✔
212
            const isInsertQuery = query.substr(0, 11) === "INSERT INTO"
98,534✔
213

98,534✔
214
            if (parameters?.some(Array.isArray)) {
98,534!
215
                statement = await promisify(databaseConnection.prepare).call(
×
216
                    databaseConnection,
×
217
                    query,
×
218
                )
×
219
            }
×
220

98,534✔
221
            let raw: any
98,534✔
222
            try {
98,534✔
223
                raw = statement
98,534✔
224
                    ? await promisify(statement.exec).call(
98,534!
225
                          statement,
×
226
                          parameters,
×
227
                      )
×
228
                    : await promisify(databaseConnection.exec).call(
98,534✔
229
                          databaseConnection,
98,534✔
230
                          query,
98,534✔
231
                          parameters,
98,534✔
232
                          {},
98,534✔
233
                      )
98,534✔
234
            } catch (err) {
98,534✔
235
                throw new QueryFailedError(query, parameters, err)
12✔
236
            }
12✔
237

98,522✔
238
            // log slow queries if maxQueryExecution time is set
98,522✔
239
            const maxQueryExecutionTime =
98,522✔
240
                this.driver.connection.options.maxQueryExecutionTime
98,522✔
241
            const queryEndTime = Date.now()
98,522✔
242
            const queryExecutionTime = queryEndTime - queryStartTime
98,522✔
243

98,522✔
244
            this.broadcaster.broadcastAfterQueryEvent(
98,522✔
245
                broadcasterResult,
98,522✔
246
                query,
98,522✔
247
                parameters,
98,522✔
248
                true,
98,522✔
249
                queryExecutionTime,
98,522✔
250
                raw,
98,522✔
251
                undefined,
98,522✔
252
            )
98,522✔
253

98,522✔
254
            if (
98,522✔
255
                maxQueryExecutionTime &&
98,522!
256
                queryExecutionTime > maxQueryExecutionTime
×
257
            ) {
98,534!
258
                this.driver.connection.logger.logQuerySlow(
×
259
                    queryExecutionTime,
×
260
                    query,
×
261
                    parameters,
×
262
                    this,
×
263
                )
×
264
            }
×
265

98,522✔
266
            if (typeof raw === "number") {
98,534✔
267
                result.affected = raw
37,928✔
268
            } else if (Array.isArray(raw)) {
98,534✔
269
                result.records = raw
32,208✔
270
            }
32,208✔
271

98,522✔
272
            result.raw = raw
98,522✔
273

98,522✔
274
            if (isInsertQuery) {
98,534✔
275
                const lastIdQuery = `SELECT CURRENT_IDENTITY_VALUE() FROM "SYS"."DUMMY"`
19,124✔
276
                this.driver.connection.logger.logQuery(lastIdQuery, [], this)
19,124✔
277
                try {
19,124✔
278
                    const identityValueResult: [
19,124✔
279
                        { "CURRENT_IDENTITY_VALUE()": unknown },
19,124✔
280
                    ] = await promisify(databaseConnection.exec).call(
19,124✔
281
                        databaseConnection,
19,124✔
282
                        lastIdQuery,
19,124✔
283
                    )
19,124✔
284

19,124✔
285
                    result.raw =
19,124✔
286
                        identityValueResult[0]["CURRENT_IDENTITY_VALUE()"]
19,124✔
287
                    result.records = identityValueResult
19,124✔
288
                } catch (error) {
19,124!
289
                    throw new QueryFailedError(lastIdQuery, [], error)
×
290
                }
×
291
            }
19,124✔
292
        } catch (err) {
98,534✔
293
            this.driver.connection.logger.logQueryError(
12✔
294
                err,
12✔
295
                query,
12✔
296
                parameters,
12✔
297
                this,
12✔
298
            )
12✔
299
            this.broadcaster.broadcastAfterQueryEvent(
12✔
300
                broadcasterResult,
12✔
301
                query,
12✔
302
                parameters,
12✔
303
                false,
12✔
304
                undefined,
12✔
305
                undefined,
12✔
306
                err,
12✔
307
            )
12✔
308
            throw err
12✔
309
        } finally {
98,534✔
310
            // Never forget to drop the statement we reserved
98,534✔
311
            if (statement?.drop) {
98,534!
312
                await promisify(statement.drop).call(statement)
×
313
            }
×
314

98,534✔
315
            await broadcasterResult.wait()
98,534✔
316

98,534✔
317
            // Always release the lock.
98,534✔
318
            release()
98,534✔
319
        }
98,534✔
320

98,522✔
321
        if (useStructuredResult) {
98,534✔
322
            return result
34,598✔
323
        } else {
98,534✔
324
            return result.raw
63,924✔
325
        }
63,924✔
326
    }
98,534✔
327

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

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

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

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

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

2✔
370
            const stream =
2✔
371
                this.driver.streamClient.createObjectStream(resultSet)
2✔
372

2✔
373
            if (onEnd) {
2!
374
                stream.on("end", onEnd)
×
375
            }
×
376
            stream.on("error", (error: Error) => {
2✔
377
                this.driver.connection.logger.logQueryError(
×
378
                    error,
×
379
                    query,
×
380
                    parameters,
×
381
                    this,
×
382
                )
×
383
                onError?.(error)
×
384
            })
2✔
385
            stream.on("close", cleanup)
2✔
386

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

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

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

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

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

3,020✔
438
        return currentDBQuery[0].dbName
3,020✔
439
    }
3,020✔
440

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

866✔
453
        return currentDBQuery[0]
866✔
454
    }
866✔
455

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

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

3,886✔
472
        return currentSchemaQuery[0].schemaName
3,886✔
473
    }
3,886✔
474

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

2,704✔
481
        if (!parsedTableName.schema) {
2,704!
482
            parsedTableName.schema = await this.getCurrentSchema()
×
483
        }
×
484

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

2,704✔
488
        return result[0].hasTable > 0
2,704✔
489
    }
2,704✔
490

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

8✔
500
        if (!parsedTableName.schema) {
8!
501
            parsedTableName.schema = await this.getCurrentSchema()
×
502
        }
×
503

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

8✔
507
        return result[0].hasColumn > 0
8✔
508
    }
8✔
509

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

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

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

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

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

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

8,694✔
595
        upQueries.push(this.createTableSql(table, createForeignKeys))
8,694✔
596
        downQueries.push(this.dropTableSql(table))
8,694✔
597

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

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

8,694✔
619
        await this.executeQueries(upQueries, downQueries)
8,694✔
620
    }
8,694✔
621

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

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

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

18✔
647
        if (dropIndices) {
24✔
648
            table.indices.forEach((index) => {
24✔
649
                upQueries.push(this.dropIndexSql(table, index))
6✔
650
                downQueries.push(this.createIndexSql(table, index))
6✔
651
            })
24✔
652
        }
24✔
653

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

24✔
661
        upQueries.push(this.dropTableSql(table))
24✔
662
        downQueries.push(this.createTableSql(table, createForeignKeys))
24✔
663

24✔
664
        await this.executeQueries(upQueries, downQueries)
24✔
665
    }
24✔
666

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

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

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

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

6✔
715
        const { schema: schemaName, tableName: oldTableName } =
6✔
716
            this.driver.parseTableName(oldTable)
6✔
717

6✔
718
        newTable.name = schemaName
6✔
719
            ? `${schemaName}.${newTableName}`
10✔
720
            : newTableName
10!
721

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

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

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

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

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

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

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

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

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

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

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

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

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

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

8✔
894
            // replace constraint name
8✔
895
            index.name = newIndexName
8✔
896

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

10✔
902
        await this.executeQueries(upQueries, downQueries)
10✔
903

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

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

4✔
921
        if (!parsedTableName.schema) {
52!
922
            parsedTableName.schema = await this.getCurrentSchema()
×
923
        }
×
924

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

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

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

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

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

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

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

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

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

52✔
1099
        await this.executeQueries(upQueries, downQueries)
52✔
1100

50✔
1101
        clonedTable.addColumn(column)
50✔
1102
        this.replaceCachedTable(table, clonedTable)
50✔
1103
    }
50✔
1104

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

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

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

28✔
1144
        await this.changeColumn(table, oldColumn, newColumn)
28✔
1145
    }
28✔
1146

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

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

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

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

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

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

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

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

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

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

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

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

8✔
1282
                    // replace constraint name
8✔
1283
                    index.name = newIndexName
8✔
1284

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

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

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

2✔
1315
                        // replace constraint name
2✔
1316
                        foreignKey.name = newForeignKeyName
2✔
1317

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

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

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

×
1348
                    // replace constraint name
×
1349
                    check.name = newCheckName
×
1350

×
1351
                    upQueries.push(
×
1352
                        this.createCheckConstraintSql(clonedTable, check),
×
1353
                    )
×
1354
                    downQueries.push(
×
1355
                        this.dropCheckConstraintSql(clonedTable, check),
×
1356
                    )
×
1357
                })
14✔
1358

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

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

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

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

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

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

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

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

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

×
1557
                    upQueries.push(this.dropIndexSql(table, uniqueIndex!))
×
1558
                    downQueries.push(this.createIndexSql(table, uniqueIndex!))
×
1559
                }
×
1560
            }
2✔
1561

50✔
1562
            await this.executeQueries(upQueries, downQueries)
50✔
1563
            this.replaceCachedTable(table, clonedTable)
50✔
1564
        }
50✔
1565
    }
86✔
1566

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

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

6✔
1591
        if (!parsedTableName.schema) {
68!
1592
            parsedTableName.schema = await this.getCurrentSchema()
×
1593
        }
×
1594

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

66✔
1603
        const clonedTable = table.clone()
66✔
1604
        const upQueries: Query[] = []
66✔
1605
        const downQueries: Query[] = []
66✔
1606

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

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

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

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

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

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

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

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

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

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

66✔
1815
        upQueries.push(new Query(this.dropColumnSql(table, column)))
66✔
1816
        downQueries.push(new Query(this.addColumnSql(table, column)))
66✔
1817

66✔
1818
        await this.executeQueries(upQueries, downQueries)
66✔
1819

66✔
1820
        clonedTable.removeColumn(column)
66✔
1821
        this.replaceCachedTable(table, clonedTable)
66✔
1822
    }
66✔
1823

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

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

4✔
1848
        const up = this.createPrimaryKeySql(table, columnNames)
4✔
1849

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

6✔
2023
        const upQueries: Query[] = []
6✔
2024
        const downQueries: Query[] = []
6✔
2025

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

5,620✔
2302
        const up = this.createForeignKeySql(table, foreignKey)
5,620✔
2303
        const down = this.dropForeignKeySql(table, foreignKey)
5,620✔
2304
        await this.executeQueries(up, down)
5,620✔
2305
        table.addForeignKey(foreignKey)
5,620✔
2306
    }
5,620✔
2307

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

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

30✔
2339
        const up = this.dropForeignKeySql(table, foreignKey)
30✔
2340
        const down = this.createForeignKeySql(table, foreignKey)
30✔
2341
        await this.executeQueries(up, down)
30✔
2342
        table.removeForeignKey(foreignKey)
30✔
2343
    }
30✔
2344

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

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

14✔
2369
        // new index may be passed without name. In this case we generate index name manually.
14✔
2370
        if (!index.name) index.name = this.generateIndexName(table, index)
36✔
2371

36✔
2372
        const up = this.createIndexSql(table, index)
36✔
2373
        const down = this.dropIndexSql(table, index)
36✔
2374
        await this.executeQueries(up, down)
36✔
2375
        table.addIndex(index)
36✔
2376
    }
36✔
2377

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

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

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

38✔
2412
        const up = this.dropIndexSql(table, index)
38✔
2413
        const down = this.createIndexSql(table, index)
38✔
2414
        await this.executeQueries(up, down)
38✔
2415
        table.removeIndex(index)
38✔
2416
    }
38✔
2417

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

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

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

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

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

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

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

26✔
2488
    // -------------------------------------------------------------------------
26✔
2489
    // Protected Methods
26✔
2490
    // -------------------------------------------------------------------------
26✔
2491

26✔
2492
    protected async loadViews(viewNames?: string[]): Promise<View[]> {
26✔
2493
        const hasTable = await this.hasTable(this.getTypeormMetadataTableName())
2,610✔
2494
        if (!hasTable) {
2,610✔
2495
            return []
2,602✔
2496
        }
2,602✔
2497

8✔
2498
        if (!viewNames) {
2,610!
2499
            viewNames = []
×
2500
        }
×
2501

8✔
2502
        const currentDatabase = await this.getCurrentDatabase()
8✔
2503
        const currentSchema = await this.getCurrentSchema()
8✔
2504

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

16✔
2510
                if (!schema) {
16!
2511
                    schema = currentSchema
×
2512
                }
×
2513

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

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

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

3,012✔
2547
        const currentSchema = await this.getCurrentSchema()
3,012✔
2548
        const currentDatabase = await this.getCurrentDatabase()
3,012✔
2549

3,012✔
2550
        const dbTables: { SCHEMA_NAME: string; TABLE_NAME: string }[] = []
3,012✔
2551

3,012✔
2552
        if (!tableNames) {
3,024!
2553
            const tablesSql = `SELECT "SCHEMA_NAME", "TABLE_NAME" FROM "SYS"."TABLES"`
×
2554

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

3,012✔
2568
            const tablesSql =
3,012✔
2569
                `SELECT "SCHEMA_NAME", "TABLE_NAME" FROM "SYS"."TABLES" WHERE ` +
3,012✔
2570
                tablesCondition
3,012✔
2571

3,012✔
2572
            dbTables.push(...(await this.query(tablesSql)))
3,012✔
2573
        }
3,012✔
2574

3,012✔
2575
        // if tables were not found in the db, no need to proceed
3,012✔
2576
        if (dbTables.length === 0) return []
3,024✔
2577

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

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

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

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

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

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

830✔
2644
            // create columns from the loaded columns
830✔
2645
            table.columns = dbColumns
830✔
2646
                .filter(
830✔
2647
                    (dbColumn) =>
830✔
2648
                        dbColumn["TABLE_NAME"] === dbTable["TABLE_NAME"] &&
10,430✔
2649
                        dbColumn["SCHEMA_NAME"] === dbTable["SCHEMA_NAME"],
830✔
2650
                )
830✔
2651
                .map((dbColumn) => {
830✔
2652
                    const columnConstraints = dbConstraints.filter(
2,910✔
2653
                        (dbConstraint) =>
2,910✔
2654
                            dbConstraint["TABLE_NAME"] ===
18,406✔
2655
                                dbColumn["TABLE_NAME"] &&
18,406✔
2656
                            dbConstraint["SCHEMA_NAME"] ===
6,884✔
2657
                                dbColumn["SCHEMA_NAME"] &&
18,406✔
2658
                            dbConstraint["COLUMN_NAME"] ===
6,884✔
2659
                                dbColumn["COLUMN_NAME"],
2,910✔
2660
                    )
2,910✔
2661

2,910✔
2662
                    const columnUniqueIndices = dbIndices.filter((dbIndex) => {
2,910✔
2663
                        return (
7,582✔
2664
                            dbIndex["TABLE_NAME"] === dbTable["TABLE_NAME"] &&
7,582✔
2665
                            dbIndex["SCHEMA_NAME"] === dbTable["SCHEMA_NAME"] &&
7,582✔
2666
                            dbIndex["COLUMN_NAME"] ===
3,624✔
2667
                                dbColumn["COLUMN_NAME"] &&
7,582✔
2668
                            dbIndex["CONSTRAINT"] &&
7,582✔
2669
                            dbIndex["CONSTRAINT"].indexOf("UNIQUE") !== -1
606✔
2670
                        )
7,582✔
2671
                    })
2,910✔
2672

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

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

2,910✔
2702
                    const tableColumn = new TableColumn()
2,910✔
2703
                    tableColumn.name = dbColumn["COLUMN_NAME"]
2,910✔
2704
                    tableColumn.type = dbColumn["DATA_TYPE_NAME"].toLowerCase()
2,910✔
2705

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

2,910✔
2752
                    if (dbColumn["DATA_TYPE_NAME"].toLowerCase() === "array") {
2,910!
2753
                        tableColumn.isArray = true
×
2754
                        tableColumn.type =
×
2755
                            dbColumn["CS_DATA_TYPE_NAME"].toLowerCase()
×
2756
                    }
×
2757

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

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

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

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

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

830✔
2853
            table.foreignKeys = tableForeignKeyConstraints.map(
830✔
2854
                (dbForeignKey) => {
830✔
2855
                    const foreignKeys = dbForeignKeys.filter(
310✔
2856
                        (dbFk) =>
310✔
2857
                            dbFk["CONSTRAINT_NAME"] ===
866✔
2858
                            dbForeignKey["CONSTRAINT_NAME"],
310✔
2859
                    )
310✔
2860

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

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

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

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

830✔
2928
            return table
830✔
2929
        })
486✔
2930
    }
486✔
2931

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

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

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

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

128✔
3003
            sql += `, ${checksSql}`
128✔
3004
        }
128✔
3005

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

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

12✔
3047
                    return constraint
12✔
3048
                })
10✔
3049
                .join(", ")
10✔
3050

10✔
3051
            sql += `, ${foreignKeysSql}`
10✔
3052
        }
10✔
3053

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

8,718✔
3069
        sql += `)`
8,718✔
3070

8,718✔
3071
        return new Query(sql)
8,718✔
3072
    }
8,718✔
3073

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

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

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

12✔
3104
        if (!schema) {
12!
3105
            schema = await this.getCurrentSchema()
×
3106
        }
×
3107

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

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

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

12✔
3135
        if (!schema) {
12!
3136
            schema = await this.getCurrentSchema()
×
3137
        }
×
3138

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

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

26✔
3152
    protected dropColumnSql(table: Table, column: TableColumn): string {
26✔
3153
        return `ALTER TABLE ${this.escapePath(table)} DROP ("${column.name}")`
118✔
3154
    }
118✔
3155

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

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

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

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

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

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

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

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

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

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

5,686✔
3301
        if (foreignKey.deferrable) {
5,686✔
3302
            sql += ` ${foreignKey.deferrable}`
74✔
3303
        }
74✔
3304

5,686✔
3305
        return new Query(sql)
5,686✔
3306
    }
5,686✔
3307

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

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

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

82✔
3337
        return `'${comment}'`
82✔
3338
    }
82✔
3339

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

39,940✔
3346
        if (schema) {
39,940✔
3347
            return `"${schema}"."${tableName}"`
39,940✔
3348
        }
39,940✔
3349

×
3350
        return `"${tableName}"`
×
3351
    }
×
3352

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

27,236✔
3383
        return c
27,236✔
3384
    }
27,236✔
3385

26✔
3386
    /**
26✔
3387
     * Change table comment.
26✔
3388
     */
26✔
3389
    changeTableComment(
26✔
3390
        tableOrName: Table | string,
×
3391
        comment?: string,
×
3392
    ): Promise<void> {
×
3393
        throw new TypeORMError(
×
3394
            `spa driver does not support change table comment.`,
×
3395
        )
×
3396
    }
×
3397
}
26✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc