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

typeorm / typeorm / 15219332477

23 May 2025 09:13PM UTC coverage: 17.216% (-59.1%) from 76.346%
15219332477

Pull #11332

github

naorpeled
cr comments - move if block
Pull Request #11332: feat: add new undefined and null behavior flags

1603 of 12759 branches covered (12.56%)

Branch coverage included in aggregate %.

0 of 31 new or added lines in 3 files covered. (0.0%)

14132 existing lines in 166 files now uncovered.

4731 of 24033 relevant lines covered (19.69%)

60.22 hits per line

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

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

29
/**
30
 * Runs queries on a single oracle database connection.
31
 */
32
export class OracleQueryRunner extends BaseQueryRunner implements QueryRunner {
1✔
33
    // -------------------------------------------------------------------------
34
    // Public Implemented Properties
35
    // -------------------------------------------------------------------------
36

37
    /**
38
     * Database driver used by connection.
39
     */
40
    driver: OracleDriver
41

42
    // -------------------------------------------------------------------------
43
    // Protected Properties
44
    // -------------------------------------------------------------------------
45

46
    /**
47
     * Promise used to obtain a database connection for a first time.
48
     */
49
    protected databaseConnectionPromise: Promise<any>
50

51
    // -------------------------------------------------------------------------
52
    // Constructor
53
    // -------------------------------------------------------------------------
54

55
    constructor(driver: OracleDriver, mode: ReplicationMode) {
UNCOV
56
        super()
×
UNCOV
57
        this.driver = driver
×
UNCOV
58
        this.connection = driver.connection
×
UNCOV
59
        this.broadcaster = new Broadcaster(this)
×
UNCOV
60
        this.mode = mode
×
61
    }
62

63
    // -------------------------------------------------------------------------
64
    // Public Methods
65
    // -------------------------------------------------------------------------
66

67
    /**
68
     * Creates/uses database connection from the connection pool to perform further operations.
69
     * Returns obtained database connection.
70
     */
71
    connect(): Promise<any> {
UNCOV
72
        if (this.databaseConnection)
×
UNCOV
73
            return Promise.resolve(this.databaseConnection)
×
74

UNCOV
75
        if (this.databaseConnectionPromise)
×
UNCOV
76
            return this.databaseConnectionPromise
×
77

UNCOV
78
        if (this.mode === "slave" && this.driver.isReplicated) {
×
79
            this.databaseConnectionPromise = this.driver
×
80
                .obtainSlaveConnection()
81
                .then((connection) => {
82
                    this.databaseConnection = connection
×
83
                    return this.databaseConnection
×
84
                })
85
        } else {
86
            // master
UNCOV
87
            this.databaseConnectionPromise = this.driver
×
88
                .obtainMasterConnection()
89
                .then((connection) => {
UNCOV
90
                    this.databaseConnection = connection
×
UNCOV
91
                    return this.databaseConnection
×
92
                })
93
        }
94

UNCOV
95
        return this.databaseConnectionPromise
×
96
    }
97

98
    /**
99
     * Releases used database connection.
100
     * You cannot use query runner methods once its released.
101
     */
102
    async release(): Promise<void> {
UNCOV
103
        this.isReleased = true
×
104

UNCOV
105
        if (!this.databaseConnection) {
×
UNCOV
106
            return
×
107
        }
108

UNCOV
109
        await this.databaseConnection.close()
×
110
    }
111

112
    /**
113
     * Starts transaction.
114
     */
115
    async startTransaction(
116
        isolationLevel: IsolationLevel = "READ COMMITTED",
×
117
    ): Promise<void> {
UNCOV
118
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
×
119

120
        // await this.query("START TRANSACTION");
UNCOV
121
        if (
×
122
            isolationLevel !== "SERIALIZABLE" &&
×
123
            isolationLevel !== "READ COMMITTED"
124
        ) {
125
            throw new TypeORMError(
×
126
                `Oracle only supports SERIALIZABLE and READ COMMITTED isolation`,
127
            )
128
        }
129

UNCOV
130
        this.isTransactionActive = true
×
UNCOV
131
        try {
×
UNCOV
132
            await this.broadcaster.broadcast("BeforeTransactionStart")
×
133
        } catch (err) {
134
            this.isTransactionActive = false
×
135
            throw err
×
136
        }
137

UNCOV
138
        if (this.transactionDepth === 0) {
×
UNCOV
139
            await this.query(
×
140
                "SET TRANSACTION ISOLATION LEVEL " + isolationLevel,
141
            )
142
        } else {
UNCOV
143
            await this.query(`SAVEPOINT typeorm_${this.transactionDepth}`)
×
144
        }
UNCOV
145
        this.transactionDepth += 1
×
146

UNCOV
147
        await this.broadcaster.broadcast("AfterTransactionStart")
×
148
    }
149

150
    /**
151
     * Commits transaction.
152
     * Error will be thrown if transaction was not started.
153
     */
154
    async commitTransaction(): Promise<void> {
UNCOV
155
        if (!this.isTransactionActive) throw new TransactionNotStartedError()
×
156

UNCOV
157
        await this.broadcaster.broadcast("BeforeTransactionCommit")
×
158

UNCOV
159
        if (this.transactionDepth === 1) {
×
UNCOV
160
            await this.query("COMMIT")
×
UNCOV
161
            this.isTransactionActive = false
×
162
        }
UNCOV
163
        this.transactionDepth -= 1
×
164

UNCOV
165
        await this.broadcaster.broadcast("AfterTransactionCommit")
×
166
    }
167

168
    /**
169
     * Rollbacks transaction.
170
     * Error will be thrown if transaction was not started.
171
     */
172
    async rollbackTransaction(): Promise<void> {
UNCOV
173
        if (!this.isTransactionActive) throw new TransactionNotStartedError()
×
174

UNCOV
175
        await this.broadcaster.broadcast("BeforeTransactionRollback")
×
176

UNCOV
177
        if (this.transactionDepth > 1) {
×
UNCOV
178
            await this.query(
×
179
                `ROLLBACK TO SAVEPOINT typeorm_${this.transactionDepth - 1}`,
180
            )
181
        } else {
UNCOV
182
            await this.query("ROLLBACK")
×
UNCOV
183
            this.isTransactionActive = false
×
184
        }
UNCOV
185
        this.transactionDepth -= 1
×
186

UNCOV
187
        await this.broadcaster.broadcast("AfterTransactionRollback")
×
188
    }
189

190
    /**
191
     * Executes a given SQL query.
192
     */
193
    async query(
194
        query: string,
195
        parameters?: any[],
196
        useStructuredResult = false,
×
197
    ): Promise<any> {
UNCOV
198
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
×
199

UNCOV
200
        const databaseConnection = await this.connect()
×
201

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

UNCOV
205
        const broadcasterResult = new BroadcasterResult()
×
UNCOV
206
        const queryStartTime = Date.now()
×
207

UNCOV
208
        try {
×
UNCOV
209
            const executionOptions = {
×
210
                autoCommit: !this.isTransactionActive,
211
                outFormat: this.driver.oracle.OUT_FORMAT_OBJECT,
212
            }
213

UNCOV
214
            const raw = await databaseConnection.execute(
×
215
                query,
216
                parameters || {},
×
217
                executionOptions,
218
            )
219

220
            // log slow queries if maxQueryExecution time is set
221
            const maxQueryExecutionTime =
UNCOV
222
                this.driver.options.maxQueryExecutionTime
×
UNCOV
223
            const queryEndTime = Date.now()
×
UNCOV
224
            const queryExecutionTime = queryEndTime - queryStartTime
×
225

UNCOV
226
            this.broadcaster.broadcastAfterQueryEvent(
×
227
                broadcasterResult,
228
                query,
229
                parameters,
230
                true,
231
                queryExecutionTime,
232
                raw,
233
                undefined,
234
            )
235

UNCOV
236
            if (
×
237
                maxQueryExecutionTime &&
×
238
                queryExecutionTime > maxQueryExecutionTime
239
            )
240
                this.driver.connection.logger.logQuerySlow(
×
241
                    queryExecutionTime,
242
                    query,
243
                    parameters,
244
                    this,
245
                )
246

UNCOV
247
            const result = new QueryResult()
×
248

UNCOV
249
            result.raw =
×
250
                raw.rows ||
×
251
                raw.outBinds ||
252
                raw.rowsAffected ||
253
                raw.implicitResults
254

UNCOV
255
            if (raw?.hasOwnProperty("rows") && Array.isArray(raw.rows)) {
×
UNCOV
256
                result.records = raw.rows
×
257
            }
258

UNCOV
259
            if (
×
260
                raw?.hasOwnProperty("outBinds") &&
×
261
                Array.isArray(raw.outBinds)
262
            ) {
UNCOV
263
                result.records = raw.outBinds
×
264
            }
265

UNCOV
266
            if (
×
267
                raw?.hasOwnProperty("implicitResults") &&
×
268
                Array.isArray(raw.implicitResults)
269
            ) {
UNCOV
270
                result.records = raw.implicitResults
×
271
            }
272

UNCOV
273
            if (raw?.hasOwnProperty("rowsAffected")) {
×
UNCOV
274
                result.affected = raw.rowsAffected
×
275
            }
276

UNCOV
277
            if (useStructuredResult) {
×
UNCOV
278
                return result
×
279
            } else {
UNCOV
280
                return result.raw
×
281
            }
282
        } catch (err) {
UNCOV
283
            this.driver.connection.logger.logQueryError(
×
284
                err,
285
                query,
286
                parameters,
287
                this,
288
            )
UNCOV
289
            this.broadcaster.broadcastAfterQueryEvent(
×
290
                broadcasterResult,
291
                query,
292
                parameters,
293
                false,
294
                undefined,
295
                undefined,
296
                err,
297
            )
298

UNCOV
299
            throw new QueryFailedError(query, parameters, err)
×
300
        } finally {
UNCOV
301
            await broadcasterResult.wait()
×
302
        }
303
    }
304

305
    /**
306
     * Returns raw data stream.
307
     */
308
    async stream(
309
        query: string,
310
        parameters?: any[],
311
        onEnd?: Function,
312
        onError?: Function,
313
    ): Promise<ReadStream> {
UNCOV
314
        if (this.isReleased) {
×
315
            throw new QueryRunnerAlreadyReleasedError()
×
316
        }
317

UNCOV
318
        const executionOptions = {
×
319
            autoCommit: !this.isTransactionActive,
320
            outFormat: this.driver.oracle.OUT_FORMAT_OBJECT,
321
        }
322

UNCOV
323
        const databaseConnection = await this.connect()
×
324

UNCOV
325
        this.driver.connection.logger.logQuery(query, parameters, this)
×
326

UNCOV
327
        try {
×
UNCOV
328
            const stream = databaseConnection.queryStream(
×
329
                query,
330
                parameters,
331
                executionOptions,
332
            )
UNCOV
333
            if (onEnd) {
×
334
                stream.on("end", onEnd)
×
335
            }
336

UNCOV
337
            if (onError) {
×
338
                stream.on("error", onError)
×
339
            }
340

UNCOV
341
            return stream
×
342
        } catch (err) {
343
            this.driver.connection.logger.logQueryError(
×
344
                err,
345
                query,
346
                parameters,
347
                this,
348
            )
349
            throw new QueryFailedError(query, parameters, err)
×
350
        }
351
    }
352

353
    /**
354
     * Returns all available database names including system databases.
355
     */
356
    async getDatabases(): Promise<string[]> {
357
        return Promise.resolve([])
×
358
    }
359

360
    /**
361
     * Returns all available schema names including system schemas.
362
     * If database parameter specified, returns schemas of that database.
363
     */
364
    async getSchemas(database?: string): Promise<string[]> {
365
        return Promise.resolve([])
×
366
    }
367

368
    /**
369
     * Checks if database with the given name exist.
370
     */
371
    async hasDatabase(database: string): Promise<boolean> {
372
        try {
×
373
            const query = await this.query(
×
374
                `SELECT 1 AS "exists" FROM global_name@"${database}"`,
375
            )
376

377
            return query.length > 0
×
378
        } catch (e) {
379
            return false
×
380
        }
381
    }
382

383
    /**
384
     * Loads currently using database
385
     */
386
    async getCurrentDatabase(): Promise<undefined> {
UNCOV
387
        const query = await this.query(
×
388
            `SELECT SYS_CONTEXT('USERENV','DB_NAME') AS "db_name" FROM dual`,
389
        )
UNCOV
390
        return query[0]["db_name"]
×
391
    }
392

393
    /**
394
     * Checks if schema with the given name exist.
395
     */
396
    async hasSchema(schema: string): Promise<boolean> {
397
        return Promise.resolve(false)
×
398
    }
399

400
    /**
401
     * Loads currently using database schema
402
     */
403
    async getCurrentSchema(): Promise<string> {
UNCOV
404
        const query = await this.query(
×
405
            `SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') AS "schema_name" FROM dual`,
406
        )
UNCOV
407
        return query[0]["schema_name"]
×
408
    }
409

410
    /**
411
     * Checks if table with the given name exist in the database.
412
     */
413
    async hasTable(tableOrName: Table | string): Promise<boolean> {
UNCOV
414
        const { tableName } = this.driver.parseTableName(tableOrName)
×
UNCOV
415
        const sql = `SELECT "TABLE_NAME" FROM "USER_TABLES" WHERE "TABLE_NAME" = '${tableName}'`
×
UNCOV
416
        const result = await this.query(sql)
×
UNCOV
417
        return result.length ? true : false
×
418
    }
419

420
    /**
421
     * Checks if column with the given name exist in the given table.
422
     */
423
    async hasColumn(
424
        tableOrName: Table | string,
425
        columnName: string,
426
    ): Promise<boolean> {
UNCOV
427
        const { tableName } = this.driver.parseTableName(tableOrName)
×
UNCOV
428
        const sql = `SELECT "COLUMN_NAME" FROM "USER_TAB_COLS" WHERE "TABLE_NAME" = '${tableName}' AND "COLUMN_NAME" = '${columnName}'`
×
UNCOV
429
        const result = await this.query(sql)
×
UNCOV
430
        return result.length ? true : false
×
431
    }
432

433
    /**
434
     * Creates a new database.
435
     */
436
    async createDatabase(
437
        database: string,
438
        ifNotExist?: boolean,
439
    ): Promise<void> {
440
        // Even with `IF NOT EXISTS` we get:
441
        //   ORA-01501: CREATE DATABASE failed
442
        //   ORA-01100: database already mounted
443
        if (ifNotExist) {
×
444
            try {
×
445
                await this.query(`CREATE DATABASE IF NOT EXISTS "${database}";`)
×
446
            } catch (e) {
447
                // if (e instanceof QueryFailedError) {
448
                if (e.message.includes("ORA-01100: database already mounted")) {
×
449
                    return
×
450
                }
451
                // }
452

453
                throw e
×
454
            }
455
        } else {
456
            await this.query(`CREATE DATABASE "${database}"`)
×
457
        }
458
    }
459

460
    /**
461
     * Drops database.
462
     */
463
    async dropDatabase(database: string, ifExist?: boolean): Promise<void> {
464
        return Promise.resolve()
×
465
    }
466

467
    /**
468
     * Creates a new table schema.
469
     */
470
    async createSchema(
471
        schemaPath: string,
472
        ifNotExist?: boolean,
473
    ): Promise<void> {
UNCOV
474
        throw new TypeORMError(
×
475
            `Schema create queries are not supported by Oracle driver.`,
476
        )
477
    }
478

479
    /**
480
     * Drops table schema.
481
     */
482
    async dropSchema(schemaPath: string, ifExist?: boolean): Promise<void> {
483
        throw new TypeORMError(
×
484
            `Schema drop queries are not supported by Oracle driver.`,
485
        )
486
    }
487

488
    /**
489
     * Creates a new table.
490
     */
491
    async createTable(
492
        table: Table,
493
        ifNotExist: boolean = false,
×
494
        createForeignKeys: boolean = true,
×
495
        createIndices: boolean = true,
×
496
    ): Promise<void> {
UNCOV
497
        if (ifNotExist) {
×
UNCOV
498
            const isTableExist = await this.hasTable(table)
×
UNCOV
499
            if (isTableExist) return Promise.resolve()
×
500
        }
UNCOV
501
        const upQueries: Query[] = []
×
UNCOV
502
        const downQueries: Query[] = []
×
503

UNCOV
504
        upQueries.push(this.createTableSql(table, createForeignKeys))
×
UNCOV
505
        downQueries.push(this.dropTableSql(table))
×
506

507
        // if createForeignKeys is true, we must drop created foreign keys in down query.
508
        // createTable does not need separate method to create foreign keys, because it create fk's in the same query with table creation.
UNCOV
509
        if (createForeignKeys)
×
UNCOV
510
            table.foreignKeys.forEach((foreignKey) =>
×
UNCOV
511
                downQueries.push(this.dropForeignKeySql(table, foreignKey)),
×
512
            )
513

UNCOV
514
        if (createIndices) {
×
UNCOV
515
            table.indices.forEach((index) => {
×
516
                // new index may be passed without name. In this case we generate index name manually.
UNCOV
517
                if (!index.name)
×
UNCOV
518
                    index.name = this.connection.namingStrategy.indexName(
×
519
                        table,
520
                        index.columnNames,
521
                        index.where,
522
                    )
UNCOV
523
                upQueries.push(this.createIndexSql(table, index))
×
UNCOV
524
                downQueries.push(this.dropIndexSql(index))
×
525
            })
526
        }
527

528
        // if table have column with generated type, we must add the expression to the metadata table
UNCOV
529
        const generatedColumns = table.columns.filter(
×
UNCOV
530
            (column) => column.generatedType && column.asExpression,
×
531
        )
532

UNCOV
533
        for (const column of generatedColumns) {
×
UNCOV
534
            const insertQuery = this.insertTypeormMetadataSql({
×
535
                table: table.name,
536
                type: MetadataTableType.GENERATED_COLUMN,
537
                name: column.name,
538
                value: column.asExpression,
539
            })
540

UNCOV
541
            const deleteQuery = this.deleteTypeormMetadataSql({
×
542
                table: table.name,
543
                type: MetadataTableType.GENERATED_COLUMN,
544
                name: column.name,
545
            })
546

UNCOV
547
            upQueries.push(insertQuery)
×
UNCOV
548
            downQueries.push(deleteQuery)
×
549
        }
550

UNCOV
551
        await this.executeQueries(upQueries, downQueries)
×
552
    }
553

554
    /**
555
     * Drops the table.
556
     */
557
    async dropTable(
558
        tableOrName: Table | string,
559
        ifExist?: boolean,
560
        dropForeignKeys: boolean = true,
×
561
        dropIndices: boolean = true,
×
562
    ): Promise<void> {
563
        // It needs because if table does not exist and dropForeignKeys or dropIndices is true, we don't need
564
        // to perform drop queries for foreign keys and indices.
UNCOV
565
        if (ifExist) {
×
566
            const isTableExist = await this.hasTable(tableOrName)
×
567
            if (!isTableExist) return Promise.resolve()
×
568
        }
569

570
        // if dropTable called with dropForeignKeys = true, we must create foreign keys in down query.
UNCOV
571
        const createForeignKeys: boolean = dropForeignKeys
×
UNCOV
572
        const table = InstanceChecker.isTable(tableOrName)
×
573
            ? tableOrName
574
            : await this.getCachedTable(tableOrName)
UNCOV
575
        const upQueries: Query[] = []
×
UNCOV
576
        const downQueries: Query[] = []
×
577

UNCOV
578
        if (dropIndices) {
×
UNCOV
579
            table.indices.forEach((index) => {
×
UNCOV
580
                upQueries.push(this.dropIndexSql(index))
×
UNCOV
581
                downQueries.push(this.createIndexSql(table, index))
×
582
            })
583
        }
584

585
        // if dropForeignKeys is true, we just drop the table, otherwise we also drop table foreign keys.
586
        // createTable does not need separate method to create foreign keys, because it create fk's in the same query with table creation.
UNCOV
587
        if (dropForeignKeys)
×
UNCOV
588
            table.foreignKeys.forEach((foreignKey) =>
×
UNCOV
589
                upQueries.push(this.dropForeignKeySql(table, foreignKey)),
×
590
            )
591

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

595
        // if table had columns with generated type, we must remove the expression from the metadata table
UNCOV
596
        const generatedColumns = table.columns.filter(
×
UNCOV
597
            (column) => column.generatedType && column.asExpression,
×
598
        )
599

UNCOV
600
        for (const column of generatedColumns) {
×
UNCOV
601
            const deleteQuery = this.deleteTypeormMetadataSql({
×
602
                table: table.name,
603
                type: MetadataTableType.GENERATED_COLUMN,
604
                name: column.name,
605
            })
606

UNCOV
607
            const insertQuery = this.insertTypeormMetadataSql({
×
608
                table: table.name,
609
                type: MetadataTableType.GENERATED_COLUMN,
610
                name: column.name,
611
                value: column.asExpression,
612
            })
613

UNCOV
614
            upQueries.push(deleteQuery)
×
UNCOV
615
            downQueries.push(insertQuery)
×
616
        }
617

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

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

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

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

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

668
        const { database: dbName, tableName: oldTableName } =
UNCOV
669
            this.driver.parseTableName(oldTable)
×
670

UNCOV
671
        newTable.name = dbName ? `${dbName}.${newTableName}` : newTableName
×
672

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

689
        // rename primary key constraint
UNCOV
690
        if (
×
691
            newTable.primaryColumns.length > 0 &&
×
692
            !newTable.primaryColumns[0].primaryKeyConstraintName
693
        ) {
UNCOV
694
            const columnNames = newTable.primaryColumns.map(
×
UNCOV
695
                (column) => column.name,
×
696
            )
697

UNCOV
698
            const oldPkName = this.connection.namingStrategy.primaryKeyName(
×
699
                oldTable,
700
                columnNames,
701
            )
UNCOV
702
            const newPkName = this.connection.namingStrategy.primaryKeyName(
×
703
                newTable,
704
                columnNames,
705
            )
706

707
            // build queries
UNCOV
708
            upQueries.push(
×
709
                new Query(
710
                    `ALTER TABLE ${this.escapePath(
711
                        newTable,
712
                    )} RENAME CONSTRAINT "${oldPkName}" TO "${newPkName}"`,
713
                ),
714
            )
UNCOV
715
            downQueries.push(
×
716
                new Query(
717
                    `ALTER TABLE ${this.escapePath(
718
                        newTable,
719
                    )} RENAME CONSTRAINT "${newPkName}" TO "${oldPkName}"`,
720
                ),
721
            )
722
        }
723

724
        // rename unique constraints
UNCOV
725
        newTable.uniques.forEach((unique) => {
×
726
            const oldUniqueName =
UNCOV
727
                this.connection.namingStrategy.uniqueConstraintName(
×
728
                    oldTable,
729
                    unique.columnNames,
730
                )
731

732
            // Skip renaming if Unique has user defined constraint name
UNCOV
733
            if (unique.name !== oldUniqueName) return
×
734

735
            // build new constraint name
736
            const newUniqueName =
UNCOV
737
                this.connection.namingStrategy.uniqueConstraintName(
×
738
                    newTable,
739
                    unique.columnNames,
740
                )
741

742
            // build queries
UNCOV
743
            upQueries.push(
×
744
                new Query(
745
                    `ALTER TABLE ${this.escapePath(
746
                        newTable,
747
                    )} RENAME CONSTRAINT "${
748
                        unique.name
749
                    }" TO "${newUniqueName}"`,
750
                ),
751
            )
UNCOV
752
            downQueries.push(
×
753
                new Query(
754
                    `ALTER TABLE ${this.escapePath(
755
                        newTable,
756
                    )} RENAME CONSTRAINT "${newUniqueName}" TO "${
757
                        unique.name
758
                    }"`,
759
                ),
760
            )
761

762
            // replace constraint name
UNCOV
763
            unique.name = newUniqueName
×
764
        })
765

766
        // rename index constraints
UNCOV
767
        newTable.indices.forEach((index) => {
×
UNCOV
768
            const oldIndexName = this.connection.namingStrategy.indexName(
×
769
                oldTable,
770
                index.columnNames,
771
                index.where,
772
            )
773

774
            // Skip renaming if Index has user defined constraint name
UNCOV
775
            if (index.name !== oldIndexName) return
×
776

777
            // build new constraint name
UNCOV
778
            const newIndexName = this.connection.namingStrategy.indexName(
×
779
                newTable,
780
                index.columnNames,
781
                index.where,
782
            )
783

784
            // build queries
UNCOV
785
            upQueries.push(
×
786
                new Query(
787
                    `ALTER INDEX "${index.name}" RENAME TO "${newIndexName}"`,
788
                ),
789
            )
UNCOV
790
            downQueries.push(
×
791
                new Query(
792
                    `ALTER INDEX "${newIndexName}" RENAME TO "${index.name}"`,
793
                ),
794
            )
795

796
            // replace constraint name
UNCOV
797
            index.name = newIndexName
×
798
        })
799

800
        // rename foreign key constraints
UNCOV
801
        newTable.foreignKeys.forEach((foreignKey) => {
×
802
            const oldForeignKeyName =
UNCOV
803
                this.connection.namingStrategy.foreignKeyName(
×
804
                    oldTable,
805
                    foreignKey.columnNames,
806
                    this.getTablePath(foreignKey),
807
                    foreignKey.referencedColumnNames,
808
                )
809

810
            // Skip renaming if foreign key has user defined constraint name
UNCOV
811
            if (foreignKey.name !== oldForeignKeyName) return
×
812

813
            // build new constraint name
814
            const newForeignKeyName =
UNCOV
815
                this.connection.namingStrategy.foreignKeyName(
×
816
                    newTable,
817
                    foreignKey.columnNames,
818
                    this.getTablePath(foreignKey),
819
                    foreignKey.referencedColumnNames,
820
                )
821

822
            // build queries
UNCOV
823
            upQueries.push(
×
824
                new Query(
825
                    `ALTER TABLE ${this.escapePath(
826
                        newTable,
827
                    )} RENAME CONSTRAINT "${
828
                        foreignKey.name
829
                    }" TO "${newForeignKeyName}"`,
830
                ),
831
            )
UNCOV
832
            downQueries.push(
×
833
                new Query(
834
                    `ALTER TABLE ${this.escapePath(
835
                        newTable,
836
                    )} RENAME CONSTRAINT "${newForeignKeyName}" TO "${
837
                        foreignKey.name
838
                    }"`,
839
                ),
840
            )
841

842
            // replace constraint name
UNCOV
843
            foreignKey.name = newForeignKeyName
×
844
        })
845

UNCOV
846
        await this.executeQueries(upQueries, downQueries)
×
847

848
        // rename old table and replace it in cached tabled;
UNCOV
849
        oldTable.name = newTable.name
×
UNCOV
850
        this.replaceCachedTable(oldTable, newTable)
×
851
    }
852

853
    /**
854
     * Creates a new column from the column in the table.
855
     */
856
    async addColumn(
857
        tableOrName: Table | string,
858
        column: TableColumn,
859
    ): Promise<void> {
UNCOV
860
        const table = InstanceChecker.isTable(tableOrName)
×
861
            ? tableOrName
862
            : await this.getCachedTable(tableOrName)
UNCOV
863
        const clonedTable = table.clone()
×
UNCOV
864
        const upQueries: Query[] = []
×
UNCOV
865
        const downQueries: Query[] = []
×
866

UNCOV
867
        upQueries.push(
×
868
            new Query(
869
                `ALTER TABLE ${this.escapePath(
870
                    table,
871
                )} ADD ${this.buildCreateColumnSql(column)}`,
872
            ),
873
        )
UNCOV
874
        downQueries.push(
×
875
            new Query(
876
                `ALTER TABLE ${this.escapePath(table)} DROP COLUMN "${
877
                    column.name
878
                }"`,
879
            ),
880
        )
881

882
        // create or update primary key constraint
UNCOV
883
        if (column.isPrimary) {
×
UNCOV
884
            const primaryColumns = clonedTable.primaryColumns
×
885
            // if table already have primary key, me must drop it and recreate again
UNCOV
886
            if (primaryColumns.length > 0) {
×
UNCOV
887
                const pkName = primaryColumns[0].primaryKeyConstraintName
×
888
                    ? primaryColumns[0].primaryKeyConstraintName
889
                    : this.connection.namingStrategy.primaryKeyName(
890
                          clonedTable,
UNCOV
891
                          primaryColumns.map((column) => column.name),
×
892
                      )
893

UNCOV
894
                const columnNames = primaryColumns
×
UNCOV
895
                    .map((column) => `"${column.name}"`)
×
896
                    .join(", ")
897

UNCOV
898
                upQueries.push(
×
899
                    new Query(
900
                        `ALTER TABLE ${this.escapePath(
901
                            table,
902
                        )} DROP CONSTRAINT "${pkName}"`,
903
                    ),
904
                )
UNCOV
905
                downQueries.push(
×
906
                    new Query(
907
                        `ALTER TABLE ${this.escapePath(
908
                            table,
909
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
910
                    ),
911
                )
912
            }
913

UNCOV
914
            primaryColumns.push(column)
×
UNCOV
915
            const pkName = primaryColumns[0].primaryKeyConstraintName
×
916
                ? primaryColumns[0].primaryKeyConstraintName
917
                : this.connection.namingStrategy.primaryKeyName(
918
                      clonedTable,
UNCOV
919
                      primaryColumns.map((column) => column.name),
×
920
                  )
921

UNCOV
922
            const columnNames = primaryColumns
×
UNCOV
923
                .map((column) => `"${column.name}"`)
×
924
                .join(", ")
925

UNCOV
926
            upQueries.push(
×
927
                new Query(
928
                    `ALTER TABLE ${this.escapePath(
929
                        table,
930
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
931
                ),
932
            )
UNCOV
933
            downQueries.push(
×
934
                new Query(
935
                    `ALTER TABLE ${this.escapePath(
936
                        table,
937
                    )} DROP CONSTRAINT "${pkName}"`,
938
                ),
939
            )
940
        }
941

942
        // create column index
UNCOV
943
        const columnIndex = clonedTable.indices.find(
×
944
            (index) =>
UNCOV
945
                index.columnNames.length === 1 &&
×
946
                index.columnNames[0] === column.name,
947
        )
UNCOV
948
        if (columnIndex) {
×
949
            clonedTable.indices.splice(
×
950
                clonedTable.indices.indexOf(columnIndex),
951
                1,
952
            )
953
            upQueries.push(this.createIndexSql(table, columnIndex))
×
954
            downQueries.push(this.dropIndexSql(columnIndex))
×
955
        }
956

957
        // create unique constraint
UNCOV
958
        if (column.isUnique) {
×
UNCOV
959
            const uniqueConstraint = new TableUnique({
×
960
                name: this.connection.namingStrategy.uniqueConstraintName(
961
                    table,
962
                    [column.name],
963
                ),
964
                columnNames: [column.name],
965
            })
UNCOV
966
            clonedTable.uniques.push(uniqueConstraint)
×
UNCOV
967
            upQueries.push(
×
968
                new Query(
969
                    `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
970
                        uniqueConstraint.name
971
                    }" UNIQUE ("${column.name}")`,
972
                ),
973
            )
UNCOV
974
            downQueries.push(
×
975
                new Query(
976
                    `ALTER TABLE ${this.escapePath(table)} DROP CONSTRAINT "${
977
                        uniqueConstraint.name
978
                    }"`,
979
                ),
980
            )
981
        }
982

UNCOV
983
        if (column.generatedType && column.asExpression) {
×
UNCOV
984
            const insertQuery = this.insertTypeormMetadataSql({
×
985
                table: table.name,
986
                type: MetadataTableType.GENERATED_COLUMN,
987
                name: column.name,
988
                value: column.asExpression,
989
            })
990

UNCOV
991
            const deleteQuery = this.deleteTypeormMetadataSql({
×
992
                table: table.name,
993
                type: MetadataTableType.GENERATED_COLUMN,
994
                name: column.name,
995
            })
996

UNCOV
997
            upQueries.push(insertQuery)
×
UNCOV
998
            downQueries.push(deleteQuery)
×
999
        }
1000

UNCOV
1001
        await this.executeQueries(upQueries, downQueries)
×
1002

UNCOV
1003
        clonedTable.addColumn(column)
×
UNCOV
1004
        this.replaceCachedTable(table, clonedTable)
×
1005
    }
1006

1007
    /**
1008
     * Creates a new columns from the column in the table.
1009
     */
1010
    async addColumns(
1011
        tableOrName: Table | string,
1012
        columns: TableColumn[],
1013
    ): Promise<void> {
UNCOV
1014
        for (const column of columns) {
×
UNCOV
1015
            await this.addColumn(tableOrName, column)
×
1016
        }
1017
    }
1018

1019
    /**
1020
     * Renames column in the given table.
1021
     */
1022
    async renameColumn(
1023
        tableOrName: Table | string,
1024
        oldTableColumnOrName: TableColumn | string,
1025
        newTableColumnOrName: TableColumn | string,
1026
    ): Promise<void> {
UNCOV
1027
        const table = InstanceChecker.isTable(tableOrName)
×
1028
            ? tableOrName
1029
            : await this.getCachedTable(tableOrName)
UNCOV
1030
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
×
1031
            ? oldTableColumnOrName
UNCOV
1032
            : table.columns.find((c) => c.name === oldTableColumnOrName)
×
UNCOV
1033
        if (!oldColumn)
×
1034
            throw new TypeORMError(
×
1035
                `Column "${oldTableColumnOrName}" was not found in the ${this.escapePath(
1036
                    table,
1037
                )} table.`,
1038
            )
1039

UNCOV
1040
        let newColumn: TableColumn | undefined = undefined
×
UNCOV
1041
        if (InstanceChecker.isTableColumn(newTableColumnOrName)) {
×
UNCOV
1042
            newColumn = newTableColumnOrName
×
1043
        } else {
UNCOV
1044
            newColumn = oldColumn.clone()
×
UNCOV
1045
            newColumn.name = newTableColumnOrName
×
1046
        }
1047

UNCOV
1048
        await this.changeColumn(table, oldColumn, newColumn)
×
1049
    }
1050

1051
    /**
1052
     * Changes a column in the table.
1053
     */
1054
    async changeColumn(
1055
        tableOrName: Table | string,
1056
        oldTableColumnOrName: TableColumn | string,
1057
        newColumn: TableColumn,
1058
    ): Promise<void> {
UNCOV
1059
        const table = InstanceChecker.isTable(tableOrName)
×
1060
            ? tableOrName
1061
            : await this.getCachedTable(tableOrName)
UNCOV
1062
        let clonedTable = table.clone()
×
UNCOV
1063
        const upQueries: Query[] = []
×
UNCOV
1064
        const downQueries: Query[] = []
×
1065

UNCOV
1066
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
×
1067
            ? oldTableColumnOrName
1068
            : table.columns.find(
1069
                  (column) => column.name === oldTableColumnOrName,
×
1070
              )
UNCOV
1071
        if (!oldColumn)
×
1072
            throw new TypeORMError(
×
1073
                `Column "${oldTableColumnOrName}" was not found in the ${this.escapePath(
1074
                    table,
1075
                )} table.`,
1076
            )
1077

UNCOV
1078
        if (
×
1079
            (newColumn.isGenerated !== oldColumn.isGenerated &&
×
1080
                newColumn.generationStrategy !== "uuid") ||
1081
            oldColumn.type !== newColumn.type ||
1082
            oldColumn.length !== newColumn.length ||
1083
            oldColumn.generatedType !== newColumn.generatedType ||
1084
            oldColumn.asExpression !== newColumn.asExpression
1085
        ) {
1086
            // Oracle does not support changing of IDENTITY column, so we must drop column and recreate it again.
1087
            // Also, we recreate column if column type changed
UNCOV
1088
            await this.dropColumn(table, oldColumn)
×
UNCOV
1089
            await this.addColumn(table, newColumn)
×
1090

1091
            // update cloned table
UNCOV
1092
            clonedTable = table.clone()
×
1093
        } else {
UNCOV
1094
            if (newColumn.name !== oldColumn.name) {
×
1095
                // rename column
UNCOV
1096
                upQueries.push(
×
1097
                    new Query(
1098
                        `ALTER TABLE ${this.escapePath(table)} RENAME COLUMN "${
1099
                            oldColumn.name
1100
                        }" TO "${newColumn.name}"`,
1101
                    ),
1102
                )
UNCOV
1103
                downQueries.push(
×
1104
                    new Query(
1105
                        `ALTER TABLE ${this.escapePath(table)} RENAME COLUMN "${
1106
                            newColumn.name
1107
                        }" TO "${oldColumn.name}"`,
1108
                    ),
1109
                )
1110

1111
                // rename column primary key constraint
UNCOV
1112
                if (
×
1113
                    oldColumn.isPrimary === true &&
×
1114
                    !oldColumn.primaryKeyConstraintName
1115
                ) {
UNCOV
1116
                    const primaryColumns = clonedTable.primaryColumns
×
1117

1118
                    // build old primary constraint name
UNCOV
1119
                    const columnNames = primaryColumns.map(
×
UNCOV
1120
                        (column) => column.name,
×
1121
                    )
1122
                    const oldPkName =
UNCOV
1123
                        this.connection.namingStrategy.primaryKeyName(
×
1124
                            clonedTable,
1125
                            columnNames,
1126
                        )
1127

1128
                    // replace old column name with new column name
UNCOV
1129
                    columnNames.splice(columnNames.indexOf(oldColumn.name), 1)
×
UNCOV
1130
                    columnNames.push(newColumn.name)
×
1131

1132
                    // build new primary constraint name
1133
                    const newPkName =
UNCOV
1134
                        this.connection.namingStrategy.primaryKeyName(
×
1135
                            clonedTable,
1136
                            columnNames,
1137
                        )
1138

UNCOV
1139
                    upQueries.push(
×
1140
                        new Query(
1141
                            `ALTER TABLE ${this.escapePath(
1142
                                table,
1143
                            )} RENAME CONSTRAINT "${oldPkName}" TO "${newPkName}"`,
1144
                        ),
1145
                    )
UNCOV
1146
                    downQueries.push(
×
1147
                        new Query(
1148
                            `ALTER TABLE ${this.escapePath(
1149
                                table,
1150
                            )} RENAME CONSTRAINT "${newPkName}" TO "${oldPkName}"`,
1151
                        ),
1152
                    )
1153
                }
1154

1155
                // rename unique constraints
UNCOV
1156
                clonedTable.findColumnUniques(oldColumn).forEach((unique) => {
×
1157
                    const oldUniqueName =
UNCOV
1158
                        this.connection.namingStrategy.uniqueConstraintName(
×
1159
                            clonedTable,
1160
                            unique.columnNames,
1161
                        )
1162

1163
                    // Skip renaming if Unique has user defined constraint name
UNCOV
1164
                    if (unique.name !== oldUniqueName) return
×
1165

1166
                    // build new constraint name
UNCOV
1167
                    unique.columnNames.splice(
×
1168
                        unique.columnNames.indexOf(oldColumn.name),
1169
                        1,
1170
                    )
UNCOV
1171
                    unique.columnNames.push(newColumn.name)
×
1172
                    const newUniqueName =
UNCOV
1173
                        this.connection.namingStrategy.uniqueConstraintName(
×
1174
                            clonedTable,
1175
                            unique.columnNames,
1176
                        )
1177

1178
                    // build queries
UNCOV
1179
                    upQueries.push(
×
1180
                        new Query(
1181
                            `ALTER TABLE ${this.escapePath(
1182
                                table,
1183
                            )} RENAME CONSTRAINT "${
1184
                                unique.name
1185
                            }" TO "${newUniqueName}"`,
1186
                        ),
1187
                    )
UNCOV
1188
                    downQueries.push(
×
1189
                        new Query(
1190
                            `ALTER TABLE ${this.escapePath(
1191
                                table,
1192
                            )} RENAME CONSTRAINT "${newUniqueName}" TO "${
1193
                                unique.name
1194
                            }"`,
1195
                        ),
1196
                    )
1197

1198
                    // replace constraint name
UNCOV
1199
                    unique.name = newUniqueName
×
1200
                })
1201

1202
                // rename index constraints
UNCOV
1203
                clonedTable.findColumnIndices(oldColumn).forEach((index) => {
×
1204
                    const oldIndexName =
UNCOV
1205
                        this.connection.namingStrategy.indexName(
×
1206
                            clonedTable,
1207
                            index.columnNames,
1208
                            index.where,
1209
                        )
1210

1211
                    // Skip renaming if Index has user defined constraint name
UNCOV
1212
                    if (index.name !== oldIndexName) return
×
1213

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

1227
                    // build queries
UNCOV
1228
                    upQueries.push(
×
1229
                        new Query(
1230
                            `ALTER INDEX "${index.name}" RENAME TO "${newIndexName}"`,
1231
                        ),
1232
                    )
UNCOV
1233
                    downQueries.push(
×
1234
                        new Query(
1235
                            `ALTER INDEX "${newIndexName}" RENAME TO "${index.name}"`,
1236
                        ),
1237
                    )
1238

1239
                    // replace constraint name
UNCOV
1240
                    index.name = newIndexName
×
1241
                })
1242

1243
                // rename foreign key constraints
UNCOV
1244
                clonedTable
×
1245
                    .findColumnForeignKeys(oldColumn)
1246
                    .forEach((foreignKey) => {
1247
                        const foreignKeyName =
UNCOV
1248
                            this.connection.namingStrategy.foreignKeyName(
×
1249
                                clonedTable,
1250
                                foreignKey.columnNames,
1251
                                this.getTablePath(foreignKey),
1252
                                foreignKey.referencedColumnNames,
1253
                            )
1254

1255
                        // Skip renaming if foreign key has user defined constraint name
UNCOV
1256
                        if (foreignKey.name !== foreignKeyName) return
×
1257

1258
                        // build new constraint name
UNCOV
1259
                        foreignKey.columnNames.splice(
×
1260
                            foreignKey.columnNames.indexOf(oldColumn.name),
1261
                            1,
1262
                        )
UNCOV
1263
                        foreignKey.columnNames.push(newColumn.name)
×
1264
                        const newForeignKeyName =
UNCOV
1265
                            this.connection.namingStrategy.foreignKeyName(
×
1266
                                clonedTable,
1267
                                foreignKey.columnNames,
1268
                                this.getTablePath(foreignKey),
1269
                                foreignKey.referencedColumnNames,
1270
                            )
1271

1272
                        // build queries
UNCOV
1273
                        upQueries.push(
×
1274
                            new Query(
1275
                                `ALTER TABLE ${this.escapePath(
1276
                                    table,
1277
                                )} RENAME CONSTRAINT "${
1278
                                    foreignKey.name
1279
                                }" TO "${newForeignKeyName}"`,
1280
                            ),
1281
                        )
UNCOV
1282
                        downQueries.push(
×
1283
                            new Query(
1284
                                `ALTER TABLE ${this.escapePath(
1285
                                    table,
1286
                                )} RENAME CONSTRAINT "${newForeignKeyName}" TO "${
1287
                                    foreignKey.name
1288
                                }"`,
1289
                            ),
1290
                        )
1291

1292
                        // replace constraint name
UNCOV
1293
                        foreignKey.name = newForeignKeyName
×
1294
                    })
1295

1296
                // rename old column in the Table object
UNCOV
1297
                const oldTableColumn = clonedTable.columns.find(
×
UNCOV
1298
                    (column) => column.name === oldColumn.name,
×
1299
                )
UNCOV
1300
                clonedTable.columns[
×
1301
                    clonedTable.columns.indexOf(oldTableColumn!)
1302
                ].name = newColumn.name
UNCOV
1303
                oldColumn.name = newColumn.name
×
1304
            }
1305

UNCOV
1306
            if (this.isColumnChanged(oldColumn, newColumn, true)) {
×
UNCOV
1307
                let defaultUp: string = ""
×
UNCOV
1308
                let defaultDown: string = ""
×
UNCOV
1309
                let nullableUp: string = ""
×
UNCOV
1310
                let nullableDown: string = ""
×
1311

1312
                // changing column default
UNCOV
1313
                if (
×
1314
                    newColumn.default !== null &&
×
1315
                    newColumn.default !== undefined
1316
                ) {
UNCOV
1317
                    defaultUp = `DEFAULT ${newColumn.default}`
×
1318

UNCOV
1319
                    if (
×
1320
                        oldColumn.default !== null &&
×
1321
                        oldColumn.default !== undefined
1322
                    ) {
UNCOV
1323
                        defaultDown = `DEFAULT ${oldColumn.default}`
×
1324
                    } else {
UNCOV
1325
                        defaultDown = "DEFAULT NULL"
×
1326
                    }
1327
                } else if (
×
1328
                    oldColumn.default !== null &&
×
1329
                    oldColumn.default !== undefined
1330
                ) {
1331
                    defaultUp = "DEFAULT NULL"
×
1332
                    defaultDown = `DEFAULT ${oldColumn.default}`
×
1333
                }
1334

1335
                // changing column isNullable property
UNCOV
1336
                if (newColumn.isNullable !== oldColumn.isNullable) {
×
1337
                    if (newColumn.isNullable === true) {
×
1338
                        nullableUp = "NULL"
×
1339
                        nullableDown = "NOT NULL"
×
1340
                    } else {
1341
                        nullableUp = "NOT NULL"
×
1342
                        nullableDown = "NULL"
×
1343
                    }
1344
                }
1345

UNCOV
1346
                upQueries.push(
×
1347
                    new Query(
1348
                        `ALTER TABLE ${this.escapePath(table)} MODIFY "${
1349
                            oldColumn.name
1350
                        }" ${this.connection.driver.createFullType(
1351
                            newColumn,
1352
                        )} ${defaultUp} ${nullableUp}`,
1353
                    ),
1354
                )
UNCOV
1355
                downQueries.push(
×
1356
                    new Query(
1357
                        `ALTER TABLE ${this.escapePath(table)} MODIFY "${
1358
                            oldColumn.name
1359
                        }" ${this.connection.driver.createFullType(
1360
                            oldColumn,
1361
                        )} ${defaultDown} ${nullableDown}`,
1362
                    ),
1363
                )
1364
            }
1365

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

1369
                // if primary column state changed, we must always drop existed constraint.
UNCOV
1370
                if (primaryColumns.length > 0) {
×
UNCOV
1371
                    const pkName = primaryColumns[0].primaryKeyConstraintName
×
1372
                        ? primaryColumns[0].primaryKeyConstraintName
1373
                        : this.connection.namingStrategy.primaryKeyName(
1374
                              clonedTable,
UNCOV
1375
                              primaryColumns.map((column) => column.name),
×
1376
                          )
1377

UNCOV
1378
                    const columnNames = primaryColumns
×
UNCOV
1379
                        .map((column) => `"${column.name}"`)
×
1380
                        .join(", ")
1381

UNCOV
1382
                    upQueries.push(
×
1383
                        new Query(
1384
                            `ALTER TABLE ${this.escapePath(
1385
                                table,
1386
                            )} DROP CONSTRAINT "${pkName}"`,
1387
                        ),
1388
                    )
UNCOV
1389
                    downQueries.push(
×
1390
                        new Query(
1391
                            `ALTER TABLE ${this.escapePath(
1392
                                table,
1393
                            )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1394
                        ),
1395
                    )
1396
                }
1397

UNCOV
1398
                if (newColumn.isPrimary === true) {
×
UNCOV
1399
                    primaryColumns.push(newColumn)
×
1400
                    // update column in table
UNCOV
1401
                    const column = clonedTable.columns.find(
×
UNCOV
1402
                        (column) => column.name === newColumn.name,
×
1403
                    )
UNCOV
1404
                    column!.isPrimary = true
×
UNCOV
1405
                    const pkName = primaryColumns[0].primaryKeyConstraintName
×
1406
                        ? primaryColumns[0].primaryKeyConstraintName
1407
                        : this.connection.namingStrategy.primaryKeyName(
1408
                              clonedTable,
UNCOV
1409
                              primaryColumns.map((column) => column.name),
×
1410
                          )
1411

UNCOV
1412
                    const columnNames = primaryColumns
×
UNCOV
1413
                        .map((column) => `"${column.name}"`)
×
1414
                        .join(", ")
1415

UNCOV
1416
                    upQueries.push(
×
1417
                        new Query(
1418
                            `ALTER TABLE ${this.escapePath(
1419
                                table,
1420
                            )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1421
                        ),
1422
                    )
UNCOV
1423
                    downQueries.push(
×
1424
                        new Query(
1425
                            `ALTER TABLE ${this.escapePath(
1426
                                table,
1427
                            )} DROP CONSTRAINT "${pkName}"`,
1428
                        ),
1429
                    )
1430
                } else {
UNCOV
1431
                    const primaryColumn = primaryColumns.find(
×
UNCOV
1432
                        (c) => c.name === newColumn.name,
×
1433
                    )
UNCOV
1434
                    primaryColumns.splice(
×
1435
                        primaryColumns.indexOf(primaryColumn!),
1436
                        1,
1437
                    )
1438

1439
                    // update column in table
UNCOV
1440
                    const column = clonedTable.columns.find(
×
UNCOV
1441
                        (column) => column.name === newColumn.name,
×
1442
                    )
UNCOV
1443
                    column!.isPrimary = false
×
1444

1445
                    // if we have another primary keys, we must recreate constraint.
UNCOV
1446
                    if (primaryColumns.length > 0) {
×
UNCOV
1447
                        const pkName = primaryColumns[0]
×
1448
                            .primaryKeyConstraintName
1449
                            ? primaryColumns[0].primaryKeyConstraintName
1450
                            : this.connection.namingStrategy.primaryKeyName(
1451
                                  clonedTable,
UNCOV
1452
                                  primaryColumns.map((column) => column.name),
×
1453
                              )
1454

UNCOV
1455
                        const columnNames = primaryColumns
×
UNCOV
1456
                            .map((column) => `"${column.name}"`)
×
1457
                            .join(", ")
1458

UNCOV
1459
                        upQueries.push(
×
1460
                            new Query(
1461
                                `ALTER TABLE ${this.escapePath(
1462
                                    table,
1463
                                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1464
                            ),
1465
                        )
UNCOV
1466
                        downQueries.push(
×
1467
                            new Query(
1468
                                `ALTER TABLE ${this.escapePath(
1469
                                    table,
1470
                                )} DROP CONSTRAINT "${pkName}"`,
1471
                            ),
1472
                        )
1473
                    }
1474
                }
1475
            }
1476

UNCOV
1477
            if (newColumn.isUnique !== oldColumn.isUnique) {
×
UNCOV
1478
                if (newColumn.isUnique === true) {
×
UNCOV
1479
                    const uniqueConstraint = new TableUnique({
×
1480
                        name: this.connection.namingStrategy.uniqueConstraintName(
1481
                            table,
1482
                            [newColumn.name],
1483
                        ),
1484
                        columnNames: [newColumn.name],
1485
                    })
UNCOV
1486
                    clonedTable.uniques.push(uniqueConstraint)
×
UNCOV
1487
                    upQueries.push(
×
1488
                        new Query(
1489
                            `ALTER TABLE ${this.escapePath(
1490
                                table,
1491
                            )} ADD CONSTRAINT "${
1492
                                uniqueConstraint.name
1493
                            }" UNIQUE ("${newColumn.name}")`,
1494
                        ),
1495
                    )
UNCOV
1496
                    downQueries.push(
×
1497
                        new Query(
1498
                            `ALTER TABLE ${this.escapePath(
1499
                                table,
1500
                            )} DROP CONSTRAINT "${uniqueConstraint.name}"`,
1501
                        ),
1502
                    )
1503
                } else {
1504
                    const uniqueConstraint = clonedTable.uniques.find(
×
1505
                        (unique) => {
1506
                            return (
×
1507
                                unique.columnNames.length === 1 &&
×
1508
                                !!unique.columnNames.find(
1509
                                    (columnName) =>
1510
                                        columnName === newColumn.name,
×
1511
                                )
1512
                            )
1513
                        },
1514
                    )
1515
                    clonedTable.uniques.splice(
×
1516
                        clonedTable.uniques.indexOf(uniqueConstraint!),
1517
                        1,
1518
                    )
1519
                    upQueries.push(
×
1520
                        new Query(
1521
                            `ALTER TABLE ${this.escapePath(
1522
                                table,
1523
                            )} DROP CONSTRAINT "${uniqueConstraint!.name}"`,
1524
                        ),
1525
                    )
1526
                    downQueries.push(
×
1527
                        new Query(
1528
                            `ALTER TABLE ${this.escapePath(
1529
                                table,
1530
                            )} ADD CONSTRAINT "${
1531
                                uniqueConstraint!.name
1532
                            }" UNIQUE ("${newColumn.name}")`,
1533
                        ),
1534
                    )
1535
                }
1536
            }
1537

UNCOV
1538
            await this.executeQueries(upQueries, downQueries)
×
UNCOV
1539
            this.replaceCachedTable(table, clonedTable)
×
1540
        }
1541
    }
1542

1543
    /**
1544
     * Changes a column in the table.
1545
     */
1546
    async changeColumns(
1547
        tableOrName: Table | string,
1548
        changedColumns: { newColumn: TableColumn; oldColumn: TableColumn }[],
1549
    ): Promise<void> {
UNCOV
1550
        for (const { oldColumn, newColumn } of changedColumns) {
×
UNCOV
1551
            await this.changeColumn(tableOrName, oldColumn, newColumn)
×
1552
        }
1553
    }
1554

1555
    /**
1556
     * Drops column in the table.
1557
     */
1558
    async dropColumn(
1559
        tableOrName: Table | string,
1560
        columnOrName: TableColumn | string,
1561
    ): Promise<void> {
UNCOV
1562
        const table = InstanceChecker.isTable(tableOrName)
×
1563
            ? tableOrName
1564
            : await this.getCachedTable(tableOrName)
UNCOV
1565
        const column = InstanceChecker.isTableColumn(columnOrName)
×
1566
            ? columnOrName
1567
            : table.findColumnByName(columnOrName)
UNCOV
1568
        if (!column)
×
UNCOV
1569
            throw new TypeORMError(
×
1570
                `Column "${columnOrName}" was not found in table ${this.escapePath(
1571
                    table,
1572
                )}`,
1573
            )
1574

UNCOV
1575
        const clonedTable = table.clone()
×
UNCOV
1576
        const upQueries: Query[] = []
×
UNCOV
1577
        const downQueries: Query[] = []
×
1578

1579
        // drop primary key constraint
UNCOV
1580
        if (column.isPrimary) {
×
UNCOV
1581
            const pkName = column.primaryKeyConstraintName
×
1582
                ? column.primaryKeyConstraintName
1583
                : this.connection.namingStrategy.primaryKeyName(
1584
                      clonedTable,
UNCOV
1585
                      clonedTable.primaryColumns.map((column) => column.name),
×
1586
                  )
1587

UNCOV
1588
            const columnNames = clonedTable.primaryColumns
×
UNCOV
1589
                .map((primaryColumn) => `"${primaryColumn.name}"`)
×
1590
                .join(", ")
1591

UNCOV
1592
            upQueries.push(
×
1593
                new Query(
1594
                    `ALTER TABLE ${this.escapePath(
1595
                        clonedTable,
1596
                    )} DROP CONSTRAINT "${pkName}"`,
1597
                ),
1598
            )
UNCOV
1599
            downQueries.push(
×
1600
                new Query(
1601
                    `ALTER TABLE ${this.escapePath(
1602
                        clonedTable,
1603
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1604
                ),
1605
            )
1606

1607
            // update column in table
UNCOV
1608
            const tableColumn = clonedTable.findColumnByName(column.name)
×
UNCOV
1609
            tableColumn!.isPrimary = false
×
1610

1611
            // if primary key have multiple columns, we must recreate it without dropped column
UNCOV
1612
            if (clonedTable.primaryColumns.length > 0) {
×
1613
                const pkName = clonedTable.primaryColumns[0]
×
1614
                    .primaryKeyConstraintName
1615
                    ? clonedTable.primaryColumns[0].primaryKeyConstraintName
1616
                    : this.connection.namingStrategy.primaryKeyName(
1617
                          clonedTable,
1618
                          clonedTable.primaryColumns.map(
1619
                              (column) => column.name,
×
1620
                          ),
1621
                      )
1622

1623
                const columnNames = clonedTable.primaryColumns
×
1624
                    .map((primaryColumn) => `"${primaryColumn.name}"`)
×
1625
                    .join(", ")
1626

1627
                upQueries.push(
×
1628
                    new Query(
1629
                        `ALTER TABLE ${this.escapePath(
1630
                            clonedTable,
1631
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1632
                    ),
1633
                )
1634
                downQueries.push(
×
1635
                    new Query(
1636
                        `ALTER TABLE ${this.escapePath(
1637
                            clonedTable,
1638
                        )} DROP CONSTRAINT "${pkName}"`,
1639
                    ),
1640
                )
1641
            }
1642
        }
1643

1644
        // drop column index
UNCOV
1645
        const columnIndex = clonedTable.indices.find(
×
1646
            (index) =>
UNCOV
1647
                index.columnNames.length === 1 &&
×
1648
                index.columnNames[0] === column.name,
1649
        )
UNCOV
1650
        if (columnIndex) {
×
1651
            upQueries.push(this.dropIndexSql(columnIndex))
×
1652
            downQueries.push(this.createIndexSql(table, columnIndex))
×
1653
        }
1654

1655
        // drop column check
UNCOV
1656
        const columnCheck = clonedTable.checks.find(
×
1657
            (check) =>
UNCOV
1658
                !!check.columnNames &&
×
1659
                check.columnNames.length === 1 &&
1660
                check.columnNames[0] === column.name,
1661
        )
UNCOV
1662
        if (columnCheck) {
×
UNCOV
1663
            clonedTable.checks.splice(
×
1664
                clonedTable.checks.indexOf(columnCheck),
1665
                1,
1666
            )
UNCOV
1667
            upQueries.push(this.dropCheckConstraintSql(table, columnCheck))
×
UNCOV
1668
            downQueries.push(this.createCheckConstraintSql(table, columnCheck))
×
1669
        }
1670

1671
        // drop column unique
UNCOV
1672
        const columnUnique = clonedTable.uniques.find(
×
1673
            (unique) =>
UNCOV
1674
                unique.columnNames.length === 1 &&
×
1675
                unique.columnNames[0] === column.name,
1676
        )
UNCOV
1677
        if (columnUnique) {
×
UNCOV
1678
            clonedTable.uniques.splice(
×
1679
                clonedTable.uniques.indexOf(columnUnique),
1680
                1,
1681
            )
UNCOV
1682
            upQueries.push(this.dropUniqueConstraintSql(table, columnUnique))
×
UNCOV
1683
            downQueries.push(
×
1684
                this.createUniqueConstraintSql(table, columnUnique),
1685
            )
1686
        }
1687

UNCOV
1688
        upQueries.push(
×
1689
            new Query(
1690
                `ALTER TABLE ${this.escapePath(table)} DROP COLUMN "${
1691
                    column.name
1692
                }"`,
1693
            ),
1694
        )
UNCOV
1695
        downQueries.push(
×
1696
            new Query(
1697
                `ALTER TABLE ${this.escapePath(
1698
                    table,
1699
                )} ADD ${this.buildCreateColumnSql(column)}`,
1700
            ),
1701
        )
1702

UNCOV
1703
        if (column.generatedType && column.asExpression) {
×
UNCOV
1704
            const deleteQuery = this.deleteTypeormMetadataSql({
×
1705
                table: table.name,
1706
                type: MetadataTableType.GENERATED_COLUMN,
1707
                name: column.name,
1708
            })
UNCOV
1709
            const insertQuery = this.insertTypeormMetadataSql({
×
1710
                table: table.name,
1711
                type: MetadataTableType.GENERATED_COLUMN,
1712
                name: column.name,
1713
                value: column.asExpression,
1714
            })
1715

UNCOV
1716
            upQueries.push(deleteQuery)
×
UNCOV
1717
            downQueries.push(insertQuery)
×
1718
        }
1719

UNCOV
1720
        await this.executeQueries(upQueries, downQueries)
×
1721

UNCOV
1722
        clonedTable.removeColumn(column)
×
UNCOV
1723
        this.replaceCachedTable(table, clonedTable)
×
1724
    }
1725

1726
    /**
1727
     * Drops the columns in the table.
1728
     */
1729
    async dropColumns(
1730
        tableOrName: Table | string,
1731
        columns: TableColumn[] | string[],
1732
    ): Promise<void> {
UNCOV
1733
        for (const column of columns) {
×
UNCOV
1734
            await this.dropColumn(tableOrName, column)
×
1735
        }
1736
    }
1737

1738
    /**
1739
     * Creates a new primary key.
1740
     */
1741
    async createPrimaryKey(
1742
        tableOrName: Table | string,
1743
        columnNames: string[],
1744
        constraintName?: string,
1745
    ): Promise<void> {
UNCOV
1746
        const table = InstanceChecker.isTable(tableOrName)
×
1747
            ? tableOrName
1748
            : await this.getCachedTable(tableOrName)
UNCOV
1749
        const clonedTable = table.clone()
×
1750

UNCOV
1751
        const up = this.createPrimaryKeySql(table, columnNames, constraintName)
×
1752

1753
        // mark columns as primary, because dropPrimaryKeySql build constraint name from table primary column names.
UNCOV
1754
        clonedTable.columns.forEach((column) => {
×
UNCOV
1755
            if (columnNames.find((columnName) => columnName === column.name))
×
UNCOV
1756
                column.isPrimary = true
×
1757
        })
UNCOV
1758
        const down = this.dropPrimaryKeySql(clonedTable)
×
1759

UNCOV
1760
        await this.executeQueries(up, down)
×
UNCOV
1761
        this.replaceCachedTable(table, clonedTable)
×
1762
    }
1763

1764
    /**
1765
     * Updates composite primary keys.
1766
     */
1767
    async updatePrimaryKeys(
1768
        tableOrName: Table | string,
1769
        columns: TableColumn[],
1770
    ): Promise<void> {
UNCOV
1771
        const table = InstanceChecker.isTable(tableOrName)
×
1772
            ? tableOrName
1773
            : await this.getCachedTable(tableOrName)
UNCOV
1774
        const columnNames = columns.map((column) => column.name)
×
UNCOV
1775
        const clonedTable = table.clone()
×
UNCOV
1776
        const upQueries: Query[] = []
×
UNCOV
1777
        const downQueries: Query[] = []
×
1778

1779
        // if table already have primary columns, we must drop them.
UNCOV
1780
        const primaryColumns = clonedTable.primaryColumns
×
UNCOV
1781
        if (primaryColumns.length > 0) {
×
UNCOV
1782
            const pkName = primaryColumns[0].primaryKeyConstraintName
×
1783
                ? primaryColumns[0].primaryKeyConstraintName
1784
                : this.connection.namingStrategy.primaryKeyName(
1785
                      clonedTable,
UNCOV
1786
                      primaryColumns.map((column) => column.name),
×
1787
                  )
1788

UNCOV
1789
            const columnNamesString = primaryColumns
×
UNCOV
1790
                .map((column) => `"${column.name}"`)
×
1791
                .join(", ")
1792

UNCOV
1793
            upQueries.push(
×
1794
                new Query(
1795
                    `ALTER TABLE ${this.escapePath(
1796
                        table,
1797
                    )} DROP CONSTRAINT "${pkName}"`,
1798
                ),
1799
            )
UNCOV
1800
            downQueries.push(
×
1801
                new Query(
1802
                    `ALTER TABLE ${this.escapePath(
1803
                        table,
1804
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
1805
                ),
1806
            )
1807
        }
1808

1809
        // update columns in table.
UNCOV
1810
        clonedTable.columns
×
UNCOV
1811
            .filter((column) => columnNames.indexOf(column.name) !== -1)
×
UNCOV
1812
            .forEach((column) => (column.isPrimary = true))
×
1813

UNCOV
1814
        const pkName = primaryColumns[0].primaryKeyConstraintName
×
1815
            ? primaryColumns[0].primaryKeyConstraintName
1816
            : this.connection.namingStrategy.primaryKeyName(
1817
                  clonedTable,
1818
                  columnNames,
1819
              )
1820

UNCOV
1821
        const columnNamesString = columnNames
×
UNCOV
1822
            .map((columnName) => `"${columnName}"`)
×
1823
            .join(", ")
UNCOV
1824
        upQueries.push(
×
1825
            new Query(
1826
                `ALTER TABLE ${this.escapePath(
1827
                    table,
1828
                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
1829
            ),
1830
        )
UNCOV
1831
        downQueries.push(
×
1832
            new Query(
1833
                `ALTER TABLE ${this.escapePath(
1834
                    table,
1835
                )} DROP CONSTRAINT "${pkName}"`,
1836
            ),
1837
        )
1838

UNCOV
1839
        await this.executeQueries(upQueries, downQueries)
×
UNCOV
1840
        this.replaceCachedTable(table, clonedTable)
×
1841
    }
1842

1843
    /**
1844
     * Drops a primary key.
1845
     */
1846
    async dropPrimaryKey(
1847
        tableOrName: Table | string,
1848
        constraintName?: string,
1849
    ): Promise<void> {
UNCOV
1850
        const table = InstanceChecker.isTable(tableOrName)
×
1851
            ? tableOrName
1852
            : await this.getCachedTable(tableOrName)
UNCOV
1853
        const up = this.dropPrimaryKeySql(table)
×
UNCOV
1854
        const down = this.createPrimaryKeySql(
×
1855
            table,
UNCOV
1856
            table.primaryColumns.map((column) => column.name),
×
1857
            constraintName,
1858
        )
UNCOV
1859
        await this.executeQueries(up, down)
×
UNCOV
1860
        table.primaryColumns.forEach((column) => {
×
UNCOV
1861
            column.isPrimary = false
×
1862
        })
1863
    }
1864

1865
    /**
1866
     * Creates a new unique constraint.
1867
     */
1868
    async createUniqueConstraint(
1869
        tableOrName: Table | string,
1870
        uniqueConstraint: TableUnique,
1871
    ): Promise<void> {
UNCOV
1872
        const table = InstanceChecker.isTable(tableOrName)
×
1873
            ? tableOrName
1874
            : await this.getCachedTable(tableOrName)
1875

1876
        // new unique constraint may be passed without name. In this case we generate unique name manually.
UNCOV
1877
        if (!uniqueConstraint.name)
×
UNCOV
1878
            uniqueConstraint.name =
×
1879
                this.connection.namingStrategy.uniqueConstraintName(
1880
                    table,
1881
                    uniqueConstraint.columnNames,
1882
                )
1883

UNCOV
1884
        const up = this.createUniqueConstraintSql(table, uniqueConstraint)
×
UNCOV
1885
        const down = this.dropUniqueConstraintSql(table, uniqueConstraint)
×
UNCOV
1886
        await this.executeQueries(up, down)
×
UNCOV
1887
        table.addUniqueConstraint(uniqueConstraint)
×
1888
    }
1889

1890
    /**
1891
     * Creates a new unique constraints.
1892
     */
1893
    async createUniqueConstraints(
1894
        tableOrName: Table | string,
1895
        uniqueConstraints: TableUnique[],
1896
    ): Promise<void> {
UNCOV
1897
        const promises = uniqueConstraints.map((uniqueConstraint) =>
×
UNCOV
1898
            this.createUniqueConstraint(tableOrName, uniqueConstraint),
×
1899
        )
UNCOV
1900
        await Promise.all(promises)
×
1901
    }
1902

1903
    /**
1904
     * Drops an unique constraint.
1905
     */
1906
    async dropUniqueConstraint(
1907
        tableOrName: Table | string,
1908
        uniqueOrName: TableUnique | string,
1909
    ): Promise<void> {
UNCOV
1910
        const table = InstanceChecker.isTable(tableOrName)
×
1911
            ? tableOrName
1912
            : await this.getCachedTable(tableOrName)
UNCOV
1913
        const uniqueConstraint = InstanceChecker.isTableUnique(uniqueOrName)
×
1914
            ? uniqueOrName
1915
            : table.uniques.find((u) => u.name === uniqueOrName)
×
UNCOV
1916
        if (!uniqueConstraint)
×
1917
            throw new TypeORMError(
×
1918
                `Supplied unique constraint was not found in table ${table.name}`,
1919
            )
1920

UNCOV
1921
        const up = this.dropUniqueConstraintSql(table, uniqueConstraint)
×
UNCOV
1922
        const down = this.createUniqueConstraintSql(table, uniqueConstraint)
×
UNCOV
1923
        await this.executeQueries(up, down)
×
UNCOV
1924
        table.removeUniqueConstraint(uniqueConstraint)
×
1925
    }
1926

1927
    /**
1928
     * Creates an unique constraints.
1929
     */
1930
    async dropUniqueConstraints(
1931
        tableOrName: Table | string,
1932
        uniqueConstraints: TableUnique[],
1933
    ): Promise<void> {
UNCOV
1934
        const promises = uniqueConstraints.map((uniqueConstraint) =>
×
UNCOV
1935
            this.dropUniqueConstraint(tableOrName, uniqueConstraint),
×
1936
        )
UNCOV
1937
        await Promise.all(promises)
×
1938
    }
1939

1940
    /**
1941
     * Creates new check constraint.
1942
     */
1943
    async createCheckConstraint(
1944
        tableOrName: Table | string,
1945
        checkConstraint: TableCheck,
1946
    ): Promise<void> {
UNCOV
1947
        const table = InstanceChecker.isTable(tableOrName)
×
1948
            ? tableOrName
1949
            : await this.getCachedTable(tableOrName)
1950

1951
        // new unique constraint may be passed without name. In this case we generate unique name manually.
UNCOV
1952
        if (!checkConstraint.name)
×
UNCOV
1953
            checkConstraint.name =
×
1954
                this.connection.namingStrategy.checkConstraintName(
1955
                    table,
1956
                    checkConstraint.expression!,
1957
                )
1958

UNCOV
1959
        const up = this.createCheckConstraintSql(table, checkConstraint)
×
UNCOV
1960
        const down = this.dropCheckConstraintSql(table, checkConstraint)
×
UNCOV
1961
        await this.executeQueries(up, down)
×
UNCOV
1962
        table.addCheckConstraint(checkConstraint)
×
1963
    }
1964

1965
    /**
1966
     * Creates new check constraints.
1967
     */
1968
    async createCheckConstraints(
1969
        tableOrName: Table | string,
1970
        checkConstraints: TableCheck[],
1971
    ): Promise<void> {
UNCOV
1972
        const promises = checkConstraints.map((checkConstraint) =>
×
UNCOV
1973
            this.createCheckConstraint(tableOrName, checkConstraint),
×
1974
        )
UNCOV
1975
        await Promise.all(promises)
×
1976
    }
1977

1978
    /**
1979
     * Drops check constraint.
1980
     */
1981
    async dropCheckConstraint(
1982
        tableOrName: Table | string,
1983
        checkOrName: TableCheck | string,
1984
    ): Promise<void> {
UNCOV
1985
        const table = InstanceChecker.isTable(tableOrName)
×
1986
            ? tableOrName
1987
            : await this.getCachedTable(tableOrName)
UNCOV
1988
        const checkConstraint = InstanceChecker.isTableCheck(checkOrName)
×
1989
            ? checkOrName
1990
            : table.checks.find((c) => c.name === checkOrName)
×
UNCOV
1991
        if (!checkConstraint)
×
1992
            throw new TypeORMError(
×
1993
                `Supplied check constraint was not found in table ${table.name}`,
1994
            )
1995

UNCOV
1996
        const up = this.dropCheckConstraintSql(table, checkConstraint)
×
UNCOV
1997
        const down = this.createCheckConstraintSql(table, checkConstraint)
×
UNCOV
1998
        await this.executeQueries(up, down)
×
UNCOV
1999
        table.removeCheckConstraint(checkConstraint)
×
2000
    }
2001

2002
    /**
2003
     * Drops check constraints.
2004
     */
2005
    async dropCheckConstraints(
2006
        tableOrName: Table | string,
2007
        checkConstraints: TableCheck[],
2008
    ): Promise<void> {
UNCOV
2009
        const promises = checkConstraints.map((checkConstraint) =>
×
UNCOV
2010
            this.dropCheckConstraint(tableOrName, checkConstraint),
×
2011
        )
UNCOV
2012
        await Promise.all(promises)
×
2013
    }
2014

2015
    /**
2016
     * Creates a new exclusion constraint.
2017
     */
2018
    async createExclusionConstraint(
2019
        tableOrName: Table | string,
2020
        exclusionConstraint: TableExclusion,
2021
    ): Promise<void> {
2022
        throw new TypeORMError(`Oracle does not support exclusion constraints.`)
×
2023
    }
2024

2025
    /**
2026
     * Creates a new exclusion constraints.
2027
     */
2028
    async createExclusionConstraints(
2029
        tableOrName: Table | string,
2030
        exclusionConstraints: TableExclusion[],
2031
    ): Promise<void> {
2032
        throw new TypeORMError(`Oracle does not support exclusion constraints.`)
×
2033
    }
2034

2035
    /**
2036
     * Drops exclusion constraint.
2037
     */
2038
    async dropExclusionConstraint(
2039
        tableOrName: Table | string,
2040
        exclusionOrName: TableExclusion | string,
2041
    ): Promise<void> {
2042
        throw new TypeORMError(`Oracle does not support exclusion constraints.`)
×
2043
    }
2044

2045
    /**
2046
     * Drops exclusion constraints.
2047
     */
2048
    async dropExclusionConstraints(
2049
        tableOrName: Table | string,
2050
        exclusionConstraints: TableExclusion[],
2051
    ): Promise<void> {
2052
        throw new TypeORMError(`Oracle does not support exclusion constraints.`)
×
2053
    }
2054

2055
    /**
2056
     * Creates a new foreign key.
2057
     */
2058
    async createForeignKey(
2059
        tableOrName: Table | string,
2060
        foreignKey: TableForeignKey,
2061
    ): Promise<void> {
UNCOV
2062
        const table = InstanceChecker.isTable(tableOrName)
×
2063
            ? tableOrName
2064
            : await this.getCachedTable(tableOrName)
2065

2066
        // new FK may be passed without name. In this case we generate FK name manually.
UNCOV
2067
        if (!foreignKey.name)
×
UNCOV
2068
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
×
2069
                table,
2070
                foreignKey.columnNames,
2071
                this.getTablePath(foreignKey),
2072
                foreignKey.referencedColumnNames,
2073
            )
2074

UNCOV
2075
        const up = this.createForeignKeySql(table, foreignKey)
×
UNCOV
2076
        const down = this.dropForeignKeySql(table, foreignKey)
×
UNCOV
2077
        await this.executeQueries(up, down)
×
UNCOV
2078
        table.addForeignKey(foreignKey)
×
2079
    }
2080

2081
    /**
2082
     * Creates a new foreign keys.
2083
     */
2084
    async createForeignKeys(
2085
        tableOrName: Table | string,
2086
        foreignKeys: TableForeignKey[],
2087
    ): Promise<void> {
UNCOV
2088
        const promises = foreignKeys.map((foreignKey) =>
×
UNCOV
2089
            this.createForeignKey(tableOrName, foreignKey),
×
2090
        )
UNCOV
2091
        await Promise.all(promises)
×
2092
    }
2093

2094
    /**
2095
     * Drops a foreign key from the table.
2096
     */
2097
    async dropForeignKey(
2098
        tableOrName: Table | string,
2099
        foreignKeyOrName: TableForeignKey | string,
2100
    ): Promise<void> {
UNCOV
2101
        const table = InstanceChecker.isTable(tableOrName)
×
2102
            ? tableOrName
2103
            : await this.getCachedTable(tableOrName)
UNCOV
2104
        const foreignKey = InstanceChecker.isTableForeignKey(foreignKeyOrName)
×
2105
            ? foreignKeyOrName
2106
            : table.foreignKeys.find((fk) => fk.name === foreignKeyOrName)
×
UNCOV
2107
        if (!foreignKey)
×
2108
            throw new TypeORMError(
×
2109
                `Supplied foreign key was not found in table ${table.name}`,
2110
            )
2111

UNCOV
2112
        const up = this.dropForeignKeySql(table, foreignKey)
×
UNCOV
2113
        const down = this.createForeignKeySql(table, foreignKey)
×
UNCOV
2114
        await this.executeQueries(up, down)
×
UNCOV
2115
        table.removeForeignKey(foreignKey)
×
2116
    }
2117

2118
    /**
2119
     * Drops a foreign keys from the table.
2120
     */
2121
    async dropForeignKeys(
2122
        tableOrName: Table | string,
2123
        foreignKeys: TableForeignKey[],
2124
    ): Promise<void> {
UNCOV
2125
        const promises = foreignKeys.map((foreignKey) =>
×
UNCOV
2126
            this.dropForeignKey(tableOrName, foreignKey),
×
2127
        )
UNCOV
2128
        await Promise.all(promises)
×
2129
    }
2130

2131
    /**
2132
     * Creates a new index.
2133
     */
2134
    async createIndex(
2135
        tableOrName: Table | string,
2136
        index: TableIndex,
2137
    ): Promise<void> {
UNCOV
2138
        const table = InstanceChecker.isTable(tableOrName)
×
2139
            ? tableOrName
2140
            : await this.getCachedTable(tableOrName)
2141

2142
        // new index may be passed without name. In this case we generate index name manually.
UNCOV
2143
        if (!index.name) index.name = this.generateIndexName(table, index)
×
2144

UNCOV
2145
        const up = this.createIndexSql(table, index)
×
UNCOV
2146
        const down = this.dropIndexSql(index)
×
UNCOV
2147
        await this.executeQueries(up, down)
×
UNCOV
2148
        table.addIndex(index)
×
2149
    }
2150

2151
    /**
2152
     * Creates a new indices
2153
     */
2154
    async createIndices(
2155
        tableOrName: Table | string,
2156
        indices: TableIndex[],
2157
    ): Promise<void> {
UNCOV
2158
        const promises = indices.map((index) =>
×
UNCOV
2159
            this.createIndex(tableOrName, index),
×
2160
        )
UNCOV
2161
        await Promise.all(promises)
×
2162
    }
2163

2164
    /**
2165
     * Drops an index from the table.
2166
     */
2167
    async dropIndex(
2168
        tableOrName: Table | string,
2169
        indexOrName: TableIndex | string,
2170
    ): Promise<void> {
UNCOV
2171
        const table = InstanceChecker.isTable(tableOrName)
×
2172
            ? tableOrName
2173
            : await this.getCachedTable(tableOrName)
UNCOV
2174
        const index = InstanceChecker.isTableIndex(indexOrName)
×
2175
            ? indexOrName
2176
            : table.indices.find((i) => i.name === indexOrName)
×
UNCOV
2177
        if (!index)
×
2178
            throw new TypeORMError(
×
2179
                `Supplied index ${indexOrName} was not found in table ${table.name}`,
2180
            )
2181
        // old index may be passed without name. In this case we generate index name manually.
UNCOV
2182
        if (!index.name) index.name = this.generateIndexName(table, index)
×
2183

UNCOV
2184
        const up = this.dropIndexSql(index)
×
UNCOV
2185
        const down = this.createIndexSql(table, index)
×
UNCOV
2186
        await this.executeQueries(up, down)
×
UNCOV
2187
        table.removeIndex(index)
×
2188
    }
2189

2190
    /**
2191
     * Drops an indices from the table.
2192
     */
2193
    async dropIndices(
2194
        tableOrName: Table | string,
2195
        indices: TableIndex[],
2196
    ): Promise<void> {
2197
        const promises = indices.map((index) =>
×
2198
            this.dropIndex(tableOrName, index),
×
2199
        )
2200
        await Promise.all(promises)
×
2201
    }
2202

2203
    /**
2204
     * Clears all table contents.
2205
     * Note: this operation uses SQL's TRUNCATE query which cannot be reverted in transactions.
2206
     */
2207
    async clearTable(tableName: string): Promise<void> {
UNCOV
2208
        await this.query(`TRUNCATE TABLE ${this.escapePath(tableName)}`)
×
2209
    }
2210

2211
    /**
2212
     * Removes all tables from the currently connected database.
2213
     */
2214
    async clearDatabase(): Promise<void> {
UNCOV
2215
        const isAnotherTransactionActive = this.isTransactionActive
×
UNCOV
2216
        if (!isAnotherTransactionActive) await this.startTransaction()
×
UNCOV
2217
        try {
×
2218
            // drop views
UNCOV
2219
            const dropViewsQuery = `SELECT 'DROP VIEW "' || VIEW_NAME || '"' AS "query" FROM "USER_VIEWS"`
×
UNCOV
2220
            const dropViewQueries: ObjectLiteral[] = await this.query(
×
2221
                dropViewsQuery,
2222
            )
UNCOV
2223
            await Promise.all(
×
UNCOV
2224
                dropViewQueries.map((query) => this.query(query["query"])),
×
2225
            )
2226

2227
            // drop materialized views
UNCOV
2228
            const dropMatViewsQuery = `SELECT 'DROP MATERIALIZED VIEW "' || MVIEW_NAME || '"' AS "query" FROM "USER_MVIEWS"`
×
UNCOV
2229
            const dropMatViewQueries: ObjectLiteral[] = await this.query(
×
2230
                dropMatViewsQuery,
2231
            )
UNCOV
2232
            await Promise.all(
×
UNCOV
2233
                dropMatViewQueries.map((query) => this.query(query["query"])),
×
2234
            )
2235

2236
            // drop tables
UNCOV
2237
            const dropTablesQuery = `SELECT 'DROP TABLE "' || TABLE_NAME || '" CASCADE CONSTRAINTS' AS "query" FROM "USER_TABLES"`
×
UNCOV
2238
            const dropTableQueries: ObjectLiteral[] = await this.query(
×
2239
                dropTablesQuery,
2240
            )
UNCOV
2241
            await Promise.all(
×
UNCOV
2242
                dropTableQueries.map((query) => this.query(query["query"])),
×
2243
            )
UNCOV
2244
            if (!isAnotherTransactionActive) await this.commitTransaction()
×
2245
        } catch (error) {
2246
            try {
×
2247
                // we throw original error even if rollback thrown an error
2248
                if (!isAnotherTransactionActive)
×
2249
                    await this.rollbackTransaction()
×
2250
            } catch (rollbackError) {}
2251
            throw error
×
2252
        }
2253
    }
2254

2255
    // -------------------------------------------------------------------------
2256
    // Protected Methods
2257
    // -------------------------------------------------------------------------
2258

2259
    protected async loadViews(viewNames?: string[]): Promise<View[]> {
UNCOV
2260
        const hasTable = await this.hasTable(this.getTypeormMetadataTableName())
×
UNCOV
2261
        if (!hasTable) {
×
UNCOV
2262
            return []
×
2263
        }
2264

UNCOV
2265
        if (!viewNames) {
×
2266
            viewNames = []
×
2267
        }
2268

UNCOV
2269
        const currentDatabase = await this.getCurrentDatabase()
×
UNCOV
2270
        const currentSchema = await this.getCurrentSchema()
×
2271

UNCOV
2272
        const viewsCondition = viewNames
×
UNCOV
2273
            .map((viewName) => this.driver.parseTableName(viewName))
×
2274
            .map(({ schema, tableName }) => {
UNCOV
2275
                if (!schema) {
×
2276
                    schema = this.driver.options.schema || currentSchema
×
2277
                }
2278

UNCOV
2279
                return `("T"."schema" = '${schema}' AND "T"."name" = '${tableName}')`
×
2280
            })
2281
            .join(" OR ")
2282

2283
        let query =
UNCOV
2284
            `SELECT "T".* FROM ${this.escapePath(
×
2285
                this.getTypeormMetadataTableName(),
2286
            )} "T" ` +
2287
            `INNER JOIN "USER_OBJECTS" "O" ON "O"."OBJECT_NAME" = "T"."name" AND "O"."OBJECT_TYPE" IN ( 'MATERIALIZED VIEW', 'VIEW' ) ` +
2288
            `WHERE "T"."type" IN ('${MetadataTableType.MATERIALIZED_VIEW}', '${MetadataTableType.VIEW}')`
UNCOV
2289
        if (viewsCondition.length > 0) query += ` AND ${viewsCondition}`
×
2290

UNCOV
2291
        const dbViews = await this.query(query)
×
UNCOV
2292
        return dbViews.map((dbView: any) => {
×
UNCOV
2293
            const parsedName = this.driver.parseTableName(dbView["name"])
×
2294

UNCOV
2295
            const view = new View()
×
UNCOV
2296
            view.database =
×
2297
                parsedName.database || dbView["database"] || currentDatabase
×
UNCOV
2298
            view.schema = parsedName.schema || dbView["schema"] || currentSchema
×
UNCOV
2299
            view.name = parsedName.tableName
×
UNCOV
2300
            view.expression = dbView["value"]
×
UNCOV
2301
            view.materialized =
×
2302
                dbView["type"] === MetadataTableType.MATERIALIZED_VIEW
UNCOV
2303
            return view
×
2304
        })
2305
    }
2306

2307
    /**
2308
     * Loads all tables (with given names) from the database and creates a Table from them.
2309
     */
2310
    protected async loadTables(tableNames?: string[]): Promise<Table[]> {
UNCOV
2311
        if (tableNames && tableNames.length === 0) {
×
UNCOV
2312
            return []
×
2313
        }
2314

UNCOV
2315
        const dbTables: { TABLE_NAME: string; OWNER: string }[] = []
×
2316

UNCOV
2317
        const currentSchema = await this.getCurrentSchema()
×
UNCOV
2318
        const currentDatabase = await this.getCurrentDatabase()
×
2319

UNCOV
2320
        if (!tableNames) {
×
2321
            const tablesSql = `SELECT "TABLE_NAME", "OWNER" FROM "ALL_TABLES"`
×
2322
            dbTables.push(...(await this.query(tablesSql)))
×
2323
        } else {
UNCOV
2324
            const tablesCondition = tableNames
×
2325
                .map((tableName) => {
UNCOV
2326
                    const parts = tableName.split(".")
×
2327

UNCOV
2328
                    if (parts.length >= 3) {
×
2329
                        const [, schema, name] = parts
×
2330
                        return `("OWNER" = '${schema}' AND "TABLE_NAME" = '${name}')`
×
UNCOV
2331
                    } else if (parts.length === 2) {
×
UNCOV
2332
                        const [schema, name] = parts
×
UNCOV
2333
                        return `("OWNER" = '${schema}' AND "TABLE_NAME" = '${name}')`
×
UNCOV
2334
                    } else if (parts.length === 1) {
×
UNCOV
2335
                        const [name] = parts
×
UNCOV
2336
                        return `("TABLE_NAME" = '${name}')`
×
2337
                    } else {
2338
                        return `(1=0)`
×
2339
                    }
2340
                })
2341
                .join(" OR ")
UNCOV
2342
            const tablesSql = `SELECT "TABLE_NAME", "OWNER" FROM "ALL_TABLES" WHERE ${tablesCondition}`
×
UNCOV
2343
            dbTables.push(...(await this.query(tablesSql)))
×
2344
        }
2345

2346
        // if tables were not found in the db, no need to proceed
UNCOV
2347
        if (dbTables.length === 0) {
×
UNCOV
2348
            return []
×
2349
        }
2350

2351
        // load tables, columns, indices and foreign keys
UNCOV
2352
        const columnsCondition = dbTables
×
2353
            .map(({ TABLE_NAME, OWNER }) => {
UNCOV
2354
                return `("C"."OWNER" = '${OWNER}' AND "C"."TABLE_NAME" = '${TABLE_NAME}')`
×
2355
            })
2356
            .join(" OR ")
UNCOV
2357
        const columnsSql = `SELECT * FROM "ALL_TAB_COLS" "C" WHERE (${columnsCondition})`
×
2358

2359
        const indicesSql =
UNCOV
2360
            `SELECT "C"."INDEX_NAME", "C"."OWNER", "C"."TABLE_NAME", "C"."UNIQUENESS", ` +
×
2361
            `LISTAGG ("COL"."COLUMN_NAME", ',') WITHIN GROUP (ORDER BY "COL"."COLUMN_NAME") AS "COLUMN_NAMES" ` +
2362
            `FROM "ALL_INDEXES" "C" ` +
2363
            `INNER JOIN "ALL_IND_COLUMNS" "COL" ON "COL"."INDEX_OWNER" = "C"."OWNER" AND "COL"."INDEX_NAME" = "C"."INDEX_NAME" ` +
2364
            `LEFT JOIN "ALL_CONSTRAINTS" "CON" ON "CON"."OWNER" = "C"."OWNER" AND "CON"."CONSTRAINT_NAME" = "C"."INDEX_NAME" ` +
2365
            `WHERE (${columnsCondition}) AND "CON"."CONSTRAINT_NAME" IS NULL ` +
2366
            `GROUP BY "C"."INDEX_NAME", "C"."OWNER", "C"."TABLE_NAME", "C"."UNIQUENESS"`
2367

2368
        const foreignKeysSql =
UNCOV
2369
            `SELECT "C"."CONSTRAINT_NAME", "C"."OWNER", "C"."TABLE_NAME", "COL"."COLUMN_NAME", "REF_COL"."TABLE_NAME" AS "REFERENCED_TABLE_NAME", ` +
×
2370
            `"REF_COL"."COLUMN_NAME" AS "REFERENCED_COLUMN_NAME", "C"."DELETE_RULE" AS "ON_DELETE" ` +
2371
            `FROM "ALL_CONSTRAINTS" "C" ` +
2372
            `INNER JOIN "ALL_CONS_COLUMNS" "COL" ON "COL"."OWNER" = "C"."OWNER" AND "COL"."CONSTRAINT_NAME" = "C"."CONSTRAINT_NAME" ` +
2373
            `INNER JOIN "ALL_CONS_COLUMNS" "REF_COL" ON "REF_COL"."OWNER" = "C"."R_OWNER" AND "REF_COL"."CONSTRAINT_NAME" = "C"."R_CONSTRAINT_NAME" AND "REF_COL"."POSITION" = "COL"."POSITION" ` +
2374
            `WHERE (${columnsCondition}) AND "C"."CONSTRAINT_TYPE" = 'R'`
2375

2376
        const constraintsSql =
UNCOV
2377
            `SELECT "C"."CONSTRAINT_NAME", "C"."CONSTRAINT_TYPE", "C"."OWNER", "C"."TABLE_NAME", "COL"."COLUMN_NAME", "C"."SEARCH_CONDITION" ` +
×
2378
            `FROM "ALL_CONSTRAINTS" "C" ` +
2379
            `INNER JOIN "ALL_CONS_COLUMNS" "COL" ON "COL"."OWNER" = "C"."OWNER" AND "COL"."CONSTRAINT_NAME" = "C"."CONSTRAINT_NAME" ` +
2380
            `WHERE (${columnsCondition}) AND "C"."CONSTRAINT_TYPE" IN ('C', 'U', 'P') AND "C"."GENERATED" = 'USER NAME'`
2381

2382
        const [
2383
            dbColumns,
2384
            dbIndices,
2385
            dbForeignKeys,
2386
            dbConstraints,
UNCOV
2387
        ]: ObjectLiteral[][] = await Promise.all([
×
2388
            this.query(columnsSql),
2389
            this.query(indicesSql),
2390
            this.query(foreignKeysSql),
2391
            this.query(constraintsSql),
2392
        ])
2393

2394
        // create tables for loaded tables
UNCOV
2395
        return await Promise.all(
×
2396
            dbTables.map(async (dbTable) => {
UNCOV
2397
                const table = new Table()
×
2398
                const owner =
UNCOV
2399
                    dbTable["OWNER"] === currentSchema &&
×
2400
                    (!this.driver.options.schema ||
2401
                        this.driver.options.schema === currentSchema)
2402
                        ? undefined
2403
                        : dbTable["OWNER"]
UNCOV
2404
                table.database = currentDatabase
×
UNCOV
2405
                table.schema = dbTable["OWNER"]
×
UNCOV
2406
                table.name = this.driver.buildTableName(
×
2407
                    dbTable["TABLE_NAME"],
2408
                    owner,
2409
                )
2410

2411
                // create columns from the loaded columns
UNCOV
2412
                table.columns = await Promise.all(
×
2413
                    dbColumns
2414
                        .filter(
2415
                            (dbColumn) =>
UNCOV
2416
                                dbColumn["OWNER"] === dbTable["OWNER"] &&
×
2417
                                dbColumn["TABLE_NAME"] ===
2418
                                    dbTable["TABLE_NAME"] &&
2419
                                // Filter out auto-generated virtual columns,
2420
                                // since TypeORM will have no info about them.
2421
                                !(
2422
                                    dbColumn["VIRTUAL_COLUMN"] === "YES" &&
×
2423
                                    dbColumn["USER_GENERATED"] === "NO"
2424
                                ),
2425
                        )
2426
                        .map(async (dbColumn) => {
UNCOV
2427
                            const columnConstraints = dbConstraints.filter(
×
2428
                                (dbConstraint) =>
UNCOV
2429
                                    dbConstraint["OWNER"] ===
×
2430
                                        dbColumn["OWNER"] &&
2431
                                    dbConstraint["TABLE_NAME"] ===
2432
                                        dbColumn["TABLE_NAME"] &&
2433
                                    dbConstraint["COLUMN_NAME"] ===
2434
                                        dbColumn["COLUMN_NAME"],
2435
                            )
2436

UNCOV
2437
                            const uniqueConstraints = columnConstraints.filter(
×
2438
                                (constraint) =>
UNCOV
2439
                                    constraint["CONSTRAINT_TYPE"] === "U",
×
2440
                            )
2441
                            const isConstraintComposite =
UNCOV
2442
                                uniqueConstraints.every((uniqueConstraint) => {
×
UNCOV
2443
                                    return dbConstraints.some(
×
2444
                                        (dbConstraint) =>
UNCOV
2445
                                            dbConstraint["OWNER"] ===
×
2446
                                                dbColumn["OWNER"] &&
2447
                                            dbConstraint["TABLE_NAME"] ===
2448
                                                dbColumn["TABLE_NAME"] &&
2449
                                            dbConstraint["COLUMN_NAME"] !==
2450
                                                dbColumn["COLUMN_NAME"] &&
2451
                                            dbConstraint["CONSTRAINT_NAME"] ===
2452
                                                uniqueConstraint[
2453
                                                    "CONSTRAINT_NAME"
2454
                                                ] &&
2455
                                            dbConstraint["CONSTRAINT_TYPE"] ===
2456
                                                "U",
2457
                                    )
2458
                                })
2459

UNCOV
2460
                            const tableColumn = new TableColumn()
×
UNCOV
2461
                            tableColumn.name = dbColumn["COLUMN_NAME"]
×
UNCOV
2462
                            tableColumn.type =
×
2463
                                dbColumn["DATA_TYPE"].toLowerCase()
UNCOV
2464
                            if (tableColumn.type.indexOf("(") !== -1)
×
UNCOV
2465
                                tableColumn.type = tableColumn.type.replace(
×
2466
                                    /\([0-9]*\)/,
2467
                                    "",
2468
                                )
2469

2470
                            // check only columns that have length property
UNCOV
2471
                            if (
×
2472
                                this.driver.withLengthColumnTypes.indexOf(
2473
                                    tableColumn.type as ColumnType,
2474
                                ) !== -1
2475
                            ) {
2476
                                const length =
UNCOV
2477
                                    tableColumn.type === "raw"
×
2478
                                        ? dbColumn["DATA_LENGTH"]
2479
                                        : dbColumn["CHAR_COL_DECL_LENGTH"]
UNCOV
2480
                                tableColumn.length =
×
2481
                                    length &&
×
2482
                                    !this.isDefaultColumnLength(
2483
                                        table,
2484
                                        tableColumn,
2485
                                        length,
2486
                                    )
2487
                                        ? length.toString()
2488
                                        : ""
2489
                            }
2490

UNCOV
2491
                            if (
×
2492
                                tableColumn.type === "number" ||
×
2493
                                tableColumn.type === "float"
2494
                            ) {
UNCOV
2495
                                if (
×
2496
                                    dbColumn["DATA_PRECISION"] !== null &&
×
2497
                                    !this.isDefaultColumnPrecision(
2498
                                        table,
2499
                                        tableColumn,
2500
                                        dbColumn["DATA_PRECISION"],
2501
                                    )
2502
                                )
UNCOV
2503
                                    tableColumn.precision =
×
2504
                                        dbColumn["DATA_PRECISION"]
UNCOV
2505
                                if (
×
2506
                                    dbColumn["DATA_SCALE"] !== null &&
×
2507
                                    !this.isDefaultColumnScale(
2508
                                        table,
2509
                                        tableColumn,
2510
                                        dbColumn["DATA_SCALE"],
2511
                                    )
2512
                                )
UNCOV
2513
                                    tableColumn.scale = dbColumn["DATA_SCALE"]
×
UNCOV
2514
                            } else if (
×
2515
                                (tableColumn.type === "timestamp" ||
×
2516
                                    tableColumn.type ===
2517
                                        "timestamp with time zone" ||
2518
                                    tableColumn.type ===
2519
                                        "timestamp with local time zone") &&
2520
                                dbColumn["DATA_SCALE"] !== null
2521
                            ) {
UNCOV
2522
                                tableColumn.precision =
×
2523
                                    !this.isDefaultColumnPrecision(
×
2524
                                        table,
2525
                                        tableColumn,
2526
                                        dbColumn["DATA_SCALE"],
2527
                                    )
2528
                                        ? dbColumn["DATA_SCALE"]
2529
                                        : undefined
2530
                            }
2531

UNCOV
2532
                            tableColumn.default =
×
2533
                                dbColumn["DATA_DEFAULT"] !== null &&
×
2534
                                dbColumn["DATA_DEFAULT"] !== undefined &&
2535
                                dbColumn["VIRTUAL_COLUMN"] === "NO" &&
2536
                                dbColumn["DATA_DEFAULT"].trim() !== "NULL"
2537
                                    ? (tableColumn.default =
2538
                                          dbColumn["DATA_DEFAULT"].trim())
2539
                                    : undefined
2540

UNCOV
2541
                            const primaryConstraint = columnConstraints.find(
×
2542
                                (constraint) =>
UNCOV
2543
                                    constraint["CONSTRAINT_TYPE"] === "P",
×
2544
                            )
UNCOV
2545
                            if (primaryConstraint) {
×
UNCOV
2546
                                tableColumn.isPrimary = true
×
2547
                                // find another columns involved in primary key constraint
2548
                                const anotherPrimaryConstraints =
UNCOV
2549
                                    dbConstraints.filter(
×
2550
                                        (constraint) =>
UNCOV
2551
                                            constraint["OWNER"] ===
×
2552
                                                dbColumn["OWNER"] &&
2553
                                            constraint["TABLE_NAME"] ===
2554
                                                dbColumn["TABLE_NAME"] &&
2555
                                            constraint["COLUMN_NAME"] !==
2556
                                                dbColumn["COLUMN_NAME"] &&
2557
                                            constraint["CONSTRAINT_TYPE"] ===
2558
                                                "P",
2559
                                    )
2560

2561
                                // collect all column names
2562
                                const columnNames =
UNCOV
2563
                                    anotherPrimaryConstraints.map(
×
2564
                                        (constraint) =>
UNCOV
2565
                                            constraint["COLUMN_NAME"],
×
2566
                                    )
UNCOV
2567
                                columnNames.push(dbColumn["COLUMN_NAME"])
×
2568

2569
                                // build default primary key constraint name
2570
                                const pkName =
UNCOV
2571
                                    this.connection.namingStrategy.primaryKeyName(
×
2572
                                        table,
2573
                                        columnNames,
2574
                                    )
2575

2576
                                // if primary key has user-defined constraint name, write it in table column
UNCOV
2577
                                if (
×
2578
                                    primaryConstraint["CONSTRAINT_NAME"] !==
2579
                                    pkName
2580
                                ) {
UNCOV
2581
                                    tableColumn.primaryKeyConstraintName =
×
2582
                                        primaryConstraint["CONSTRAINT_NAME"]
2583
                                }
2584
                            }
2585

UNCOV
2586
                            tableColumn.isNullable =
×
2587
                                dbColumn["NULLABLE"] === "Y"
UNCOV
2588
                            tableColumn.isUnique =
×
2589
                                uniqueConstraints.length > 0 &&
×
2590
                                !isConstraintComposite
UNCOV
2591
                            tableColumn.isGenerated =
×
2592
                                dbColumn["IDENTITY_COLUMN"] === "YES"
UNCOV
2593
                            if (tableColumn.isGenerated) {
×
UNCOV
2594
                                tableColumn.generationStrategy = "increment"
×
UNCOV
2595
                                tableColumn.default = undefined
×
2596
                            }
UNCOV
2597
                            tableColumn.comment = "" // todo
×
2598

UNCOV
2599
                            if (dbColumn["VIRTUAL_COLUMN"] === "YES") {
×
UNCOV
2600
                                tableColumn.generatedType = "VIRTUAL"
×
2601

2602
                                const asExpressionQuery =
UNCOV
2603
                                    this.selectTypeormMetadataSql({
×
2604
                                        table: dbTable["TABLE_NAME"],
2605
                                        type: MetadataTableType.GENERATED_COLUMN,
2606
                                        name: tableColumn.name,
2607
                                    })
2608

UNCOV
2609
                                const results = await this.query(
×
2610
                                    asExpressionQuery.query,
2611
                                    asExpressionQuery.parameters,
2612
                                )
UNCOV
2613
                                if (results[0] && results[0].value) {
×
UNCOV
2614
                                    tableColumn.asExpression = results[0].value
×
2615
                                } else {
2616
                                    tableColumn.asExpression = ""
×
2617
                                }
2618
                            }
2619

UNCOV
2620
                            return tableColumn
×
2621
                        }),
2622
                )
2623

2624
                // find unique constraints of table, group them by constraint name and build TableUnique.
UNCOV
2625
                const tableUniqueConstraints = OrmUtils.uniq(
×
2626
                    dbConstraints.filter((dbConstraint) => {
UNCOV
2627
                        return (
×
2628
                            dbConstraint["TABLE_NAME"] ===
×
2629
                                dbTable["TABLE_NAME"] &&
2630
                            dbConstraint["OWNER"] === dbTable["OWNER"] &&
2631
                            dbConstraint["CONSTRAINT_TYPE"] === "U"
2632
                        )
2633
                    }),
UNCOV
2634
                    (dbConstraint) => dbConstraint["CONSTRAINT_NAME"],
×
2635
                )
2636

UNCOV
2637
                table.uniques = tableUniqueConstraints.map((constraint) => {
×
UNCOV
2638
                    const uniques = dbConstraints.filter(
×
2639
                        (dbC) =>
UNCOV
2640
                            dbC["CONSTRAINT_NAME"] ===
×
2641
                            constraint["CONSTRAINT_NAME"],
2642
                    )
UNCOV
2643
                    return new TableUnique({
×
2644
                        name: constraint["CONSTRAINT_NAME"],
UNCOV
2645
                        columnNames: uniques.map((u) => u["COLUMN_NAME"]),
×
2646
                    })
2647
                })
2648

2649
                // find check constraints of table, group them by constraint name and build TableCheck.
UNCOV
2650
                const tableCheckConstraints = OrmUtils.uniq(
×
2651
                    dbConstraints.filter((dbConstraint) => {
UNCOV
2652
                        return (
×
2653
                            dbConstraint["TABLE_NAME"] ===
×
2654
                                dbTable["TABLE_NAME"] &&
2655
                            dbConstraint["OWNER"] === dbTable["OWNER"] &&
2656
                            dbConstraint["CONSTRAINT_TYPE"] === "C"
2657
                        )
2658
                    }),
UNCOV
2659
                    (dbConstraint) => dbConstraint["CONSTRAINT_NAME"],
×
2660
                )
2661

UNCOV
2662
                table.checks = tableCheckConstraints.map((constraint) => {
×
UNCOV
2663
                    const checks = dbConstraints.filter(
×
2664
                        (dbC) =>
UNCOV
2665
                            dbC["TABLE_NAME"] === constraint["TABLE_NAME"] &&
×
2666
                            dbC["OWNER"] === constraint["OWNER"] &&
2667
                            dbC["CONSTRAINT_NAME"] ===
2668
                                constraint["CONSTRAINT_NAME"],
2669
                    )
UNCOV
2670
                    return new TableCheck({
×
2671
                        name: constraint["CONSTRAINT_NAME"],
UNCOV
2672
                        columnNames: checks.map((c) => c["COLUMN_NAME"]),
×
2673
                        expression: constraint["SEARCH_CONDITION"],
2674
                    })
2675
                })
2676

2677
                // find foreign key constraints of table, group them by constraint name and build TableForeignKey.
UNCOV
2678
                const tableForeignKeyConstraints = OrmUtils.uniq(
×
2679
                    dbForeignKeys.filter(
2680
                        (dbForeignKey) =>
UNCOV
2681
                            dbForeignKey["OWNER"] === dbTable["OWNER"] &&
×
2682
                            dbForeignKey["TABLE_NAME"] ===
2683
                                dbTable["TABLE_NAME"],
2684
                    ),
UNCOV
2685
                    (dbForeignKey) => dbForeignKey["CONSTRAINT_NAME"],
×
2686
                )
2687

UNCOV
2688
                table.foreignKeys = tableForeignKeyConstraints.map(
×
2689
                    (dbForeignKey) => {
UNCOV
2690
                        const foreignKeys = dbForeignKeys.filter(
×
2691
                            (dbFk) =>
UNCOV
2692
                                dbFk["TABLE_NAME"] ===
×
2693
                                    dbForeignKey["TABLE_NAME"] &&
2694
                                dbFk["OWNER"] === dbForeignKey["OWNER"] &&
2695
                                dbFk["CONSTRAINT_NAME"] ===
2696
                                    dbForeignKey["CONSTRAINT_NAME"],
2697
                        )
UNCOV
2698
                        return new TableForeignKey({
×
2699
                            name: dbForeignKey["CONSTRAINT_NAME"],
2700
                            columnNames: foreignKeys.map(
UNCOV
2701
                                (dbFk) => dbFk["COLUMN_NAME"],
×
2702
                            ),
2703
                            referencedDatabase: table.database,
2704
                            referencedSchema: dbForeignKey["OWNER"],
2705
                            referencedTableName:
2706
                                dbForeignKey["REFERENCED_TABLE_NAME"],
2707
                            referencedColumnNames: foreignKeys.map(
UNCOV
2708
                                (dbFk) => dbFk["REFERENCED_COLUMN_NAME"],
×
2709
                            ),
2710
                            onDelete: dbForeignKey["ON_DELETE"],
2711
                            onUpdate: "NO ACTION", // Oracle does not have onUpdate option in FK's, but we need it for proper synchronization
2712
                        })
2713
                    },
2714
                )
2715

2716
                // Attempt to map auto-generated virtual columns to their
2717
                // referenced columns, through its 'DATA_DEFAULT' property.
2718
                //
2719
                // An example of this happening is when a column of type
2720
                // TIMESTAMP WITH TIME ZONE is indexed. Oracle will create a
2721
                // virtual column of type TIMESTAMP with a default value of
2722
                // SYS_EXTRACT_UTC(<column>).
UNCOV
2723
                const autoGenVirtualDbColumns = dbColumns
×
2724
                    .filter(
2725
                        (dbColumn) =>
UNCOV
2726
                            dbColumn["OWNER"] === dbTable["OWNER"] &&
×
2727
                            dbColumn["TABLE_NAME"] === dbTable["TABLE_NAME"] &&
2728
                            dbColumn["VIRTUAL_COLUMN"] === "YES" &&
2729
                            dbColumn["USER_GENERATED"] === "NO",
2730
                    )
2731
                    .reduce((acc, x) => {
UNCOV
2732
                        const referencedDbColumn = dbColumns.find((dbColumn) =>
×
UNCOV
2733
                            x["DATA_DEFAULT"].includes(dbColumn["COLUMN_NAME"]),
×
2734
                        )
2735

UNCOV
2736
                        if (!referencedDbColumn) return acc
×
2737

UNCOV
2738
                        return {
×
2739
                            ...acc,
2740
                            [x["COLUMN_NAME"]]:
2741
                                referencedDbColumn["COLUMN_NAME"],
2742
                        }
2743
                    }, {})
2744

2745
                // create TableIndex objects from the loaded indices
UNCOV
2746
                table.indices = dbIndices
×
2747
                    .filter(
2748
                        (dbIndex) =>
UNCOV
2749
                            dbIndex["TABLE_NAME"] === dbTable["TABLE_NAME"] &&
×
2750
                            dbIndex["OWNER"] === dbTable["OWNER"],
2751
                    )
2752
                    .map((dbIndex) => {
2753
                        //
UNCOV
2754
                        const columnNames = dbIndex["COLUMN_NAMES"]
×
2755
                            .split(",")
2756
                            .map(
2757
                                (
2758
                                    columnName: keyof typeof autoGenVirtualDbColumns,
2759
                                ) =>
UNCOV
2760
                                    autoGenVirtualDbColumns[columnName] ??
×
2761
                                    columnName,
2762
                            )
2763

UNCOV
2764
                        return new TableIndex({
×
2765
                            name: dbIndex["INDEX_NAME"],
2766
                            columnNames,
2767
                            isUnique: dbIndex["UNIQUENESS"] === "UNIQUE",
2768
                        })
2769
                    })
2770

UNCOV
2771
                return table
×
2772
            }),
2773
        )
2774
    }
2775

2776
    /**
2777
     * Builds and returns SQL for create table.
2778
     */
2779
    protected createTableSql(table: Table, createForeignKeys?: boolean): Query {
UNCOV
2780
        const columnDefinitions = table.columns
×
UNCOV
2781
            .map((column) => this.buildCreateColumnSql(column))
×
2782
            .join(", ")
UNCOV
2783
        let sql = `CREATE TABLE ${this.escapePath(table)} (${columnDefinitions}`
×
2784

UNCOV
2785
        table.columns
×
UNCOV
2786
            .filter((column) => column.isUnique)
×
2787
            .forEach((column) => {
UNCOV
2788
                const isUniqueExist = table.uniques.some(
×
2789
                    (unique) =>
UNCOV
2790
                        unique.columnNames.length === 1 &&
×
2791
                        unique.columnNames[0] === column.name,
2792
                )
UNCOV
2793
                if (!isUniqueExist)
×
UNCOV
2794
                    table.uniques.push(
×
2795
                        new TableUnique({
2796
                            name: this.connection.namingStrategy.uniqueConstraintName(
2797
                                table,
2798
                                [column.name],
2799
                            ),
2800
                            columnNames: [column.name],
2801
                        }),
2802
                    )
2803
            })
2804

UNCOV
2805
        if (table.uniques.length > 0) {
×
UNCOV
2806
            const uniquesSql = table.uniques
×
2807
                .map((unique) => {
UNCOV
2808
                    const uniqueName = unique.name
×
2809
                        ? unique.name
2810
                        : this.connection.namingStrategy.uniqueConstraintName(
2811
                              table,
2812
                              unique.columnNames,
2813
                          )
UNCOV
2814
                    const columnNames = unique.columnNames
×
UNCOV
2815
                        .map((columnName) => `"${columnName}"`)
×
2816
                        .join(", ")
UNCOV
2817
                    return `CONSTRAINT "${uniqueName}" UNIQUE (${columnNames})`
×
2818
                })
2819
                .join(", ")
2820

UNCOV
2821
            sql += `, ${uniquesSql}`
×
2822
        }
2823

UNCOV
2824
        if (table.checks.length > 0) {
×
UNCOV
2825
            const checksSql = table.checks
×
2826
                .map((check) => {
UNCOV
2827
                    const checkName = check.name
×
2828
                        ? check.name
2829
                        : this.connection.namingStrategy.checkConstraintName(
2830
                              table,
2831
                              check.expression!,
2832
                          )
UNCOV
2833
                    return `CONSTRAINT "${checkName}" CHECK (${check.expression})`
×
2834
                })
2835
                .join(", ")
2836

UNCOV
2837
            sql += `, ${checksSql}`
×
2838
        }
2839

UNCOV
2840
        if (table.foreignKeys.length > 0 && createForeignKeys) {
×
UNCOV
2841
            const foreignKeysSql = table.foreignKeys
×
2842
                .map((fk) => {
UNCOV
2843
                    const columnNames = fk.columnNames
×
UNCOV
2844
                        .map((columnName) => `"${columnName}"`)
×
2845
                        .join(", ")
UNCOV
2846
                    if (!fk.name)
×
UNCOV
2847
                        fk.name = this.connection.namingStrategy.foreignKeyName(
×
2848
                            table,
2849
                            fk.columnNames,
2850
                            this.getTablePath(fk),
2851
                            fk.referencedColumnNames,
2852
                        )
UNCOV
2853
                    const referencedColumnNames = fk.referencedColumnNames
×
UNCOV
2854
                        .map((columnName) => `"${columnName}"`)
×
2855
                        .join(", ")
UNCOV
2856
                    let constraint = `CONSTRAINT "${
×
2857
                        fk.name
2858
                    }" FOREIGN KEY (${columnNames}) REFERENCES ${this.escapePath(
2859
                        this.getTablePath(fk),
2860
                    )} (${referencedColumnNames})`
UNCOV
2861
                    if (fk.onDelete && fk.onDelete !== "NO ACTION") {
×
2862
                        // Oracle does not support NO ACTION, but we set NO ACTION by default in EntityMetadata
2863
                        constraint += ` ON DELETE ${fk.onDelete}`
×
2864
                    }
UNCOV
2865
                    return constraint
×
2866
                })
2867
                .join(", ")
2868

UNCOV
2869
            sql += `, ${foreignKeysSql}`
×
2870
        }
2871

UNCOV
2872
        const primaryColumns = table.columns.filter(
×
UNCOV
2873
            (column) => column.isPrimary,
×
2874
        )
UNCOV
2875
        if (primaryColumns.length > 0) {
×
UNCOV
2876
            const primaryKeyName = primaryColumns[0].primaryKeyConstraintName
×
2877
                ? primaryColumns[0].primaryKeyConstraintName
2878
                : this.connection.namingStrategy.primaryKeyName(
2879
                      table,
UNCOV
2880
                      primaryColumns.map((column) => column.name),
×
2881
                  )
2882

UNCOV
2883
            const columnNames = primaryColumns
×
UNCOV
2884
                .map((column) => `"${column.name}"`)
×
2885
                .join(", ")
UNCOV
2886
            sql += `, CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNames})`
×
2887
        }
2888

UNCOV
2889
        sql += `)`
×
2890

UNCOV
2891
        return new Query(sql)
×
2892
    }
2893

2894
    /**
2895
     * Builds drop table sql.
2896
     */
2897
    protected dropTableSql(
2898
        tableOrName: Table | string,
2899
        ifExist?: boolean,
2900
    ): Query {
UNCOV
2901
        const query = ifExist
×
2902
            ? `DROP TABLE IF EXISTS ${this.escapePath(tableOrName)}`
2903
            : `DROP TABLE ${this.escapePath(tableOrName)}`
UNCOV
2904
        return new Query(query)
×
2905
    }
2906

2907
    protected createViewSql(view: View): Query {
UNCOV
2908
        const materializedClause = view.materialized ? "MATERIALIZED " : ""
×
UNCOV
2909
        if (typeof view.expression === "string") {
×
UNCOV
2910
            return new Query(
×
2911
                `CREATE ${materializedClause}VIEW ${this.escapePath(view)} AS ${
2912
                    view.expression
2913
                }`,
2914
            )
2915
        } else {
UNCOV
2916
            return new Query(
×
2917
                `CREATE ${materializedClause}VIEW ${this.escapePath(
2918
                    view,
2919
                )} AS ${view.expression(this.connection).getQuery()}`,
2920
            )
2921
        }
2922
    }
2923

2924
    protected insertViewDefinitionSql(view: View): Query {
2925
        const expression =
UNCOV
2926
            typeof view.expression === "string"
×
2927
                ? view.expression.trim()
2928
                : view.expression(this.connection).getQuery()
UNCOV
2929
        const type = view.materialized
×
2930
            ? MetadataTableType.MATERIALIZED_VIEW
2931
            : MetadataTableType.VIEW
UNCOV
2932
        const { schema, tableName } = this.driver.parseTableName(view)
×
UNCOV
2933
        return this.insertTypeormMetadataSql({
×
2934
            type: type,
2935
            name: tableName,
2936
            schema: schema,
2937
            value: expression,
2938
        })
2939
    }
2940

2941
    /**
2942
     * Builds drop view sql.
2943
     */
2944
    protected dropViewSql(view: View): Query {
UNCOV
2945
        const materializedClause = view.materialized ? "MATERIALIZED " : ""
×
UNCOV
2946
        return new Query(
×
2947
            `DROP ${materializedClause}VIEW ${this.escapePath(view)}`,
2948
        )
2949
    }
2950

2951
    /**
2952
     * Builds remove view sql.
2953
     */
2954
    protected deleteViewDefinitionSql(view: View): Query {
UNCOV
2955
        const type = view.materialized
×
2956
            ? MetadataTableType.MATERIALIZED_VIEW
2957
            : MetadataTableType.VIEW
UNCOV
2958
        return this.deleteTypeormMetadataSql({ type, name: view.name })
×
2959
    }
2960

2961
    /**
2962
     * Builds create index sql.
2963
     */
2964
    protected createIndexSql(table: Table, index: TableIndex): Query {
UNCOV
2965
        const columns = index.columnNames
×
UNCOV
2966
            .map((columnName) => `"${columnName}"`)
×
2967
            .join(", ")
UNCOV
2968
        return new Query(
×
2969
            `CREATE ${index.isUnique ? "UNIQUE " : ""}INDEX "${
×
2970
                index.name
2971
            }" ON ${this.escapePath(table)} (${columns})`,
2972
        )
2973
    }
2974

2975
    /**
2976
     * Builds drop index sql.
2977
     */
2978
    protected dropIndexSql(indexOrName: TableIndex | string): Query {
UNCOV
2979
        const indexName = InstanceChecker.isTableIndex(indexOrName)
×
2980
            ? indexOrName.name
2981
            : indexOrName
UNCOV
2982
        return new Query(`DROP INDEX "${indexName}"`)
×
2983
    }
2984

2985
    /**
2986
     * Builds create primary key sql.
2987
     */
2988
    protected createPrimaryKeySql(
2989
        table: Table,
2990
        columnNames: string[],
2991
        constraintName?: string,
2992
    ): Query {
UNCOV
2993
        const primaryKeyName = constraintName
×
2994
            ? constraintName
2995
            : this.connection.namingStrategy.primaryKeyName(table, columnNames)
2996

UNCOV
2997
        const columnNamesString = columnNames
×
UNCOV
2998
            .map((columnName) => `"${columnName}"`)
×
2999
            .join(", ")
3000

UNCOV
3001
        return new Query(
×
3002
            `ALTER TABLE ${this.escapePath(
3003
                table,
3004
            )} ADD CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNamesString})`,
3005
        )
3006
    }
3007

3008
    /**
3009
     * Builds drop primary key sql.
3010
     */
3011
    protected dropPrimaryKeySql(table: Table): Query {
UNCOV
3012
        if (!table.primaryColumns.length)
×
3013
            throw new TypeORMError(`Table ${table} has no primary keys.`)
×
3014

UNCOV
3015
        const columnNames = table.primaryColumns.map((column) => column.name)
×
UNCOV
3016
        const constraintName = table.primaryColumns[0].primaryKeyConstraintName
×
UNCOV
3017
        const primaryKeyName = constraintName
×
3018
            ? constraintName
3019
            : this.connection.namingStrategy.primaryKeyName(table, columnNames)
3020

UNCOV
3021
        return new Query(
×
3022
            `ALTER TABLE ${this.escapePath(
3023
                table,
3024
            )} DROP CONSTRAINT "${primaryKeyName}"`,
3025
        )
3026
    }
3027

3028
    /**
3029
     * Builds create unique constraint sql.
3030
     */
3031
    protected createUniqueConstraintSql(
3032
        table: Table,
3033
        uniqueConstraint: TableUnique,
3034
    ): Query {
UNCOV
3035
        const columnNames = uniqueConstraint.columnNames
×
UNCOV
3036
            .map((column) => `"` + column + `"`)
×
3037
            .join(", ")
UNCOV
3038
        return new Query(
×
3039
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
3040
                uniqueConstraint.name
3041
            }" UNIQUE (${columnNames})`,
3042
        )
3043
    }
3044

3045
    /**
3046
     * Builds drop unique constraint sql.
3047
     */
3048
    protected dropUniqueConstraintSql(
3049
        table: Table,
3050
        uniqueOrName: TableUnique | string,
3051
    ): Query {
UNCOV
3052
        const uniqueName = InstanceChecker.isTableUnique(uniqueOrName)
×
3053
            ? uniqueOrName.name
3054
            : uniqueOrName
UNCOV
3055
        return new Query(
×
3056
            `ALTER TABLE ${this.escapePath(
3057
                table,
3058
            )} DROP CONSTRAINT "${uniqueName}"`,
3059
        )
3060
    }
3061

3062
    /**
3063
     * Builds create check constraint sql.
3064
     */
3065
    protected createCheckConstraintSql(
3066
        table: Table,
3067
        checkConstraint: TableCheck,
3068
    ): Query {
UNCOV
3069
        return new Query(
×
3070
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
3071
                checkConstraint.name
3072
            }" CHECK (${checkConstraint.expression})`,
3073
        )
3074
    }
3075

3076
    /**
3077
     * Builds drop check constraint sql.
3078
     */
3079
    protected dropCheckConstraintSql(
3080
        table: Table,
3081
        checkOrName: TableCheck | string,
3082
    ): Query {
UNCOV
3083
        const checkName = InstanceChecker.isTableCheck(checkOrName)
×
3084
            ? checkOrName.name
3085
            : checkOrName
UNCOV
3086
        return new Query(
×
3087
            `ALTER TABLE ${this.escapePath(
3088
                table,
3089
            )} DROP CONSTRAINT "${checkName}"`,
3090
        )
3091
    }
3092

3093
    /**
3094
     * Builds create foreign key sql.
3095
     */
3096
    protected createForeignKeySql(
3097
        table: Table,
3098
        foreignKey: TableForeignKey,
3099
    ): Query {
UNCOV
3100
        const columnNames = foreignKey.columnNames
×
UNCOV
3101
            .map((column) => `"` + column + `"`)
×
3102
            .join(", ")
UNCOV
3103
        const referencedColumnNames = foreignKey.referencedColumnNames
×
UNCOV
3104
            .map((column) => `"` + column + `"`)
×
3105
            .join(",")
3106
        let sql =
UNCOV
3107
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
×
3108
                foreignKey.name
3109
            }" FOREIGN KEY (${columnNames}) ` +
3110
            `REFERENCES ${this.escapePath(
3111
                this.getTablePath(foreignKey),
3112
            )} (${referencedColumnNames})`
3113
        // Oracle does not support NO ACTION, but we set NO ACTION by default in EntityMetadata
UNCOV
3114
        if (foreignKey.onDelete && foreignKey.onDelete !== "NO ACTION") {
×
UNCOV
3115
            sql += ` ON DELETE ${foreignKey.onDelete}`
×
3116
        }
UNCOV
3117
        return new Query(sql)
×
3118
    }
3119

3120
    /**
3121
     * Builds drop foreign key sql.
3122
     */
3123
    protected dropForeignKeySql(
3124
        table: Table,
3125
        foreignKeyOrName: TableForeignKey | string,
3126
    ): Query {
UNCOV
3127
        const foreignKeyName = InstanceChecker.isTableForeignKey(
×
3128
            foreignKeyOrName,
3129
        )
3130
            ? foreignKeyOrName.name
3131
            : foreignKeyOrName
UNCOV
3132
        return new Query(
×
3133
            `ALTER TABLE ${this.escapePath(
3134
                table,
3135
            )} DROP CONSTRAINT "${foreignKeyName}"`,
3136
        )
3137
    }
3138

3139
    /**
3140
     * Builds a query for create column.
3141
     */
3142
    protected buildCreateColumnSql(column: TableColumn) {
3143
        let c =
UNCOV
3144
            `"${column.name}" ` + this.connection.driver.createFullType(column)
×
UNCOV
3145
        if (column.charset) c += " CHARACTER SET " + column.charset
×
UNCOV
3146
        if (column.collation) c += " COLLATE " + column.collation
×
3147

UNCOV
3148
        if (column.asExpression) c += ` AS (${column.asExpression}) VIRTUAL`
×
3149

UNCOV
3150
        if (column.default !== undefined && column.default !== null)
×
3151
            // DEFAULT must be placed before NOT NULL
UNCOV
3152
            c += " DEFAULT " + column.default
×
UNCOV
3153
        if (column.isNullable !== true && !column.isGenerated)
×
3154
            // NOT NULL is not supported with GENERATED
UNCOV
3155
            c += " NOT NULL"
×
UNCOV
3156
        if (
×
3157
            column.isGenerated === true &&
×
3158
            column.generationStrategy === "increment"
3159
        )
UNCOV
3160
            c += " GENERATED BY DEFAULT AS IDENTITY"
×
3161

UNCOV
3162
        return c
×
3163
    }
3164

3165
    /**
3166
     * Escapes given table or view path.
3167
     */
3168
    protected escapePath(target: Table | View | string): string {
3169
        // Ignore database when escaping paths
UNCOV
3170
        const { schema, tableName } = this.driver.parseTableName(target)
×
3171

UNCOV
3172
        if (schema && schema !== this.driver.schema) {
×
3173
            return `"${schema}"."${tableName}"`
×
3174
        }
3175

UNCOV
3176
        return `"${tableName}"`
×
3177
    }
3178

3179
    /**
3180
     * Change table comment.
3181
     */
3182
    changeTableComment(
3183
        tableOrName: Table | string,
3184
        comment?: string,
3185
    ): Promise<void> {
3186
        throw new TypeORMError(
×
3187
            `oracle driver does not support change table comment.`,
3188
        )
3189
    }
3190
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc