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

typeorm / typeorm / 23390157208

21 Mar 2026 10:26PM UTC coverage: 56.678% (-16.6%) from 73.277%
23390157208

Pull #12252

github

web-flow
Merge 5b60ba41c into 7038fa166
Pull Request #12252: fix: unskip cascade soft remove test

17767 of 26580 branches covered (66.84%)

Branch coverage included in aggregate %.

64033 of 117744 relevant lines covered (54.38%)

1514.83 hits per line

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

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

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

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

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

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

28✔
51
    // -------------------------------------------------------------------------
28✔
52
    // Constructor
28✔
53
    // -------------------------------------------------------------------------
28✔
54

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

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

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

38✔
75
        if (this.databaseConnectionPromise)
38✔
76
            return this.databaseConnectionPromise
194✔
77

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

32✔
95
        return this.databaseConnectionPromise
32✔
96
    }
32✔
97

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

34✔
105
        if (!this.databaseConnection) {
34✔
106
            return
2✔
107
        }
2✔
108

32✔
109
        await this.databaseConnection.close()
32✔
110
    }
32✔
111

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

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

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

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

20✔
148
        await this.broadcaster.broadcast("AfterTransactionStart")
20✔
149
    }
20✔
150

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

20✔
158
        await this.broadcaster.broadcast("BeforeTransactionCommit")
20✔
159

20✔
160
        if (this.transactionDepth === 1) {
20✔
161
            await this.query("COMMIT")
20✔
162
            this.isTransactionActive = false
20✔
163
        }
20✔
164
        this.transactionDepth -= 1
20✔
165

20✔
166
        await this.broadcaster.broadcast("AfterTransactionCommit")
20✔
167
    }
20✔
168

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

×
176
        await this.broadcaster.broadcast("BeforeTransactionRollback")
×
177

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

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

28✔
191
    /**
28✔
192
     * Executes a given SQL query.
28✔
193
     * @param query
28✔
194
     * @param parameters
28✔
195
     * @param useStructuredResult
28✔
196
     */
28✔
197
    async query(
28✔
198
        query: string,
194✔
199
        parameters?: any[],
194✔
200
        useStructuredResult = false,
194✔
201
    ): Promise<any> {
194✔
202
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
194!
203

194✔
204
        const databaseConnection = await this.connect()
194✔
205

194✔
206
        this.driver.connection.logger.logQuery(query, parameters, this)
194✔
207
        await this.broadcaster.broadcast("BeforeQuery", query, parameters)
194✔
208

194✔
209
        const broadcasterResult = new BroadcasterResult()
194✔
210
        const queryStartTime = Date.now()
194✔
211

194✔
212
        try {
194✔
213
            const executionOptions = {
194✔
214
                autoCommit: !this.isTransactionActive,
194✔
215
                outFormat: this.driver.oracle.OUT_FORMAT_OBJECT,
194✔
216
            }
194✔
217

194✔
218
            const raw = await databaseConnection.execute(
194✔
219
                query,
194✔
220
                parameters || {},
194✔
221
                executionOptions,
194✔
222
            )
194✔
223

194✔
224
            // log slow queries if maxQueryExecution time is set
194✔
225
            const maxQueryExecutionTime =
194✔
226
                this.driver.options.maxQueryExecutionTime
194✔
227
            const queryEndTime = Date.now()
194✔
228
            const queryExecutionTime = queryEndTime - queryStartTime
194✔
229

194✔
230
            this.broadcaster.broadcastAfterQueryEvent(
194✔
231
                broadcasterResult,
194✔
232
                query,
194✔
233
                parameters,
194✔
234
                true,
194✔
235
                queryExecutionTime,
194✔
236
                raw,
194✔
237
                undefined,
194✔
238
            )
194✔
239

194✔
240
            if (
194✔
241
                maxQueryExecutionTime &&
194!
242
                queryExecutionTime > maxQueryExecutionTime
×
243
            )
194✔
244
                this.driver.connection.logger.logQuerySlow(
194!
245
                    queryExecutionTime,
×
246
                    query,
×
247
                    parameters,
×
248
                    this,
×
249
                )
×
250

194✔
251
            const result = new QueryResult()
194✔
252

194✔
253
            result.raw =
194✔
254
                raw.rows ||
194✔
255
                raw.outBinds ||
194✔
256
                raw.rowsAffected ||
194✔
257
                raw.implicitResults
78✔
258

194✔
259
            if (raw?.hasOwnProperty("rows") && Array.isArray(raw.rows)) {
194✔
260
                result.records = raw.rows
66✔
261
            }
66✔
262

194✔
263
            if (
194✔
264
                raw?.hasOwnProperty("outBinds") &&
194✔
265
                Array.isArray(raw.outBinds)
44✔
266
            ) {
194✔
267
                result.records = raw.outBinds
44✔
268
            }
44✔
269

194✔
270
            if (
194✔
271
                raw?.hasOwnProperty("implicitResults") &&
194!
272
                Array.isArray(raw.implicitResults)
×
273
            ) {
194!
274
                result.records = raw.implicitResults
×
275
            }
×
276

194✔
277
            if (raw?.hasOwnProperty("rowsAffected")) {
194✔
278
                result.affected = raw.rowsAffected
128✔
279
            }
128✔
280

194✔
281
            if (useStructuredResult) {
194✔
282
                return result
84✔
283
            } else {
194✔
284
                return result.raw
110✔
285
            }
110✔
286
        } catch (err) {
194!
287
            this.driver.connection.logger.logQueryError(
×
288
                err,
×
289
                query,
×
290
                parameters,
×
291
                this,
×
292
            )
×
293
            this.broadcaster.broadcastAfterQueryEvent(
×
294
                broadcasterResult,
×
295
                query,
×
296
                parameters,
×
297
                false,
×
298
                undefined,
×
299
                undefined,
×
300
                err,
×
301
            )
×
302

×
303
            throw new QueryFailedError(query, parameters, err)
×
304
        } finally {
×
305
            await broadcasterResult.wait()
194✔
306
        }
194✔
307
    }
194✔
308

28✔
309
    /**
28✔
310
     * Returns raw data stream.
28✔
311
     * @param query
28✔
312
     * @param parameters
28✔
313
     * @param onEnd
28✔
314
     * @param onError
28✔
315
     */
28✔
316
    async stream(
28✔
317
        query: string,
×
318
        parameters?: any[],
×
319
        onEnd?: Function,
×
320
        onError?: Function,
×
321
    ): Promise<ReadStream> {
×
322
        if (this.isReleased) {
×
323
            throw new QueryRunnerAlreadyReleasedError()
×
324
        }
×
325

×
326
        const executionOptions = {
×
327
            autoCommit: !this.isTransactionActive,
×
328
            outFormat: this.driver.oracle.OUT_FORMAT_OBJECT,
×
329
        }
×
330

×
331
        const databaseConnection = await this.connect()
×
332

×
333
        this.driver.connection.logger.logQuery(query, parameters, this)
×
334

×
335
        try {
×
336
            const stream = databaseConnection.queryStream(
×
337
                query,
×
338
                parameters,
×
339
                executionOptions,
×
340
            )
×
341
            if (onEnd) {
×
342
                stream.on("end", onEnd)
×
343
            }
×
344

×
345
            if (onError) {
×
346
                stream.on("error", onError)
×
347
            }
×
348

×
349
            return stream
×
350
        } catch (err) {
×
351
            this.driver.connection.logger.logQueryError(
×
352
                err,
×
353
                query,
×
354
                parameters,
×
355
                this,
×
356
            )
×
357
            throw new QueryFailedError(query, parameters, err)
×
358
        }
×
359
    }
×
360

28✔
361
    /**
28✔
362
     * Returns all available database names including system databases.
28✔
363
     */
28✔
364
    async getDatabases(): Promise<string[]> {
28✔
365
        return Promise.resolve([])
×
366
    }
×
367

28✔
368
    /**
28✔
369
     * Returns all available schema names including system schemas.
28✔
370
     * If database parameter specified, returns schemas of that database.
28✔
371
     * @param database
28✔
372
     */
28✔
373
    async getSchemas(database?: string): Promise<string[]> {
28✔
374
        return Promise.resolve([])
×
375
    }
×
376

28✔
377
    /**
28✔
378
     * Checks if database with the given name exist.
28✔
379
     * @param database
28✔
380
     */
28✔
381
    async hasDatabase(database: string): Promise<boolean> {
28✔
382
        try {
×
383
            const query = await this.query(
×
384
                `SELECT 1 AS "exists" FROM global_name@${this.driver.escape(database)}`,
×
385
            )
×
386

×
387
            return query.length > 0
×
388
        } catch (e) {
×
389
            return false
×
390
        }
×
391
    }
×
392

28✔
393
    /**
28✔
394
     * Loads currently using database
28✔
395
     */
28✔
396
    async getCurrentDatabase(): Promise<undefined> {
28✔
397
        const query = await this.query(
6✔
398
            `SELECT SYS_CONTEXT('USERENV','DB_NAME') AS "db_name" FROM dual`,
6✔
399
        )
6✔
400
        return query[0]["db_name"]
6✔
401
    }
6✔
402

28✔
403
    /**
28✔
404
     * Checks if schema with the given name exist.
28✔
405
     * @param schema
28✔
406
     */
28✔
407
    async hasSchema(schema: string): Promise<boolean> {
28✔
408
        return Promise.resolve(false)
×
409
    }
×
410

28✔
411
    /**
28✔
412
     * Loads currently using database schema
28✔
413
     */
28✔
414
    async getCurrentSchema(): Promise<string> {
28✔
415
        const query = await this.query(
6✔
416
            `SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') AS "schema_name" FROM dual`,
6✔
417
        )
6✔
418
        return query[0]["schema_name"]
6✔
419
    }
6✔
420

28✔
421
    /**
28✔
422
     * Checks if table with the given name exist in the database.
28✔
423
     * @param tableOrName
28✔
424
     */
28✔
425
    async hasTable(tableOrName: Table | string): Promise<boolean> {
28✔
426
        const { tableName } = this.driver.parseTableName(tableOrName)
4✔
427
        const sql = `SELECT "TABLE_NAME" FROM "USER_TABLES" WHERE "TABLE_NAME" = :1`
4✔
428
        const result = await this.query(sql, [tableName])
4✔
429
        return result.length ? true : false
4!
430
    }
4✔
431

28✔
432
    /**
28✔
433
     * Checks if column with the given name exist in the given table.
28✔
434
     * @param tableOrName
28✔
435
     * @param columnName
28✔
436
     */
28✔
437
    async hasColumn(
28✔
438
        tableOrName: Table | string,
×
439
        columnName: string,
×
440
    ): Promise<boolean> {
×
441
        const { tableName } = this.driver.parseTableName(tableOrName)
×
442
        const sql = `SELECT "COLUMN_NAME" FROM "USER_TAB_COLS" WHERE "TABLE_NAME" = :1 AND "COLUMN_NAME" = :2`
×
443
        const result = await this.query(sql, [tableName, columnName])
×
444
        return result.length ? true : false
×
445
    }
×
446

28✔
447
    /**
28✔
448
     * Creates a new database.
28✔
449
     * @param database
28✔
450
     * @param ifNotExists
28✔
451
     */
28✔
452
    async createDatabase(
28✔
453
        database: string,
×
454
        ifNotExists?: boolean,
×
455
    ): Promise<void> {
×
456
        // Even with `IF NOT EXISTS` we get:
×
457
        //   ORA-01501: CREATE DATABASE failed
×
458
        //   ORA-01100: database already mounted
×
459
        if (ifNotExists) {
×
460
            try {
×
461
                await this.query(
×
462
                    `CREATE DATABASE IF NOT EXISTS ${this.driver.escape(database)};`,
×
463
                )
×
464
            } catch (e) {
×
465
                // if (e instanceof QueryFailedError) {
×
466
                if (e.message.includes("ORA-01100: database already mounted")) {
×
467
                    return
×
468
                }
×
469
                // }
×
470

×
471
                throw e
×
472
            }
×
473
        } else {
×
474
            await this.query(`CREATE DATABASE ${this.driver.escape(database)}`)
×
475
        }
×
476
    }
×
477

28✔
478
    /**
28✔
479
     * Drops database.
28✔
480
     * @param database
28✔
481
     * @param ifExists
28✔
482
     */
28✔
483
    async dropDatabase(database: string, ifExists?: boolean): Promise<void> {
28✔
484
        return Promise.resolve()
×
485
    }
×
486

28✔
487
    /**
28✔
488
     * Creates a new table schema.
28✔
489
     * @param schemaPath
28✔
490
     * @param ifNotExists
28✔
491
     */
28✔
492
    async createSchema(
28✔
493
        schemaPath: string,
×
494
        ifNotExists?: boolean,
×
495
    ): Promise<void> {
×
496
        throw new TypeORMError(
×
497
            `Schema create queries are not supported by Oracle driver.`,
×
498
        )
×
499
    }
×
500

28✔
501
    /**
28✔
502
     * Drops table schema.
28✔
503
     * @param schemaPath
28✔
504
     * @param ifExists
28✔
505
     */
28✔
506
    async dropSchema(schemaPath: string, ifExists?: boolean): Promise<void> {
28✔
507
        throw new TypeORMError(
×
508
            `Schema drop queries are not supported by Oracle driver.`,
×
509
        )
×
510
    }
×
511

28✔
512
    /**
28✔
513
     * Creates a new table.
28✔
514
     * @param table
28✔
515
     * @param ifNotExists
28✔
516
     * @param createForeignKeys
28✔
517
     * @param createIndices
28✔
518
     */
28✔
519
    async createTable(
28✔
520
        table: Table,
12✔
521
        ifNotExists: boolean = false,
12✔
522
        createForeignKeys: boolean = true,
12✔
523
        createIndices: boolean = true,
12✔
524
    ): Promise<void> {
12✔
525
        if (ifNotExists) {
12!
526
            const isTableExist = await this.hasTable(table)
×
527
            if (isTableExist) return Promise.resolve()
×
528
        }
×
529
        const upQueries: Query[] = []
12✔
530
        const downQueries: Query[] = []
12✔
531

12✔
532
        upQueries.push(this.createTableSql(table, createForeignKeys))
12✔
533
        downQueries.push(this.dropTableSql(table))
12✔
534

12✔
535
        // if createForeignKeys is true, we must drop created foreign keys in down query.
12✔
536
        // createTable does not need separate method to create foreign keys, because it create fk's in the same query with table creation.
12✔
537
        if (createForeignKeys)
12✔
538
            table.foreignKeys.forEach((foreignKey) =>
12!
539
                downQueries.push(this.dropForeignKeySql(table, foreignKey)),
×
540
            )
×
541

12✔
542
        if (createIndices) {
12✔
543
            table.indices.forEach((index) => {
12✔
544
                // new index may be passed without name. In this case we generate index name manually.
8✔
545
                if (!index.name)
8✔
546
                    index.name = this.connection.namingStrategy.indexName(
8!
547
                        table,
×
548
                        index.columnNames,
×
549
                        index.where,
×
550
                    )
×
551
                upQueries.push(this.createIndexSql(table, index))
8✔
552
                downQueries.push(this.dropIndexSql(index))
8✔
553
            })
12✔
554
        }
12✔
555

12✔
556
        // if table have column with generated type, we must add the expression to the metadata table
12✔
557
        const generatedColumns = table.columns.filter(
12✔
558
            (column) => column.generatedType && column.asExpression,
12!
559
        )
12✔
560

12✔
561
        for (const column of generatedColumns) {
12!
562
            const insertQuery = this.insertTypeormMetadataSql({
×
563
                table: table.name,
×
564
                type: MetadataTableType.GENERATED_COLUMN,
×
565
                name: column.name,
×
566
                value: column.asExpression,
×
567
            })
×
568

×
569
            const deleteQuery = this.deleteTypeormMetadataSql({
×
570
                table: table.name,
×
571
                type: MetadataTableType.GENERATED_COLUMN,
×
572
                name: column.name,
×
573
            })
×
574

×
575
            upQueries.push(insertQuery)
×
576
            downQueries.push(deleteQuery)
×
577
        }
×
578

12✔
579
        await this.executeQueries(upQueries, downQueries)
12✔
580
    }
12✔
581

28✔
582
    /**
28✔
583
     * Drops the table.
28✔
584
     * @param tableOrName
28✔
585
     * @param ifExists
28✔
586
     * @param dropForeignKeys
28✔
587
     * @param dropIndices
28✔
588
     */
28✔
589
    async dropTable(
28✔
590
        tableOrName: Table | string,
×
591
        ifExists?: boolean,
×
592
        dropForeignKeys: boolean = true,
×
593
        dropIndices: boolean = true,
×
594
    ): Promise<void> {
×
595
        // It needs because if table does not exist and dropForeignKeys or dropIndices is true, we don't need
×
596
        // to perform drop queries for foreign keys and indices.
×
597
        if (ifExists) {
×
598
            const isTableExist = await this.hasTable(tableOrName)
×
599
            if (!isTableExist) return Promise.resolve()
×
600
        }
×
601

×
602
        // if dropTable called with dropForeignKeys = true, we must create foreign keys in down query.
×
603
        const createForeignKeys: boolean = dropForeignKeys
×
604
        const table = InstanceChecker.isTable(tableOrName)
×
605
            ? tableOrName
×
606
            : await this.getCachedTable(tableOrName)
×
607
        const upQueries: Query[] = []
×
608
        const downQueries: Query[] = []
×
609

×
610
        if (dropIndices) {
×
611
            table.indices.forEach((index) => {
×
612
                upQueries.push(this.dropIndexSql(index))
×
613
                downQueries.push(this.createIndexSql(table, index))
×
614
            })
×
615
        }
×
616

×
617
        // if dropForeignKeys is true, we just drop the table, otherwise we also drop table foreign keys.
×
618
        // createTable does not need separate method to create foreign keys, because it create fk's in the same query with table creation.
×
619
        if (dropForeignKeys)
×
620
            table.foreignKeys.forEach((foreignKey) =>
×
621
                upQueries.push(this.dropForeignKeySql(table, foreignKey)),
×
622
            )
×
623

×
624
        upQueries.push(this.dropTableSql(table))
×
625
        downQueries.push(this.createTableSql(table, createForeignKeys))
×
626

×
627
        // if table had columns with generated type, we must remove the expression from the metadata table
×
628
        const generatedColumns = table.columns.filter(
×
629
            (column) => column.generatedType && column.asExpression,
×
630
        )
×
631

×
632
        for (const column of generatedColumns) {
×
633
            const deleteQuery = this.deleteTypeormMetadataSql({
×
634
                table: table.name,
×
635
                type: MetadataTableType.GENERATED_COLUMN,
×
636
                name: column.name,
×
637
            })
×
638

×
639
            const insertQuery = this.insertTypeormMetadataSql({
×
640
                table: table.name,
×
641
                type: MetadataTableType.GENERATED_COLUMN,
×
642
                name: column.name,
×
643
                value: column.asExpression,
×
644
            })
×
645

×
646
            upQueries.push(deleteQuery)
×
647
            downQueries.push(insertQuery)
×
648
        }
×
649

×
650
        await this.executeQueries(upQueries, downQueries)
×
651
    }
×
652

28✔
653
    /**
28✔
654
     * Creates a new view.
28✔
655
     * @param view
28✔
656
     * @param syncWithMetadata
28✔
657
     */
28✔
658
    async createView(
28✔
659
        view: View,
×
660
        syncWithMetadata: boolean = false,
×
661
    ): Promise<void> {
×
662
        const upQueries: Query[] = []
×
663
        const downQueries: Query[] = []
×
664
        upQueries.push(this.createViewSql(view))
×
665
        if (syncWithMetadata) upQueries.push(this.insertViewDefinitionSql(view))
×
666
        downQueries.push(this.dropViewSql(view))
×
667
        if (syncWithMetadata)
×
668
            downQueries.push(this.deleteViewDefinitionSql(view))
×
669
        await this.executeQueries(upQueries, downQueries)
×
670
    }
×
671

28✔
672
    /**
28✔
673
     * Drops the view.
28✔
674
     * @param target
28✔
675
     * @param ifExists
28✔
676
     */
28✔
677
    async dropView(target: View | string, ifExists?: boolean): Promise<void> {
28✔
678
        const viewName = InstanceChecker.isView(target) ? target.name : target
×
679
        const view = ifExists
×
680
            ? await this.getCachedView(viewName).catch(() => undefined)
×
681
            : await this.getCachedView(viewName)
×
682
        if (!view) return
×
683

×
684
        const upQueries: Query[] = []
×
685
        const downQueries: Query[] = []
×
686
        upQueries.push(this.deleteViewDefinitionSql(view))
×
687
        upQueries.push(this.dropViewSql(view))
×
688
        downQueries.push(this.insertViewDefinitionSql(view))
×
689
        downQueries.push(this.createViewSql(view))
×
690
        await this.executeQueries(upQueries, downQueries)
×
691
    }
×
692

28✔
693
    /**
28✔
694
     * Renames the given table.
28✔
695
     * @param oldTableOrName
28✔
696
     * @param newTableName
28✔
697
     */
28✔
698
    async renameTable(
28✔
699
        oldTableOrName: Table | string,
×
700
        newTableName: string,
×
701
    ): Promise<void> {
×
702
        const upQueries: Query[] = []
×
703
        const downQueries: Query[] = []
×
704
        const oldTable = InstanceChecker.isTable(oldTableOrName)
×
705
            ? oldTableOrName
×
706
            : await this.getCachedTable(oldTableOrName)
×
707
        const newTable = oldTable.clone()
×
708

×
709
        const { database: dbName, tableName: oldTableName } =
×
710
            this.driver.parseTableName(oldTable)
×
711

×
712
        newTable.name = dbName ? `${dbName}.${newTableName}` : newTableName
×
713

×
714
        // rename table
×
715
        upQueries.push(
×
716
            new Query(
×
717
                `ALTER TABLE ${this.escapePath(
×
718
                    oldTable,
×
719
                )} RENAME TO "${newTableName}"`,
×
720
            ),
×
721
        )
×
722
        downQueries.push(
×
723
            new Query(
×
724
                `ALTER TABLE ${this.escapePath(
×
725
                    newTable,
×
726
                )} RENAME TO "${oldTableName}"`,
×
727
            ),
×
728
        )
×
729

×
730
        // rename primary key constraint
×
731
        if (
×
732
            newTable.primaryColumns.length > 0 &&
×
733
            !newTable.primaryColumns[0].primaryKeyConstraintName
×
734
        ) {
×
735
            const columnNames = newTable.primaryColumns.map(
×
736
                (column) => column.name,
×
737
            )
×
738

×
739
            const oldPkName = this.connection.namingStrategy.primaryKeyName(
×
740
                oldTable,
×
741
                columnNames,
×
742
            )
×
743
            const newPkName = this.connection.namingStrategy.primaryKeyName(
×
744
                newTable,
×
745
                columnNames,
×
746
            )
×
747

×
748
            // build queries
×
749
            upQueries.push(
×
750
                new Query(
×
751
                    `ALTER TABLE ${this.escapePath(
×
752
                        newTable,
×
753
                    )} RENAME CONSTRAINT "${oldPkName}" TO "${newPkName}"`,
×
754
                ),
×
755
            )
×
756
            downQueries.push(
×
757
                new Query(
×
758
                    `ALTER TABLE ${this.escapePath(
×
759
                        newTable,
×
760
                    )} RENAME CONSTRAINT "${newPkName}" TO "${oldPkName}"`,
×
761
                ),
×
762
            )
×
763
        }
×
764

×
765
        // rename unique constraints
×
766
        newTable.uniques.forEach((unique) => {
×
767
            const oldUniqueName =
×
768
                this.connection.namingStrategy.uniqueConstraintName(
×
769
                    oldTable,
×
770
                    unique.columnNames,
×
771
                )
×
772

×
773
            // Skip renaming if Unique has user defined constraint name
×
774
            if (unique.name !== oldUniqueName) return
×
775

×
776
            // build new constraint name
×
777
            const newUniqueName =
×
778
                this.connection.namingStrategy.uniqueConstraintName(
×
779
                    newTable,
×
780
                    unique.columnNames,
×
781
                )
×
782

×
783
            // build queries
×
784
            upQueries.push(
×
785
                new Query(
×
786
                    `ALTER TABLE ${this.escapePath(
×
787
                        newTable,
×
788
                    )} RENAME CONSTRAINT "${
×
789
                        unique.name
×
790
                    }" TO "${newUniqueName}"`,
×
791
                ),
×
792
            )
×
793
            downQueries.push(
×
794
                new Query(
×
795
                    `ALTER TABLE ${this.escapePath(
×
796
                        newTable,
×
797
                    )} RENAME CONSTRAINT "${newUniqueName}" TO "${
×
798
                        unique.name
×
799
                    }"`,
×
800
                ),
×
801
            )
×
802

×
803
            // replace constraint name
×
804
            unique.name = newUniqueName
×
805
        })
×
806

×
807
        // rename index constraints
×
808
        newTable.indices.forEach((index) => {
×
809
            const oldIndexName = this.connection.namingStrategy.indexName(
×
810
                oldTable,
×
811
                index.columnNames,
×
812
                index.where,
×
813
            )
×
814

×
815
            // Skip renaming if Index has user defined constraint name
×
816
            if (index.name !== oldIndexName) return
×
817

×
818
            // build new constraint name
×
819
            const newIndexName = this.connection.namingStrategy.indexName(
×
820
                newTable,
×
821
                index.columnNames,
×
822
                index.where,
×
823
            )
×
824

×
825
            // build queries
×
826
            upQueries.push(
×
827
                new Query(
×
828
                    `ALTER INDEX "${index.name}" RENAME TO "${newIndexName}"`,
×
829
                ),
×
830
            )
×
831
            downQueries.push(
×
832
                new Query(
×
833
                    `ALTER INDEX "${newIndexName}" RENAME TO "${index.name}"`,
×
834
                ),
×
835
            )
×
836

×
837
            // replace constraint name
×
838
            index.name = newIndexName
×
839
        })
×
840

×
841
        // rename foreign key constraints
×
842
        newTable.foreignKeys.forEach((foreignKey) => {
×
843
            const oldForeignKeyName =
×
844
                this.connection.namingStrategy.foreignKeyName(
×
845
                    oldTable,
×
846
                    foreignKey.columnNames,
×
847
                    this.getTablePath(foreignKey),
×
848
                    foreignKey.referencedColumnNames,
×
849
                )
×
850

×
851
            // Skip renaming if foreign key has user defined constraint name
×
852
            if (foreignKey.name !== oldForeignKeyName) return
×
853

×
854
            // build new constraint name
×
855
            const newForeignKeyName =
×
856
                this.connection.namingStrategy.foreignKeyName(
×
857
                    newTable,
×
858
                    foreignKey.columnNames,
×
859
                    this.getTablePath(foreignKey),
×
860
                    foreignKey.referencedColumnNames,
×
861
                )
×
862

×
863
            // build queries
×
864
            upQueries.push(
×
865
                new Query(
×
866
                    `ALTER TABLE ${this.escapePath(
×
867
                        newTable,
×
868
                    )} RENAME CONSTRAINT "${
×
869
                        foreignKey.name
×
870
                    }" TO "${newForeignKeyName}"`,
×
871
                ),
×
872
            )
×
873
            downQueries.push(
×
874
                new Query(
×
875
                    `ALTER TABLE ${this.escapePath(
×
876
                        newTable,
×
877
                    )} RENAME CONSTRAINT "${newForeignKeyName}" TO "${
×
878
                        foreignKey.name
×
879
                    }"`,
×
880
                ),
×
881
            )
×
882

×
883
            // replace constraint name
×
884
            foreignKey.name = newForeignKeyName
×
885
        })
×
886

×
887
        await this.executeQueries(upQueries, downQueries)
×
888

×
889
        // rename old table and replace it in cached tabled;
×
890
        oldTable.name = newTable.name
×
891
        this.replaceCachedTable(oldTable, newTable)
×
892
    }
×
893

28✔
894
    /**
28✔
895
     * Creates a new column from the column in the table.
28✔
896
     * @param tableOrName
28✔
897
     * @param column
28✔
898
     */
28✔
899
    async addColumn(
28✔
900
        tableOrName: Table | string,
×
901
        column: TableColumn,
×
902
    ): Promise<void> {
×
903
        const table = InstanceChecker.isTable(tableOrName)
×
904
            ? tableOrName
×
905
            : await this.getCachedTable(tableOrName)
×
906
        const clonedTable = table.clone()
×
907
        const upQueries: Query[] = []
×
908
        const downQueries: Query[] = []
×
909

×
910
        upQueries.push(
×
911
            new Query(
×
912
                `ALTER TABLE ${this.escapePath(
×
913
                    table,
×
914
                )} ADD ${this.buildCreateColumnSql(column)}`,
×
915
            ),
×
916
        )
×
917
        downQueries.push(
×
918
            new Query(
×
919
                `ALTER TABLE ${this.escapePath(table)} DROP COLUMN "${
×
920
                    column.name
×
921
                }"`,
×
922
            ),
×
923
        )
×
924

×
925
        // create or update primary key constraint
×
926
        if (column.isPrimary) {
×
927
            const primaryColumns = clonedTable.primaryColumns
×
928
            // if table already have primary key, me must drop it and recreate again
×
929
            if (primaryColumns.length > 0) {
×
930
                const pkName = primaryColumns[0].primaryKeyConstraintName
×
931
                    ? primaryColumns[0].primaryKeyConstraintName
×
932
                    : this.connection.namingStrategy.primaryKeyName(
×
933
                          clonedTable,
×
934
                          primaryColumns.map((column) => column.name),
×
935
                      )
×
936

×
937
                const columnNames = primaryColumns
×
938
                    .map((column) => `"${column.name}"`)
×
939
                    .join(", ")
×
940

×
941
                upQueries.push(
×
942
                    new Query(
×
943
                        `ALTER TABLE ${this.escapePath(
×
944
                            table,
×
945
                        )} DROP CONSTRAINT "${pkName}"`,
×
946
                    ),
×
947
                )
×
948
                downQueries.push(
×
949
                    new Query(
×
950
                        `ALTER TABLE ${this.escapePath(
×
951
                            table,
×
952
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
×
953
                    ),
×
954
                )
×
955
            }
×
956

×
957
            primaryColumns.push(column)
×
958
            const pkName = primaryColumns[0].primaryKeyConstraintName
×
959
                ? primaryColumns[0].primaryKeyConstraintName
×
960
                : this.connection.namingStrategy.primaryKeyName(
×
961
                      clonedTable,
×
962
                      primaryColumns.map((column) => column.name),
×
963
                  )
×
964

×
965
            const columnNames = primaryColumns
×
966
                .map((column) => `"${column.name}"`)
×
967
                .join(", ")
×
968

×
969
            upQueries.push(
×
970
                new Query(
×
971
                    `ALTER TABLE ${this.escapePath(
×
972
                        table,
×
973
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
×
974
                ),
×
975
            )
×
976
            downQueries.push(
×
977
                new Query(
×
978
                    `ALTER TABLE ${this.escapePath(
×
979
                        table,
×
980
                    )} DROP CONSTRAINT "${pkName}"`,
×
981
                ),
×
982
            )
×
983
        }
×
984

×
985
        // create column index
×
986
        const columnIndex = clonedTable.indices.find(
×
987
            (index) =>
×
988
                index.columnNames.length === 1 &&
×
989
                index.columnNames[0] === column.name,
×
990
        )
×
991
        if (columnIndex) {
×
992
            clonedTable.indices.splice(
×
993
                clonedTable.indices.indexOf(columnIndex),
×
994
                1,
×
995
            )
×
996
            upQueries.push(this.createIndexSql(table, columnIndex))
×
997
            downQueries.push(this.dropIndexSql(columnIndex))
×
998
        }
×
999

×
1000
        // create unique constraint
×
1001
        if (column.isUnique) {
×
1002
            const uniqueConstraint = new TableUnique({
×
1003
                name: this.connection.namingStrategy.uniqueConstraintName(
×
1004
                    table,
×
1005
                    [column.name],
×
1006
                ),
×
1007
                columnNames: [column.name],
×
1008
            })
×
1009
            clonedTable.uniques.push(uniqueConstraint)
×
1010
            upQueries.push(
×
1011
                new Query(
×
1012
                    `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
×
1013
                        uniqueConstraint.name
×
1014
                    }" UNIQUE ("${column.name}")`,
×
1015
                ),
×
1016
            )
×
1017
            downQueries.push(
×
1018
                new Query(
×
1019
                    `ALTER TABLE ${this.escapePath(table)} DROP CONSTRAINT "${
×
1020
                        uniqueConstraint.name
×
1021
                    }"`,
×
1022
                ),
×
1023
            )
×
1024
        }
×
1025

×
1026
        if (column.generatedType && column.asExpression) {
×
1027
            const insertQuery = this.insertTypeormMetadataSql({
×
1028
                table: table.name,
×
1029
                type: MetadataTableType.GENERATED_COLUMN,
×
1030
                name: column.name,
×
1031
                value: column.asExpression,
×
1032
            })
×
1033

×
1034
            const deleteQuery = this.deleteTypeormMetadataSql({
×
1035
                table: table.name,
×
1036
                type: MetadataTableType.GENERATED_COLUMN,
×
1037
                name: column.name,
×
1038
            })
×
1039

×
1040
            upQueries.push(insertQuery)
×
1041
            downQueries.push(deleteQuery)
×
1042
        }
×
1043

×
1044
        await this.executeQueries(upQueries, downQueries)
×
1045

×
1046
        clonedTable.addColumn(column)
×
1047
        this.replaceCachedTable(table, clonedTable)
×
1048
    }
×
1049

28✔
1050
    /**
28✔
1051
     * Creates a new columns from the column in the table.
28✔
1052
     * @param tableOrName
28✔
1053
     * @param columns
28✔
1054
     */
28✔
1055
    async addColumns(
28✔
1056
        tableOrName: Table | string,
×
1057
        columns: TableColumn[],
×
1058
    ): Promise<void> {
×
1059
        for (const column of columns) {
×
1060
            await this.addColumn(tableOrName, column)
×
1061
        }
×
1062
    }
×
1063

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

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

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

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

×
1117
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
×
1118
            ? oldTableColumnOrName
×
1119
            : table.columns.find(
×
1120
                  (column) => column.name === oldTableColumnOrName,
×
1121
              )
×
1122
        if (!oldColumn)
×
1123
            throw new TypeORMError(
×
1124
                `Column "${oldTableColumnOrName}" was not found in the ${this.escapePath(
×
1125
                    table,
×
1126
                )} table.`,
×
1127
            )
×
1128

×
1129
        if (
×
1130
            (newColumn.isGenerated !== oldColumn.isGenerated &&
×
1131
                newColumn.generationStrategy !== "uuid") ||
×
1132
            oldColumn.type !== newColumn.type ||
×
1133
            oldColumn.length !== newColumn.length ||
×
1134
            oldColumn.generatedType !== newColumn.generatedType ||
×
1135
            oldColumn.asExpression !== newColumn.asExpression
×
1136
        ) {
×
1137
            // Oracle does not support changing of IDENTITY column, so we must drop column and recreate it again.
×
1138
            // Also, we recreate column if column type changed
×
1139
            await this.dropColumn(table, oldColumn)
×
1140
            await this.addColumn(table, newColumn)
×
1141

×
1142
            // update cloned table
×
1143
            clonedTable = table.clone()
×
1144
        } else {
×
1145
            if (newColumn.name !== oldColumn.name) {
×
1146
                // rename column
×
1147
                upQueries.push(
×
1148
                    new Query(
×
1149
                        `ALTER TABLE ${this.escapePath(table)} RENAME COLUMN "${
×
1150
                            oldColumn.name
×
1151
                        }" TO "${newColumn.name}"`,
×
1152
                    ),
×
1153
                )
×
1154
                downQueries.push(
×
1155
                    new Query(
×
1156
                        `ALTER TABLE ${this.escapePath(table)} RENAME COLUMN "${
×
1157
                            newColumn.name
×
1158
                        }" TO "${oldColumn.name}"`,
×
1159
                    ),
×
1160
                )
×
1161

×
1162
                // rename column primary key constraint
×
1163
                if (
×
1164
                    oldColumn.isPrimary === true &&
×
1165
                    !oldColumn.primaryKeyConstraintName
×
1166
                ) {
×
1167
                    const primaryColumns = clonedTable.primaryColumns
×
1168

×
1169
                    // build old primary constraint name
×
1170
                    const columnNames = primaryColumns.map(
×
1171
                        (column) => column.name,
×
1172
                    )
×
1173
                    const oldPkName =
×
1174
                        this.connection.namingStrategy.primaryKeyName(
×
1175
                            clonedTable,
×
1176
                            columnNames,
×
1177
                        )
×
1178

×
1179
                    // replace old column name with new column name
×
1180
                    columnNames.splice(columnNames.indexOf(oldColumn.name), 1)
×
1181
                    columnNames.push(newColumn.name)
×
1182

×
1183
                    // build new primary constraint name
×
1184
                    const newPkName =
×
1185
                        this.connection.namingStrategy.primaryKeyName(
×
1186
                            clonedTable,
×
1187
                            columnNames,
×
1188
                        )
×
1189

×
1190
                    upQueries.push(
×
1191
                        new Query(
×
1192
                            `ALTER TABLE ${this.escapePath(
×
1193
                                table,
×
1194
                            )} RENAME CONSTRAINT "${oldPkName}" TO "${newPkName}"`,
×
1195
                        ),
×
1196
                    )
×
1197
                    downQueries.push(
×
1198
                        new Query(
×
1199
                            `ALTER TABLE ${this.escapePath(
×
1200
                                table,
×
1201
                            )} RENAME CONSTRAINT "${newPkName}" TO "${oldPkName}"`,
×
1202
                        ),
×
1203
                    )
×
1204
                }
×
1205

×
1206
                // rename unique constraints
×
1207
                clonedTable.findColumnUniques(oldColumn).forEach((unique) => {
×
1208
                    const oldUniqueName =
×
1209
                        this.connection.namingStrategy.uniqueConstraintName(
×
1210
                            clonedTable,
×
1211
                            unique.columnNames,
×
1212
                        )
×
1213

×
1214
                    // Skip renaming if Unique has user defined constraint name
×
1215
                    if (unique.name !== oldUniqueName) return
×
1216

×
1217
                    // build new constraint name
×
1218
                    unique.columnNames.splice(
×
1219
                        unique.columnNames.indexOf(oldColumn.name),
×
1220
                        1,
×
1221
                    )
×
1222
                    unique.columnNames.push(newColumn.name)
×
1223
                    const newUniqueName =
×
1224
                        this.connection.namingStrategy.uniqueConstraintName(
×
1225
                            clonedTable,
×
1226
                            unique.columnNames,
×
1227
                        )
×
1228

×
1229
                    // build queries
×
1230
                    upQueries.push(
×
1231
                        new Query(
×
1232
                            `ALTER TABLE ${this.escapePath(
×
1233
                                table,
×
1234
                            )} RENAME CONSTRAINT "${
×
1235
                                unique.name
×
1236
                            }" TO "${newUniqueName}"`,
×
1237
                        ),
×
1238
                    )
×
1239
                    downQueries.push(
×
1240
                        new Query(
×
1241
                            `ALTER TABLE ${this.escapePath(
×
1242
                                table,
×
1243
                            )} RENAME CONSTRAINT "${newUniqueName}" TO "${
×
1244
                                unique.name
×
1245
                            }"`,
×
1246
                        ),
×
1247
                    )
×
1248

×
1249
                    // replace constraint name
×
1250
                    unique.name = newUniqueName
×
1251
                })
×
1252

×
1253
                // rename index constraints
×
1254
                clonedTable.findColumnIndices(oldColumn).forEach((index) => {
×
1255
                    const oldIndexName =
×
1256
                        this.connection.namingStrategy.indexName(
×
1257
                            clonedTable,
×
1258
                            index.columnNames,
×
1259
                            index.where,
×
1260
                        )
×
1261

×
1262
                    // Skip renaming if Index has user defined constraint name
×
1263
                    if (index.name !== oldIndexName) return
×
1264

×
1265
                    // build new constraint name
×
1266
                    index.columnNames.splice(
×
1267
                        index.columnNames.indexOf(oldColumn.name),
×
1268
                        1,
×
1269
                    )
×
1270
                    index.columnNames.push(newColumn.name)
×
1271
                    const newIndexName =
×
1272
                        this.connection.namingStrategy.indexName(
×
1273
                            clonedTable,
×
1274
                            index.columnNames,
×
1275
                            index.where,
×
1276
                        )
×
1277

×
1278
                    // build queries
×
1279
                    upQueries.push(
×
1280
                        new Query(
×
1281
                            `ALTER INDEX "${index.name}" RENAME TO "${newIndexName}"`,
×
1282
                        ),
×
1283
                    )
×
1284
                    downQueries.push(
×
1285
                        new Query(
×
1286
                            `ALTER INDEX "${newIndexName}" RENAME TO "${index.name}"`,
×
1287
                        ),
×
1288
                    )
×
1289

×
1290
                    // replace constraint name
×
1291
                    index.name = newIndexName
×
1292
                })
×
1293

×
1294
                // rename foreign key constraints
×
1295
                clonedTable
×
1296
                    .findColumnForeignKeys(oldColumn)
×
1297
                    .forEach((foreignKey) => {
×
1298
                        const foreignKeyName =
×
1299
                            this.connection.namingStrategy.foreignKeyName(
×
1300
                                clonedTable,
×
1301
                                foreignKey.columnNames,
×
1302
                                this.getTablePath(foreignKey),
×
1303
                                foreignKey.referencedColumnNames,
×
1304
                            )
×
1305

×
1306
                        // Skip renaming if foreign key has user defined constraint name
×
1307
                        if (foreignKey.name !== foreignKeyName) return
×
1308

×
1309
                        // build new constraint name
×
1310
                        foreignKey.columnNames.splice(
×
1311
                            foreignKey.columnNames.indexOf(oldColumn.name),
×
1312
                            1,
×
1313
                        )
×
1314
                        foreignKey.columnNames.push(newColumn.name)
×
1315
                        const newForeignKeyName =
×
1316
                            this.connection.namingStrategy.foreignKeyName(
×
1317
                                clonedTable,
×
1318
                                foreignKey.columnNames,
×
1319
                                this.getTablePath(foreignKey),
×
1320
                                foreignKey.referencedColumnNames,
×
1321
                            )
×
1322

×
1323
                        // build queries
×
1324
                        upQueries.push(
×
1325
                            new Query(
×
1326
                                `ALTER TABLE ${this.escapePath(
×
1327
                                    table,
×
1328
                                )} RENAME CONSTRAINT "${
×
1329
                                    foreignKey.name
×
1330
                                }" TO "${newForeignKeyName}"`,
×
1331
                            ),
×
1332
                        )
×
1333
                        downQueries.push(
×
1334
                            new Query(
×
1335
                                `ALTER TABLE ${this.escapePath(
×
1336
                                    table,
×
1337
                                )} RENAME CONSTRAINT "${newForeignKeyName}" TO "${
×
1338
                                    foreignKey.name
×
1339
                                }"`,
×
1340
                            ),
×
1341
                        )
×
1342

×
1343
                        // replace constraint name
×
1344
                        foreignKey.name = newForeignKeyName
×
1345
                    })
×
1346

×
1347
                // rename old column in the Table object
×
1348
                const oldTableColumn = clonedTable.columns.find(
×
1349
                    (column) => column.name === oldColumn.name,
×
1350
                )
×
1351
                clonedTable.columns[
×
1352
                    clonedTable.columns.indexOf(oldTableColumn!)
×
1353
                ].name = newColumn.name
×
1354
                oldColumn.name = newColumn.name
×
1355
            }
×
1356

×
1357
            if (this.isColumnChanged(oldColumn, newColumn, true)) {
×
1358
                let defaultUp: string = ""
×
1359
                let defaultDown: string = ""
×
1360
                let nullableUp: string = ""
×
1361
                let nullableDown: string = ""
×
1362

×
1363
                // changing column default
×
1364
                if (
×
1365
                    newColumn.default !== null &&
×
1366
                    newColumn.default !== undefined
×
1367
                ) {
×
1368
                    defaultUp = `DEFAULT ${newColumn.default}`
×
1369

×
1370
                    if (
×
1371
                        oldColumn.default !== null &&
×
1372
                        oldColumn.default !== undefined
×
1373
                    ) {
×
1374
                        defaultDown = `DEFAULT ${oldColumn.default}`
×
1375
                    } else {
×
1376
                        defaultDown = "DEFAULT NULL"
×
1377
                    }
×
1378
                } else if (
×
1379
                    oldColumn.default !== null &&
×
1380
                    oldColumn.default !== undefined
×
1381
                ) {
×
1382
                    defaultUp = "DEFAULT NULL"
×
1383
                    defaultDown = `DEFAULT ${oldColumn.default}`
×
1384
                }
×
1385

×
1386
                // changing column isNullable property
×
1387
                if (newColumn.isNullable !== oldColumn.isNullable) {
×
1388
                    if (newColumn.isNullable === true) {
×
1389
                        nullableUp = "NULL"
×
1390
                        nullableDown = "NOT NULL"
×
1391
                    } else {
×
1392
                        nullableUp = "NOT NULL"
×
1393
                        nullableDown = "NULL"
×
1394
                    }
×
1395
                }
×
1396

×
1397
                upQueries.push(
×
1398
                    new Query(
×
1399
                        `ALTER TABLE ${this.escapePath(table)} MODIFY "${
×
1400
                            oldColumn.name
×
1401
                        }" ${this.connection.driver.createFullType(
×
1402
                            newColumn,
×
1403
                        )} ${defaultUp} ${nullableUp}`,
×
1404
                    ),
×
1405
                )
×
1406
                downQueries.push(
×
1407
                    new Query(
×
1408
                        `ALTER TABLE ${this.escapePath(table)} MODIFY "${
×
1409
                            oldColumn.name
×
1410
                        }" ${this.connection.driver.createFullType(
×
1411
                            oldColumn,
×
1412
                        )} ${defaultDown} ${nullableDown}`,
×
1413
                    ),
×
1414
                )
×
1415
            }
×
1416

×
1417
            if (newColumn.isPrimary !== oldColumn.isPrimary) {
×
1418
                const primaryColumns = clonedTable.primaryColumns
×
1419

×
1420
                // if primary column state changed, we must always drop existed constraint.
×
1421
                if (primaryColumns.length > 0) {
×
1422
                    const pkName = primaryColumns[0].primaryKeyConstraintName
×
1423
                        ? primaryColumns[0].primaryKeyConstraintName
×
1424
                        : this.connection.namingStrategy.primaryKeyName(
×
1425
                              clonedTable,
×
1426
                              primaryColumns.map((column) => column.name),
×
1427
                          )
×
1428

×
1429
                    const columnNames = primaryColumns
×
1430
                        .map((column) => `"${column.name}"`)
×
1431
                        .join(", ")
×
1432

×
1433
                    upQueries.push(
×
1434
                        new Query(
×
1435
                            `ALTER TABLE ${this.escapePath(
×
1436
                                table,
×
1437
                            )} DROP CONSTRAINT "${pkName}"`,
×
1438
                        ),
×
1439
                    )
×
1440
                    downQueries.push(
×
1441
                        new Query(
×
1442
                            `ALTER TABLE ${this.escapePath(
×
1443
                                table,
×
1444
                            )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
×
1445
                        ),
×
1446
                    )
×
1447
                }
×
1448

×
1449
                if (newColumn.isPrimary === true) {
×
1450
                    primaryColumns.push(newColumn)
×
1451
                    // update column in table
×
1452
                    const column = clonedTable.columns.find(
×
1453
                        (column) => column.name === newColumn.name,
×
1454
                    )
×
1455
                    column!.isPrimary = true
×
1456
                    const pkName = primaryColumns[0].primaryKeyConstraintName
×
1457
                        ? primaryColumns[0].primaryKeyConstraintName
×
1458
                        : this.connection.namingStrategy.primaryKeyName(
×
1459
                              clonedTable,
×
1460
                              primaryColumns.map((column) => column.name),
×
1461
                          )
×
1462

×
1463
                    const columnNames = primaryColumns
×
1464
                        .map((column) => `"${column.name}"`)
×
1465
                        .join(", ")
×
1466

×
1467
                    upQueries.push(
×
1468
                        new Query(
×
1469
                            `ALTER TABLE ${this.escapePath(
×
1470
                                table,
×
1471
                            )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
×
1472
                        ),
×
1473
                    )
×
1474
                    downQueries.push(
×
1475
                        new Query(
×
1476
                            `ALTER TABLE ${this.escapePath(
×
1477
                                table,
×
1478
                            )} DROP CONSTRAINT "${pkName}"`,
×
1479
                        ),
×
1480
                    )
×
1481
                } else {
×
1482
                    const primaryColumn = primaryColumns.find(
×
1483
                        (c) => c.name === newColumn.name,
×
1484
                    )
×
1485
                    primaryColumns.splice(
×
1486
                        primaryColumns.indexOf(primaryColumn!),
×
1487
                        1,
×
1488
                    )
×
1489

×
1490
                    // update column in table
×
1491
                    const column = clonedTable.columns.find(
×
1492
                        (column) => column.name === newColumn.name,
×
1493
                    )
×
1494
                    column!.isPrimary = false
×
1495

×
1496
                    // if we have another primary keys, we must recreate constraint.
×
1497
                    if (primaryColumns.length > 0) {
×
1498
                        const pkName = primaryColumns[0]
×
1499
                            .primaryKeyConstraintName
×
1500
                            ? primaryColumns[0].primaryKeyConstraintName
×
1501
                            : this.connection.namingStrategy.primaryKeyName(
×
1502
                                  clonedTable,
×
1503
                                  primaryColumns.map((column) => column.name),
×
1504
                              )
×
1505

×
1506
                        const columnNames = primaryColumns
×
1507
                            .map((column) => `"${column.name}"`)
×
1508
                            .join(", ")
×
1509

×
1510
                        upQueries.push(
×
1511
                            new Query(
×
1512
                                `ALTER TABLE ${this.escapePath(
×
1513
                                    table,
×
1514
                                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
×
1515
                            ),
×
1516
                        )
×
1517
                        downQueries.push(
×
1518
                            new Query(
×
1519
                                `ALTER TABLE ${this.escapePath(
×
1520
                                    table,
×
1521
                                )} DROP CONSTRAINT "${pkName}"`,
×
1522
                            ),
×
1523
                        )
×
1524
                    }
×
1525
                }
×
1526
            }
×
1527

×
1528
            if (newColumn.isUnique !== oldColumn.isUnique) {
×
1529
                if (newColumn.isUnique === true) {
×
1530
                    const uniqueConstraint = new TableUnique({
×
1531
                        name: this.connection.namingStrategy.uniqueConstraintName(
×
1532
                            table,
×
1533
                            [newColumn.name],
×
1534
                        ),
×
1535
                        columnNames: [newColumn.name],
×
1536
                    })
×
1537
                    clonedTable.uniques.push(uniqueConstraint)
×
1538
                    upQueries.push(
×
1539
                        new Query(
×
1540
                            `ALTER TABLE ${this.escapePath(
×
1541
                                table,
×
1542
                            )} ADD CONSTRAINT "${
×
1543
                                uniqueConstraint.name
×
1544
                            }" UNIQUE ("${newColumn.name}")`,
×
1545
                        ),
×
1546
                    )
×
1547
                    downQueries.push(
×
1548
                        new Query(
×
1549
                            `ALTER TABLE ${this.escapePath(
×
1550
                                table,
×
1551
                            )} DROP CONSTRAINT "${uniqueConstraint.name}"`,
×
1552
                        ),
×
1553
                    )
×
1554
                } else {
×
1555
                    const uniqueConstraint = clonedTable.uniques.find(
×
1556
                        (unique) => {
×
1557
                            return (
×
1558
                                unique.columnNames.length === 1 &&
×
1559
                                !!unique.columnNames.find(
×
1560
                                    (columnName) =>
×
1561
                                        columnName === newColumn.name,
×
1562
                                )
×
1563
                            )
×
1564
                        },
×
1565
                    )
×
1566
                    clonedTable.uniques.splice(
×
1567
                        clonedTable.uniques.indexOf(uniqueConstraint!),
×
1568
                        1,
×
1569
                    )
×
1570
                    upQueries.push(
×
1571
                        new Query(
×
1572
                            `ALTER TABLE ${this.escapePath(
×
1573
                                table,
×
1574
                            )} DROP CONSTRAINT "${uniqueConstraint!.name}"`,
×
1575
                        ),
×
1576
                    )
×
1577
                    downQueries.push(
×
1578
                        new Query(
×
1579
                            `ALTER TABLE ${this.escapePath(
×
1580
                                table,
×
1581
                            )} ADD CONSTRAINT "${
×
1582
                                uniqueConstraint!.name
×
1583
                            }" UNIQUE ("${newColumn.name}")`,
×
1584
                        ),
×
1585
                    )
×
1586
                }
×
1587
            }
×
1588

×
1589
            await this.executeQueries(upQueries, downQueries)
×
1590
            this.replaceCachedTable(table, clonedTable)
×
1591
        }
×
1592
    }
×
1593

28✔
1594
    /**
28✔
1595
     * Changes a column in the table.
28✔
1596
     * @param tableOrName
28✔
1597
     * @param changedColumns
28✔
1598
     */
28✔
1599
    async changeColumns(
28✔
1600
        tableOrName: Table | string,
×
1601
        changedColumns: { newColumn: TableColumn; oldColumn: TableColumn }[],
×
1602
    ): Promise<void> {
×
1603
        for (const { oldColumn, newColumn } of changedColumns) {
×
1604
            await this.changeColumn(tableOrName, oldColumn, newColumn)
×
1605
        }
×
1606
    }
×
1607

28✔
1608
    /**
28✔
1609
     * Drops column in the table.
28✔
1610
     * @param tableOrName
28✔
1611
     * @param columnOrName
28✔
1612
     * @param ifExists
28✔
1613
     */
28✔
1614
    async dropColumn(
28✔
1615
        tableOrName: Table | string,
×
1616
        columnOrName: TableColumn | string,
×
1617
        ifExists?: boolean,
×
1618
    ): Promise<void> {
×
1619
        const table = InstanceChecker.isTable(tableOrName)
×
1620
            ? tableOrName
×
1621
            : await this.getCachedTable(tableOrName)
×
1622
        const column = InstanceChecker.isTableColumn(columnOrName)
×
1623
            ? columnOrName
×
1624
            : table.findColumnByName(columnOrName)
×
1625
        if (!column) {
×
1626
            if (ifExists) return
×
1627
            throw new TypeORMError(
×
1628
                `Column "${columnOrName}" was not found in table ${this.escapePath(
×
1629
                    table,
×
1630
                )}`,
×
1631
            )
×
1632
        }
×
1633

×
1634
        const clonedTable = table.clone()
×
1635
        const upQueries: Query[] = []
×
1636
        const downQueries: Query[] = []
×
1637

×
1638
        // drop primary key constraint
×
1639
        if (column.isPrimary) {
×
1640
            const pkName = column.primaryKeyConstraintName
×
1641
                ? column.primaryKeyConstraintName
×
1642
                : this.connection.namingStrategy.primaryKeyName(
×
1643
                      clonedTable,
×
1644
                      clonedTable.primaryColumns.map((column) => column.name),
×
1645
                  )
×
1646

×
1647
            const columnNames = clonedTable.primaryColumns
×
1648
                .map((primaryColumn) => `"${primaryColumn.name}"`)
×
1649
                .join(", ")
×
1650

×
1651
            upQueries.push(
×
1652
                new Query(
×
1653
                    `ALTER TABLE ${this.escapePath(
×
1654
                        clonedTable,
×
1655
                    )} DROP CONSTRAINT "${pkName}"`,
×
1656
                ),
×
1657
            )
×
1658
            downQueries.push(
×
1659
                new Query(
×
1660
                    `ALTER TABLE ${this.escapePath(
×
1661
                        clonedTable,
×
1662
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
×
1663
                ),
×
1664
            )
×
1665

×
1666
            // update column in table
×
1667
            const tableColumn = clonedTable.findColumnByName(column.name)
×
1668
            tableColumn!.isPrimary = false
×
1669

×
1670
            // if primary key have multiple columns, we must recreate it without dropped column
×
1671
            if (clonedTable.primaryColumns.length > 0) {
×
1672
                const pkName = clonedTable.primaryColumns[0]
×
1673
                    .primaryKeyConstraintName
×
1674
                    ? clonedTable.primaryColumns[0].primaryKeyConstraintName
×
1675
                    : this.connection.namingStrategy.primaryKeyName(
×
1676
                          clonedTable,
×
1677
                          clonedTable.primaryColumns.map(
×
1678
                              (column) => column.name,
×
1679
                          ),
×
1680
                      )
×
1681

×
1682
                const columnNames = clonedTable.primaryColumns
×
1683
                    .map((primaryColumn) => `"${primaryColumn.name}"`)
×
1684
                    .join(", ")
×
1685

×
1686
                upQueries.push(
×
1687
                    new Query(
×
1688
                        `ALTER TABLE ${this.escapePath(
×
1689
                            clonedTable,
×
1690
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
×
1691
                    ),
×
1692
                )
×
1693
                downQueries.push(
×
1694
                    new Query(
×
1695
                        `ALTER TABLE ${this.escapePath(
×
1696
                            clonedTable,
×
1697
                        )} DROP CONSTRAINT "${pkName}"`,
×
1698
                    ),
×
1699
                )
×
1700
            }
×
1701
        }
×
1702

×
1703
        // drop column index
×
1704
        const columnIndex = clonedTable.indices.find(
×
1705
            (index) =>
×
1706
                index.columnNames.length === 1 &&
×
1707
                index.columnNames[0] === column.name,
×
1708
        )
×
1709
        if (columnIndex) {
×
1710
            upQueries.push(this.dropIndexSql(columnIndex))
×
1711
            downQueries.push(this.createIndexSql(table, columnIndex))
×
1712
        }
×
1713

×
1714
        // drop column check
×
1715
        const columnCheck = clonedTable.checks.find(
×
1716
            (check) =>
×
1717
                !!check.columnNames &&
×
1718
                check.columnNames.length === 1 &&
×
1719
                check.columnNames[0] === column.name,
×
1720
        )
×
1721
        if (columnCheck) {
×
1722
            clonedTable.checks.splice(
×
1723
                clonedTable.checks.indexOf(columnCheck),
×
1724
                1,
×
1725
            )
×
1726
            upQueries.push(this.dropCheckConstraintSql(table, columnCheck))
×
1727
            downQueries.push(this.createCheckConstraintSql(table, columnCheck))
×
1728
        }
×
1729

×
1730
        // drop column unique
×
1731
        const columnUnique = clonedTable.uniques.find(
×
1732
            (unique) =>
×
1733
                unique.columnNames.length === 1 &&
×
1734
                unique.columnNames[0] === column.name,
×
1735
        )
×
1736
        if (columnUnique) {
×
1737
            clonedTable.uniques.splice(
×
1738
                clonedTable.uniques.indexOf(columnUnique),
×
1739
                1,
×
1740
            )
×
1741
            upQueries.push(this.dropUniqueConstraintSql(table, columnUnique))
×
1742
            downQueries.push(
×
1743
                this.createUniqueConstraintSql(table, columnUnique),
×
1744
            )
×
1745
        }
×
1746

×
1747
        upQueries.push(
×
1748
            new Query(
×
1749
                `ALTER TABLE ${this.escapePath(table)} DROP COLUMN "${
×
1750
                    column.name
×
1751
                }"`,
×
1752
            ),
×
1753
        )
×
1754
        downQueries.push(
×
1755
            new Query(
×
1756
                `ALTER TABLE ${this.escapePath(
×
1757
                    table,
×
1758
                )} ADD ${this.buildCreateColumnSql(column)}`,
×
1759
            ),
×
1760
        )
×
1761

×
1762
        if (column.generatedType && column.asExpression) {
×
1763
            const deleteQuery = this.deleteTypeormMetadataSql({
×
1764
                table: table.name,
×
1765
                type: MetadataTableType.GENERATED_COLUMN,
×
1766
                name: column.name,
×
1767
            })
×
1768
            const insertQuery = this.insertTypeormMetadataSql({
×
1769
                table: table.name,
×
1770
                type: MetadataTableType.GENERATED_COLUMN,
×
1771
                name: column.name,
×
1772
                value: column.asExpression,
×
1773
            })
×
1774

×
1775
            upQueries.push(deleteQuery)
×
1776
            downQueries.push(insertQuery)
×
1777
        }
×
1778

×
1779
        await this.executeQueries(upQueries, downQueries)
×
1780

×
1781
        clonedTable.removeColumn(column)
×
1782
        this.replaceCachedTable(table, clonedTable)
×
1783
    }
×
1784

28✔
1785
    /**
28✔
1786
     * Drops the columns in the table.
28✔
1787
     * @param tableOrName
28✔
1788
     * @param columns
28✔
1789
     * @param ifExists
28✔
1790
     */
28✔
1791
    async dropColumns(
28✔
1792
        tableOrName: Table | string,
×
1793
        columns: TableColumn[] | string[],
×
1794
        ifExists?: boolean,
×
1795
    ): Promise<void> {
×
1796
        for (const column of [...columns]) {
×
1797
            await this.dropColumn(tableOrName, column, ifExists)
×
1798
        }
×
1799
    }
×
1800

28✔
1801
    /**
28✔
1802
     * Creates a new primary key.
28✔
1803
     * @param tableOrName
28✔
1804
     * @param columnNames
28✔
1805
     * @param constraintName
28✔
1806
     */
28✔
1807
    async createPrimaryKey(
28✔
1808
        tableOrName: Table | string,
×
1809
        columnNames: string[],
×
1810
        constraintName?: string,
×
1811
    ): Promise<void> {
×
1812
        const table = InstanceChecker.isTable(tableOrName)
×
1813
            ? tableOrName
×
1814
            : await this.getCachedTable(tableOrName)
×
1815
        const clonedTable = table.clone()
×
1816

×
1817
        const up = this.createPrimaryKeySql(table, columnNames, constraintName)
×
1818

×
1819
        // mark columns as primary, because dropPrimaryKeySql build constraint name from table primary column names.
×
1820
        clonedTable.columns.forEach((column) => {
×
1821
            if (columnNames.find((columnName) => columnName === column.name))
×
1822
                column.isPrimary = true
×
1823
        })
×
1824
        const down = this.dropPrimaryKeySql(clonedTable)
×
1825

×
1826
        await this.executeQueries(up, down)
×
1827
        this.replaceCachedTable(table, clonedTable)
×
1828
    }
×
1829

28✔
1830
    /**
28✔
1831
     * Updates composite primary keys.
28✔
1832
     * @param tableOrName
28✔
1833
     * @param columns
28✔
1834
     */
28✔
1835
    async updatePrimaryKeys(
28✔
1836
        tableOrName: Table | string,
×
1837
        columns: TableColumn[],
×
1838
    ): Promise<void> {
×
1839
        const table = InstanceChecker.isTable(tableOrName)
×
1840
            ? tableOrName
×
1841
            : await this.getCachedTable(tableOrName)
×
1842
        const columnNames = columns.map((column) => column.name)
×
1843
        const clonedTable = table.clone()
×
1844
        const upQueries: Query[] = []
×
1845
        const downQueries: Query[] = []
×
1846

×
1847
        // if table already have primary columns, we must drop them.
×
1848
        const primaryColumns = clonedTable.primaryColumns
×
1849
        if (primaryColumns.length > 0) {
×
1850
            const pkName = primaryColumns[0].primaryKeyConstraintName
×
1851
                ? primaryColumns[0].primaryKeyConstraintName
×
1852
                : this.connection.namingStrategy.primaryKeyName(
×
1853
                      clonedTable,
×
1854
                      primaryColumns.map((column) => column.name),
×
1855
                  )
×
1856

×
1857
            const columnNamesString = primaryColumns
×
1858
                .map((column) => `"${column.name}"`)
×
1859
                .join(", ")
×
1860

×
1861
            upQueries.push(
×
1862
                new Query(
×
1863
                    `ALTER TABLE ${this.escapePath(
×
1864
                        table,
×
1865
                    )} DROP CONSTRAINT "${pkName}"`,
×
1866
                ),
×
1867
            )
×
1868
            downQueries.push(
×
1869
                new Query(
×
1870
                    `ALTER TABLE ${this.escapePath(
×
1871
                        table,
×
1872
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
×
1873
                ),
×
1874
            )
×
1875
        }
×
1876

×
1877
        // update columns in table.
×
1878
        clonedTable.columns
×
1879
            .filter((column) => columnNames.indexOf(column.name) !== -1)
×
1880
            .forEach((column) => {
×
1881
                column.isPrimary = true
×
1882
            })
×
1883

×
1884
        const pkName = primaryColumns[0].primaryKeyConstraintName
×
1885
            ? primaryColumns[0].primaryKeyConstraintName
×
1886
            : this.connection.namingStrategy.primaryKeyName(
×
1887
                  clonedTable,
×
1888
                  columnNames,
×
1889
              )
×
1890

×
1891
        const columnNamesString = columnNames
×
1892
            .map((columnName) => `"${columnName}"`)
×
1893
            .join(", ")
×
1894
        upQueries.push(
×
1895
            new Query(
×
1896
                `ALTER TABLE ${this.escapePath(
×
1897
                    table,
×
1898
                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
×
1899
            ),
×
1900
        )
×
1901
        downQueries.push(
×
1902
            new Query(
×
1903
                `ALTER TABLE ${this.escapePath(
×
1904
                    table,
×
1905
                )} DROP CONSTRAINT "${pkName}"`,
×
1906
            ),
×
1907
        )
×
1908

×
1909
        await this.executeQueries(upQueries, downQueries)
×
1910
        this.replaceCachedTable(table, clonedTable)
×
1911
    }
×
1912

28✔
1913
    /**
28✔
1914
     * Drops a primary key.
28✔
1915
     * @param tableOrName
28✔
1916
     * @param constraintName
28✔
1917
     * @param ifExists
28✔
1918
     */
28✔
1919
    async dropPrimaryKey(
28✔
1920
        tableOrName: Table | string,
×
1921
        constraintName?: string,
×
1922
        ifExists?: boolean,
×
1923
    ): Promise<void> {
×
1924
        const table = InstanceChecker.isTable(tableOrName)
×
1925
            ? tableOrName
×
1926
            : await this.getCachedTable(tableOrName)
×
1927
        if (ifExists && table.primaryColumns.length === 0) return
×
1928

×
1929
        const up = this.dropPrimaryKeySql(table)
×
1930
        const down = this.createPrimaryKeySql(
×
1931
            table,
×
1932
            table.primaryColumns.map((column) => column.name),
×
1933
            constraintName,
×
1934
        )
×
1935
        await this.executeQueries(up, down)
×
1936
        table.primaryColumns.forEach((column) => {
×
1937
            column.isPrimary = false
×
1938
        })
×
1939
    }
×
1940

28✔
1941
    /**
28✔
1942
     * Creates a new unique constraint.
28✔
1943
     * @param tableOrName
28✔
1944
     * @param uniqueConstraint
28✔
1945
     */
28✔
1946
    async createUniqueConstraint(
28✔
1947
        tableOrName: Table | string,
×
1948
        uniqueConstraint: TableUnique,
×
1949
    ): Promise<void> {
×
1950
        const table = InstanceChecker.isTable(tableOrName)
×
1951
            ? tableOrName
×
1952
            : await this.getCachedTable(tableOrName)
×
1953

×
1954
        // new unique constraint may be passed without name. In this case we generate unique name manually.
×
1955
        if (!uniqueConstraint.name)
×
1956
            uniqueConstraint.name =
×
1957
                this.connection.namingStrategy.uniqueConstraintName(
×
1958
                    table,
×
1959
                    uniqueConstraint.columnNames,
×
1960
                )
×
1961

×
1962
        const up = this.createUniqueConstraintSql(table, uniqueConstraint)
×
1963
        const down = this.dropUniqueConstraintSql(table, uniqueConstraint)
×
1964
        await this.executeQueries(up, down)
×
1965
        table.addUniqueConstraint(uniqueConstraint)
×
1966
    }
×
1967

28✔
1968
    /**
28✔
1969
     * Creates a new unique constraints.
28✔
1970
     * @param tableOrName
28✔
1971
     * @param uniqueConstraints
28✔
1972
     */
28✔
1973
    async createUniqueConstraints(
28✔
1974
        tableOrName: Table | string,
×
1975
        uniqueConstraints: TableUnique[],
×
1976
    ): Promise<void> {
×
1977
        const promises = uniqueConstraints.map((uniqueConstraint) =>
×
1978
            this.createUniqueConstraint(tableOrName, uniqueConstraint),
×
1979
        )
×
1980
        await Promise.all(promises)
×
1981
    }
×
1982

28✔
1983
    /**
28✔
1984
     * Drops a unique constraint.
28✔
1985
     * @param tableOrName
28✔
1986
     * @param uniqueOrName
28✔
1987
     * @param ifExists
28✔
1988
     */
28✔
1989
    async dropUniqueConstraint(
28✔
1990
        tableOrName: Table | string,
×
1991
        uniqueOrName: TableUnique | string,
×
1992
        ifExists?: boolean,
×
1993
    ): Promise<void> {
×
1994
        const table = InstanceChecker.isTable(tableOrName)
×
1995
            ? tableOrName
×
1996
            : await this.getCachedTable(tableOrName)
×
1997
        const uniqueConstraint = InstanceChecker.isTableUnique(uniqueOrName)
×
1998
            ? uniqueOrName
×
1999
            : table.uniques.find((u) => u.name === uniqueOrName)
×
2000
        if (!uniqueConstraint) {
×
2001
            if (ifExists) return
×
2002
            throw new TypeORMError(
×
2003
                `Supplied unique constraint was not found in table ${table.name}`,
×
2004
            )
×
2005
        }
×
2006

×
2007
        const up = this.dropUniqueConstraintSql(table, uniqueConstraint)
×
2008
        const down = this.createUniqueConstraintSql(table, uniqueConstraint)
×
2009
        await this.executeQueries(up, down)
×
2010
        table.removeUniqueConstraint(uniqueConstraint)
×
2011
    }
×
2012

28✔
2013
    /**
28✔
2014
     * Drops unique constraints.
28✔
2015
     * @param tableOrName
28✔
2016
     * @param uniqueConstraints
28✔
2017
     * @param ifExists
28✔
2018
     */
28✔
2019
    async dropUniqueConstraints(
28✔
2020
        tableOrName: Table | string,
×
2021
        uniqueConstraints: TableUnique[],
×
2022
        ifExists?: boolean,
×
2023
    ): Promise<void> {
×
2024
        const promises = uniqueConstraints.map((uniqueConstraint) =>
×
2025
            this.dropUniqueConstraint(tableOrName, uniqueConstraint, ifExists),
×
2026
        )
×
2027
        await Promise.all(promises)
×
2028
    }
×
2029

28✔
2030
    /**
28✔
2031
     * Creates new check constraint.
28✔
2032
     * @param tableOrName
28✔
2033
     * @param checkConstraint
28✔
2034
     */
28✔
2035
    async createCheckConstraint(
28✔
2036
        tableOrName: Table | string,
×
2037
        checkConstraint: TableCheck,
×
2038
    ): Promise<void> {
×
2039
        const table = InstanceChecker.isTable(tableOrName)
×
2040
            ? tableOrName
×
2041
            : await this.getCachedTable(tableOrName)
×
2042

×
2043
        // new unique constraint may be passed without name. In this case we generate unique name manually.
×
2044
        if (!checkConstraint.name)
×
2045
            checkConstraint.name =
×
2046
                this.connection.namingStrategy.checkConstraintName(
×
2047
                    table,
×
2048
                    checkConstraint.expression!,
×
2049
                )
×
2050

×
2051
        const up = this.createCheckConstraintSql(table, checkConstraint)
×
2052
        const down = this.dropCheckConstraintSql(table, checkConstraint)
×
2053
        await this.executeQueries(up, down)
×
2054
        table.addCheckConstraint(checkConstraint)
×
2055
    }
×
2056

28✔
2057
    /**
28✔
2058
     * Creates new check constraints.
28✔
2059
     * @param tableOrName
28✔
2060
     * @param checkConstraints
28✔
2061
     */
28✔
2062
    async createCheckConstraints(
28✔
2063
        tableOrName: Table | string,
×
2064
        checkConstraints: TableCheck[],
×
2065
    ): Promise<void> {
×
2066
        const promises = checkConstraints.map((checkConstraint) =>
×
2067
            this.createCheckConstraint(tableOrName, checkConstraint),
×
2068
        )
×
2069
        await Promise.all(promises)
×
2070
    }
×
2071

28✔
2072
    /**
28✔
2073
     * Drops check constraint.
28✔
2074
     * @param tableOrName
28✔
2075
     * @param checkOrName
28✔
2076
     * @param ifExists
28✔
2077
     */
28✔
2078
    async dropCheckConstraint(
28✔
2079
        tableOrName: Table | string,
×
2080
        checkOrName: TableCheck | string,
×
2081
        ifExists?: boolean,
×
2082
    ): Promise<void> {
×
2083
        const table = InstanceChecker.isTable(tableOrName)
×
2084
            ? tableOrName
×
2085
            : await this.getCachedTable(tableOrName)
×
2086
        const checkConstraint = InstanceChecker.isTableCheck(checkOrName)
×
2087
            ? checkOrName
×
2088
            : table.checks.find((c) => c.name === checkOrName)
×
2089
        if (!checkConstraint) {
×
2090
            if (ifExists) return
×
2091
            throw new TypeORMError(
×
2092
                `Supplied check constraint was not found in table ${table.name}`,
×
2093
            )
×
2094
        }
×
2095

×
2096
        const up = this.dropCheckConstraintSql(table, checkConstraint)
×
2097
        const down = this.createCheckConstraintSql(table, checkConstraint)
×
2098
        await this.executeQueries(up, down)
×
2099
        table.removeCheckConstraint(checkConstraint)
×
2100
    }
×
2101

28✔
2102
    /**
28✔
2103
     * Drops check constraints.
28✔
2104
     * @param tableOrName
28✔
2105
     * @param checkConstraints
28✔
2106
     * @param ifExists
28✔
2107
     */
28✔
2108
    async dropCheckConstraints(
28✔
2109
        tableOrName: Table | string,
×
2110
        checkConstraints: TableCheck[],
×
2111
        ifExists?: boolean,
×
2112
    ): Promise<void> {
×
2113
        const promises = checkConstraints.map((checkConstraint) =>
×
2114
            this.dropCheckConstraint(tableOrName, checkConstraint, ifExists),
×
2115
        )
×
2116
        await Promise.all(promises)
×
2117
    }
×
2118

28✔
2119
    /**
28✔
2120
     * Creates a new exclusion constraint.
28✔
2121
     * @param tableOrName
28✔
2122
     * @param exclusionConstraint
28✔
2123
     */
28✔
2124
    async createExclusionConstraint(
28✔
2125
        tableOrName: Table | string,
×
2126
        exclusionConstraint: TableExclusion,
×
2127
    ): Promise<void> {
×
2128
        throw new TypeORMError(`Oracle does not support exclusion constraints.`)
×
2129
    }
×
2130

28✔
2131
    /**
28✔
2132
     * Creates a new exclusion constraints.
28✔
2133
     * @param tableOrName
28✔
2134
     * @param exclusionConstraints
28✔
2135
     */
28✔
2136
    async createExclusionConstraints(
28✔
2137
        tableOrName: Table | string,
×
2138
        exclusionConstraints: TableExclusion[],
×
2139
    ): Promise<void> {
×
2140
        throw new TypeORMError(`Oracle does not support exclusion constraints.`)
×
2141
    }
×
2142

28✔
2143
    /**
28✔
2144
     * Drops exclusion constraint.
28✔
2145
     * @param tableOrName
28✔
2146
     * @param exclusionOrName
28✔
2147
     * @param ifExists
28✔
2148
     */
28✔
2149
    async dropExclusionConstraint(
28✔
2150
        tableOrName: Table | string,
×
2151
        exclusionOrName: TableExclusion | string,
×
2152
        ifExists?: boolean,
×
2153
    ): Promise<void> {
×
2154
        throw new TypeORMError(`Oracle does not support exclusion constraints.`)
×
2155
    }
×
2156

28✔
2157
    /**
28✔
2158
     * Drops exclusion constraints.
28✔
2159
     * @param tableOrName
28✔
2160
     * @param exclusionConstraints
28✔
2161
     * @param ifExists
28✔
2162
     */
28✔
2163
    async dropExclusionConstraints(
28✔
2164
        tableOrName: Table | string,
×
2165
        exclusionConstraints: TableExclusion[],
×
2166
        ifExists?: boolean,
×
2167
    ): Promise<void> {
×
2168
        throw new TypeORMError(`Oracle does not support exclusion constraints.`)
×
2169
    }
×
2170

28✔
2171
    /**
28✔
2172
     * Creates a new foreign key.
28✔
2173
     * @param tableOrName
28✔
2174
     * @param foreignKey
28✔
2175
     */
28✔
2176
    async createForeignKey(
28✔
2177
        tableOrName: Table | string,
12✔
2178
        foreignKey: TableForeignKey,
12✔
2179
    ): Promise<void> {
12✔
2180
        const table = InstanceChecker.isTable(tableOrName)
12✔
2181
            ? tableOrName
12✔
2182
            : await this.getCachedTable(tableOrName)
12!
2183

×
2184
        // new FK may be passed without name. In this case we generate FK name manually.
×
2185
        if (!foreignKey.name)
×
2186
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
×
2187
                table,
×
2188
                foreignKey.columnNames,
×
2189
                this.getTablePath(foreignKey),
×
2190
                foreignKey.referencedColumnNames,
×
2191
            )
×
2192

12✔
2193
        const up = this.createForeignKeySql(table, foreignKey)
12✔
2194
        const down = this.dropForeignKeySql(table, foreignKey)
12✔
2195
        await this.executeQueries(up, down)
12✔
2196
        table.addForeignKey(foreignKey)
12✔
2197
    }
12✔
2198

28✔
2199
    /**
28✔
2200
     * Creates a new foreign keys.
28✔
2201
     * @param tableOrName
28✔
2202
     * @param foreignKeys
28✔
2203
     */
28✔
2204
    async createForeignKeys(
28✔
2205
        tableOrName: Table | string,
8✔
2206
        foreignKeys: TableForeignKey[],
8✔
2207
    ): Promise<void> {
8✔
2208
        const promises = foreignKeys.map((foreignKey) =>
8✔
2209
            this.createForeignKey(tableOrName, foreignKey),
8✔
2210
        )
8✔
2211
        await Promise.all(promises)
8✔
2212
    }
8✔
2213

28✔
2214
    /**
28✔
2215
     * Drops a foreign key from the table.
28✔
2216
     * @param tableOrName
28✔
2217
     * @param foreignKeyOrName
28✔
2218
     * @param ifExists
28✔
2219
     */
28✔
2220
    async dropForeignKey(
28✔
2221
        tableOrName: Table | string,
×
2222
        foreignKeyOrName: TableForeignKey | string,
×
2223
        ifExists?: boolean,
×
2224
    ): Promise<void> {
×
2225
        const table = InstanceChecker.isTable(tableOrName)
×
2226
            ? tableOrName
×
2227
            : await this.getCachedTable(tableOrName)
×
2228
        const foreignKey = InstanceChecker.isTableForeignKey(foreignKeyOrName)
×
2229
            ? foreignKeyOrName
×
2230
            : table.foreignKeys.find((fk) => fk.name === foreignKeyOrName)
×
2231
        if (!foreignKey) {
×
2232
            if (ifExists) return
×
2233
            throw new TypeORMError(
×
2234
                `Supplied foreign key was not found in table ${table.name}`,
×
2235
            )
×
2236
        }
×
2237

×
2238
        if (!foreignKey.name) {
×
2239
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
×
2240
                table,
×
2241
                foreignKey.columnNames,
×
2242
                this.getTablePath(foreignKey),
×
2243
                foreignKey.referencedColumnNames,
×
2244
            )
×
2245
        }
×
2246

×
2247
        const up = this.dropForeignKeySql(table, foreignKey)
×
2248
        const down = this.createForeignKeySql(table, foreignKey)
×
2249
        await this.executeQueries(up, down)
×
2250
        table.removeForeignKey(foreignKey)
×
2251
    }
×
2252

28✔
2253
    /**
28✔
2254
     * Drops a foreign keys from the table.
28✔
2255
     * @param tableOrName
28✔
2256
     * @param foreignKeys
28✔
2257
     * @param ifExists
28✔
2258
     */
28✔
2259
    async dropForeignKeys(
28✔
2260
        tableOrName: Table | string,
×
2261
        foreignKeys: TableForeignKey[],
×
2262
        ifExists?: boolean,
×
2263
    ): Promise<void> {
×
2264
        const promises = foreignKeys.map((foreignKey) =>
×
2265
            this.dropForeignKey(tableOrName, foreignKey, ifExists),
×
2266
        )
×
2267
        await Promise.all(promises)
×
2268
    }
×
2269

28✔
2270
    /**
28✔
2271
     * Creates a new index.
28✔
2272
     * @param tableOrName
28✔
2273
     * @param index
28✔
2274
     */
28✔
2275
    async createIndex(
28✔
2276
        tableOrName: Table | string,
×
2277
        index: TableIndex,
×
2278
    ): Promise<void> {
×
2279
        const table = InstanceChecker.isTable(tableOrName)
×
2280
            ? tableOrName
×
2281
            : await this.getCachedTable(tableOrName)
×
2282

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

×
2286
        const up = this.createIndexSql(table, index)
×
2287
        const down = this.dropIndexSql(index)
×
2288
        await this.executeQueries(up, down)
×
2289
        table.addIndex(index)
×
2290
    }
×
2291

28✔
2292
    /**
28✔
2293
     * Creates a new indices
28✔
2294
     * @param tableOrName
28✔
2295
     * @param indices
28✔
2296
     */
28✔
2297
    async createIndices(
28✔
2298
        tableOrName: Table | string,
×
2299
        indices: TableIndex[],
×
2300
    ): Promise<void> {
×
2301
        const promises = indices.map((index) =>
×
2302
            this.createIndex(tableOrName, index),
×
2303
        )
×
2304
        await Promise.all(promises)
×
2305
    }
×
2306

28✔
2307
    /**
28✔
2308
     * Drops an index from the table.
28✔
2309
     * @param tableOrName
28✔
2310
     * @param indexOrName
28✔
2311
     * @param ifExists
28✔
2312
     */
28✔
2313
    async dropIndex(
28✔
2314
        tableOrName: Table | string,
×
2315
        indexOrName: TableIndex | string,
×
2316
        ifExists?: boolean,
×
2317
    ): Promise<void> {
×
2318
        const table = InstanceChecker.isTable(tableOrName)
×
2319
            ? tableOrName
×
2320
            : await this.getCachedTable(tableOrName)
×
2321
        const index = InstanceChecker.isTableIndex(indexOrName)
×
2322
            ? indexOrName
×
2323
            : table.indices.find((i) => i.name === indexOrName)
×
2324
        if (!index) {
×
2325
            if (ifExists) return
×
2326
            throw new TypeORMError(
×
2327
                `Supplied index ${indexOrName} was not found in table ${table.name}`,
×
2328
            )
×
2329
        }
×
2330
        // old index may be passed without name. In this case we generate index name manually.
×
2331
        if (!index.name) index.name = this.generateIndexName(table, index)
×
2332

×
2333
        const up = this.dropIndexSql(index)
×
2334
        const down = this.createIndexSql(table, index)
×
2335
        await this.executeQueries(up, down)
×
2336
        table.removeIndex(index)
×
2337
    }
×
2338

28✔
2339
    /**
28✔
2340
     * Drops an indices from the table.
28✔
2341
     * @param tableOrName
28✔
2342
     * @param indices
28✔
2343
     * @param ifExists
28✔
2344
     */
28✔
2345
    async dropIndices(
28✔
2346
        tableOrName: Table | string,
×
2347
        indices: TableIndex[],
×
2348
        ifExists?: boolean,
×
2349
    ): Promise<void> {
×
2350
        const promises = indices.map((index) =>
×
2351
            this.dropIndex(tableOrName, index, ifExists),
×
2352
        )
×
2353
        await Promise.all(promises)
×
2354
    }
×
2355

28✔
2356
    /**
28✔
2357
     * Clears all table contents.
28✔
2358
     * Note: this operation uses SQL's TRUNCATE query which cannot be reverted in transactions.
28✔
2359
     * @param tableName
28✔
2360
     * @param options
28✔
2361
     * @param options.cascade
28✔
2362
     */
28✔
2363
    async clearTable(
28✔
2364
        tableName: string,
×
2365
        options?: { cascade?: boolean },
×
2366
    ): Promise<void> {
×
2367
        const cascade = options?.cascade ? " CASCADE" : ""
×
2368
        await this.query(
×
2369
            `TRUNCATE TABLE ${this.escapePath(tableName)}${cascade}`,
×
2370
        )
×
2371
    }
×
2372

28✔
2373
    /**
28✔
2374
     * Removes all tables from the currently connected database.
28✔
2375
     */
28✔
2376
    async clearDatabase(): Promise<void> {
28✔
2377
        const isAnotherTransactionActive = this.isTransactionActive
4✔
2378
        if (!isAnotherTransactionActive) await this.startTransaction()
4✔
2379
        try {
4✔
2380
            // drop views
4✔
2381
            const dropViewsQuery = `SELECT 'DROP VIEW "' || VIEW_NAME || '"' AS "query" FROM "USER_VIEWS"`
4✔
2382
            const dropViewQueries: ObjectLiteral[] =
4✔
2383
                await this.query(dropViewsQuery)
4✔
2384
            await Promise.all(
4✔
2385
                dropViewQueries.map((query) => this.query(query["query"])),
4✔
2386
            )
4✔
2387

4✔
2388
            // drop materialized views
4✔
2389
            const dropMatViewsQuery = `SELECT 'DROP MATERIALIZED VIEW "' || MVIEW_NAME || '"' AS "query" FROM "USER_MVIEWS"`
4✔
2390
            const dropMatViewQueries: ObjectLiteral[] =
4✔
2391
                await this.query(dropMatViewsQuery)
4✔
2392
            await Promise.all(
4✔
2393
                dropMatViewQueries.map((query) => this.query(query["query"])),
4✔
2394
            )
4✔
2395

4✔
2396
            // drop tables
4✔
2397
            const dropTablesQuery = `SELECT 'DROP TABLE "' || TABLE_NAME || '" CASCADE CONSTRAINTS' AS "query" FROM "USER_TABLES"`
4✔
2398
            const dropTableQueries: ObjectLiteral[] =
4✔
2399
                await this.query(dropTablesQuery)
4✔
2400
            await Promise.all(
4✔
2401
                dropTableQueries.map((query) => this.query(query["query"])),
4✔
2402
            )
4✔
2403
            if (!isAnotherTransactionActive) await this.commitTransaction()
4✔
2404
        } catch (error) {
4!
2405
            try {
×
2406
                // we throw original error even if rollback thrown an error
×
2407
                if (!isAnotherTransactionActive)
×
2408
                    await this.rollbackTransaction()
×
2409
            } catch (rollbackError) {}
×
2410
            throw error
×
2411
        }
×
2412
    }
4✔
2413

28✔
2414
    // -------------------------------------------------------------------------
28✔
2415
    // Protected Methods
28✔
2416
    // -------------------------------------------------------------------------
28✔
2417

28✔
2418
    protected async loadViews(viewNames?: string[]): Promise<View[]> {
28✔
2419
        const hasTable = await this.hasTable(this.getTypeormMetadataTableName())
4✔
2420
        if (!hasTable) {
4✔
2421
            return []
4✔
2422
        }
4✔
2423

×
2424
        if (!viewNames) {
×
2425
            viewNames = []
×
2426
        }
×
2427

×
2428
        const currentDatabase = await this.getCurrentDatabase()
×
2429
        const currentSchema = await this.getCurrentSchema()
×
2430

×
2431
        const viewsCondition = viewNames
×
2432
            .map((viewName) => this.driver.parseTableName(viewName))
×
2433
            .map(({ schema, tableName }) => {
×
2434
                if (!schema) {
×
2435
                    schema = this.driver.options.schema || currentSchema
×
2436
                }
×
2437

×
2438
                return `("T"."schema" = '${schema}' AND "T"."name" = '${tableName}')`
×
2439
            })
×
2440
            .join(" OR ")
×
2441

×
2442
        let query =
×
2443
            `SELECT "T".* FROM ${this.escapePath(
×
2444
                this.getTypeormMetadataTableName(),
×
2445
            )} "T" ` +
×
2446
            `INNER JOIN "USER_OBJECTS" "O" ON "O"."OBJECT_NAME" = "T"."name" AND "O"."OBJECT_TYPE" IN ( 'MATERIALIZED VIEW', 'VIEW' ) ` +
×
2447
            `WHERE "T"."type" IN ('${MetadataTableType.MATERIALIZED_VIEW}', '${MetadataTableType.VIEW}')`
×
2448
        if (viewsCondition.length > 0) query += ` AND ${viewsCondition}`
×
2449

×
2450
        const dbViews = await this.query(query)
×
2451
        return dbViews.map((dbView: any) => {
×
2452
            const parsedName = this.driver.parseTableName(dbView["name"])
×
2453

×
2454
            const view = new View()
×
2455
            view.database =
×
2456
                parsedName.database || dbView["database"] || currentDatabase
×
2457
            view.schema = parsedName.schema || dbView["schema"] || currentSchema
×
2458
            view.name = parsedName.tableName
×
2459
            view.expression = dbView["value"]
×
2460
            view.materialized =
×
2461
                dbView["type"] === MetadataTableType.MATERIALIZED_VIEW
×
2462
            return view
×
2463
        })
×
2464
    }
×
2465

28✔
2466
    /**
28✔
2467
     * Loads all tables (with given names) from the database and creates a Table from them.
28✔
2468
     * @param tableNames
28✔
2469
     */
28✔
2470
    protected async loadTables(tableNames?: string[]): Promise<Table[]> {
28✔
2471
        if (tableNames && tableNames.length === 0) {
4!
2472
            return []
×
2473
        }
×
2474

4✔
2475
        const dbTables: { TABLE_NAME: string; OWNER: string }[] = []
4✔
2476

4✔
2477
        const currentSchema = await this.getCurrentSchema()
4✔
2478
        const currentDatabase = await this.getCurrentDatabase()
4✔
2479

4✔
2480
        if (!tableNames) {
4!
2481
            const tablesSql = `SELECT "TABLE_NAME", "OWNER" FROM "ALL_TABLES"`
×
2482
            dbTables.push(...(await this.query(tablesSql)))
×
2483
        } else {
4✔
2484
            const tablesCondition = tableNames
4✔
2485
                .map((tableName) => {
4✔
2486
                    const parts = tableName.split(".")
12✔
2487

12✔
2488
                    if (parts.length >= 3) {
12!
2489
                        const [, schema, name] = parts
×
2490
                        return `("OWNER" = '${schema}' AND "TABLE_NAME" = '${name}')`
×
2491
                    } else if (parts.length === 2) {
12✔
2492
                        const [schema, name] = parts
12✔
2493
                        return `("OWNER" = '${schema}' AND "TABLE_NAME" = '${name}')`
12✔
2494
                    } else if (parts.length === 1) {
12!
2495
                        const [name] = parts
×
2496
                        return `("TABLE_NAME" = '${name}')`
×
2497
                    } else {
×
2498
                        return `(1=0)`
×
2499
                    }
×
2500
                })
4✔
2501
                .join(" OR ")
4✔
2502
            const tablesSql = `SELECT "TABLE_NAME", "OWNER" FROM "ALL_TABLES" WHERE ${tablesCondition}`
4✔
2503
            dbTables.push(...(await this.query(tablesSql)))
4✔
2504
        }
4✔
2505

4✔
2506
        // if tables were not found in the db, no need to proceed
4✔
2507
        if (dbTables.length === 0) {
4✔
2508
            return []
4✔
2509
        }
4✔
2510

×
2511
        // load tables, columns, indices and foreign keys
×
2512
        const columnsCondition = dbTables
×
2513
            .map(({ TABLE_NAME, OWNER }) => {
×
2514
                return `("C"."OWNER" = '${OWNER}' AND "C"."TABLE_NAME" = '${TABLE_NAME}')`
×
2515
            })
×
2516
            .join(" OR ")
×
2517
        const columnsSql = `SELECT * FROM "ALL_TAB_COLS" "C" WHERE (${columnsCondition})`
×
2518

×
2519
        const indicesSql =
×
2520
            `SELECT "C"."INDEX_NAME", "C"."OWNER", "C"."TABLE_NAME", "C"."UNIQUENESS", ` +
×
2521
            `LISTAGG ("COL"."COLUMN_NAME", ',') WITHIN GROUP (ORDER BY "COL"."COLUMN_NAME") AS "COLUMN_NAMES" ` +
×
2522
            `FROM "ALL_INDEXES" "C" ` +
×
2523
            `INNER JOIN "ALL_IND_COLUMNS" "COL" ON "COL"."INDEX_OWNER" = "C"."OWNER" AND "COL"."INDEX_NAME" = "C"."INDEX_NAME" ` +
×
2524
            `LEFT JOIN "ALL_CONSTRAINTS" "CON" ON "CON"."OWNER" = "C"."OWNER" AND "CON"."CONSTRAINT_NAME" = "C"."INDEX_NAME" ` +
×
2525
            `WHERE (${columnsCondition}) AND "CON"."CONSTRAINT_NAME" IS NULL ` +
×
2526
            `GROUP BY "C"."INDEX_NAME", "C"."OWNER", "C"."TABLE_NAME", "C"."UNIQUENESS"`
×
2527

×
2528
        const foreignKeysSql =
×
2529
            `SELECT "C"."CONSTRAINT_NAME", "C"."OWNER", "C"."TABLE_NAME", "COL"."COLUMN_NAME", "REF_COL"."TABLE_NAME" AS "REFERENCED_TABLE_NAME", ` +
×
2530
            `"REF_COL"."COLUMN_NAME" AS "REFERENCED_COLUMN_NAME", "C"."DELETE_RULE" AS "ON_DELETE" ` +
×
2531
            `FROM "ALL_CONSTRAINTS" "C" ` +
×
2532
            `INNER JOIN "ALL_CONS_COLUMNS" "COL" ON "COL"."OWNER" = "C"."OWNER" AND "COL"."CONSTRAINT_NAME" = "C"."CONSTRAINT_NAME" ` +
×
2533
            `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" ` +
×
2534
            `WHERE (${columnsCondition}) AND "C"."CONSTRAINT_TYPE" = 'R'`
×
2535

×
2536
        const constraintsSql =
×
2537
            `SELECT "C"."CONSTRAINT_NAME", "C"."CONSTRAINT_TYPE", "C"."OWNER", "C"."TABLE_NAME", "COL"."COLUMN_NAME", "C"."SEARCH_CONDITION" ` +
×
2538
            `FROM "ALL_CONSTRAINTS" "C" ` +
×
2539
            `INNER JOIN "ALL_CONS_COLUMNS" "COL" ON "COL"."OWNER" = "C"."OWNER" AND "COL"."CONSTRAINT_NAME" = "C"."CONSTRAINT_NAME" ` +
×
2540
            `WHERE (${columnsCondition}) AND "C"."CONSTRAINT_TYPE" IN ('C', 'U', 'P') AND "C"."GENERATED" = 'USER NAME'`
×
2541

×
2542
        const [
×
2543
            dbColumns,
×
2544
            dbIndices,
×
2545
            dbForeignKeys,
×
2546
            dbConstraints,
×
2547
        ]: ObjectLiteral[][] = await Promise.all([
×
2548
            this.query(columnsSql),
×
2549
            this.query(indicesSql),
×
2550
            this.query(foreignKeysSql),
×
2551
            this.query(constraintsSql),
×
2552
        ])
×
2553

×
2554
        // create tables for loaded tables
×
2555
        return await Promise.all(
×
2556
            dbTables.map(async (dbTable) => {
×
2557
                const table = new Table()
×
2558
                const owner =
×
2559
                    dbTable["OWNER"] === currentSchema &&
×
2560
                    (!this.driver.options.schema ||
×
2561
                        this.driver.options.schema === currentSchema)
×
2562
                        ? undefined
×
2563
                        : dbTable["OWNER"]
×
2564
                table.database = currentDatabase
×
2565
                table.schema = dbTable["OWNER"]
×
2566
                table.name = this.driver.buildTableName(
×
2567
                    dbTable["TABLE_NAME"],
×
2568
                    owner,
×
2569
                )
×
2570

×
2571
                // create columns from the loaded columns
×
2572
                table.columns = await Promise.all(
×
2573
                    dbColumns
×
2574
                        .filter(
×
2575
                            (dbColumn) =>
×
2576
                                dbColumn["OWNER"] === dbTable["OWNER"] &&
×
2577
                                dbColumn["TABLE_NAME"] ===
×
2578
                                    dbTable["TABLE_NAME"] &&
×
2579
                                // Filter out auto-generated virtual columns,
×
2580
                                // since TypeORM will have no info about them.
×
2581
                                !(
×
2582
                                    dbColumn["VIRTUAL_COLUMN"] === "YES" &&
×
2583
                                    dbColumn["USER_GENERATED"] === "NO"
×
2584
                                ),
×
2585
                        )
×
2586
                        .map(async (dbColumn) => {
×
2587
                            const columnConstraints = dbConstraints.filter(
×
2588
                                (dbConstraint) =>
×
2589
                                    dbConstraint["OWNER"] ===
×
2590
                                        dbColumn["OWNER"] &&
×
2591
                                    dbConstraint["TABLE_NAME"] ===
×
2592
                                        dbColumn["TABLE_NAME"] &&
×
2593
                                    dbConstraint["COLUMN_NAME"] ===
×
2594
                                        dbColumn["COLUMN_NAME"],
×
2595
                            )
×
2596

×
2597
                            const uniqueConstraints = columnConstraints.filter(
×
2598
                                (constraint) =>
×
2599
                                    constraint["CONSTRAINT_TYPE"] === "U",
×
2600
                            )
×
2601
                            const isConstraintComposite =
×
2602
                                uniqueConstraints.every((uniqueConstraint) => {
×
2603
                                    return dbConstraints.some(
×
2604
                                        (dbConstraint) =>
×
2605
                                            dbConstraint["OWNER"] ===
×
2606
                                                dbColumn["OWNER"] &&
×
2607
                                            dbConstraint["TABLE_NAME"] ===
×
2608
                                                dbColumn["TABLE_NAME"] &&
×
2609
                                            dbConstraint["COLUMN_NAME"] !==
×
2610
                                                dbColumn["COLUMN_NAME"] &&
×
2611
                                            dbConstraint["CONSTRAINT_NAME"] ===
×
2612
                                                uniqueConstraint[
×
2613
                                                    "CONSTRAINT_NAME"
×
2614
                                                ] &&
×
2615
                                            dbConstraint["CONSTRAINT_TYPE"] ===
×
2616
                                                "U",
×
2617
                                    )
×
2618
                                })
×
2619

×
2620
                            const tableColumn = new TableColumn()
×
2621
                            tableColumn.name = dbColumn["COLUMN_NAME"]
×
2622
                            tableColumn.type =
×
2623
                                dbColumn["DATA_TYPE"].toLowerCase()
×
2624
                            if (tableColumn.type.indexOf("(") !== -1)
×
2625
                                tableColumn.type = tableColumn.type.replace(
×
2626
                                    /\([0-9]*\)/,
×
2627
                                    "",
×
2628
                                )
×
2629

×
2630
                            // check only columns that have length property
×
2631
                            if (
×
2632
                                this.driver.withLengthColumnTypes.indexOf(
×
2633
                                    tableColumn.type as ColumnType,
×
2634
                                ) !== -1
×
2635
                            ) {
×
2636
                                const length =
×
2637
                                    tableColumn.type === "raw"
×
2638
                                        ? dbColumn["DATA_LENGTH"]
×
2639
                                        : dbColumn["CHAR_COL_DECL_LENGTH"]
×
2640
                                tableColumn.length =
×
2641
                                    length &&
×
2642
                                    !this.isDefaultColumnLength(
×
2643
                                        table,
×
2644
                                        tableColumn,
×
2645
                                        length,
×
2646
                                    )
×
2647
                                        ? length.toString()
×
2648
                                        : ""
×
2649
                            }
×
2650

×
2651
                            if (
×
2652
                                tableColumn.type === "number" ||
×
2653
                                tableColumn.type === "float"
×
2654
                            ) {
×
2655
                                if (
×
2656
                                    dbColumn["DATA_PRECISION"] !== null &&
×
2657
                                    !this.isDefaultColumnPrecision(
×
2658
                                        table,
×
2659
                                        tableColumn,
×
2660
                                        dbColumn["DATA_PRECISION"],
×
2661
                                    )
×
2662
                                )
×
2663
                                    tableColumn.precision =
×
2664
                                        dbColumn["DATA_PRECISION"]
×
2665
                                if (
×
2666
                                    dbColumn["DATA_SCALE"] !== null &&
×
2667
                                    !this.isDefaultColumnScale(
×
2668
                                        table,
×
2669
                                        tableColumn,
×
2670
                                        dbColumn["DATA_SCALE"],
×
2671
                                    )
×
2672
                                )
×
2673
                                    tableColumn.scale = dbColumn["DATA_SCALE"]
×
2674
                            } else if (
×
2675
                                (tableColumn.type === "timestamp" ||
×
2676
                                    tableColumn.type ===
×
2677
                                        "timestamp with time zone" ||
×
2678
                                    tableColumn.type ===
×
2679
                                        "timestamp with local time zone") &&
×
2680
                                dbColumn["DATA_SCALE"] !== null
×
2681
                            ) {
×
2682
                                tableColumn.precision =
×
2683
                                    !this.isDefaultColumnPrecision(
×
2684
                                        table,
×
2685
                                        tableColumn,
×
2686
                                        dbColumn["DATA_SCALE"],
×
2687
                                    )
×
2688
                                        ? dbColumn["DATA_SCALE"]
×
2689
                                        : undefined
×
2690
                            }
×
2691

×
2692
                            tableColumn.default =
×
2693
                                dbColumn["DATA_DEFAULT"] !== null &&
×
2694
                                dbColumn["DATA_DEFAULT"] !== undefined &&
×
2695
                                dbColumn["VIRTUAL_COLUMN"] === "NO" &&
×
2696
                                dbColumn["DATA_DEFAULT"].trim() !== "NULL"
×
2697
                                    ? (tableColumn.default =
×
2698
                                          dbColumn["DATA_DEFAULT"].trim())
×
2699
                                    : undefined
×
2700

×
2701
                            const primaryConstraint = columnConstraints.find(
×
2702
                                (constraint) =>
×
2703
                                    constraint["CONSTRAINT_TYPE"] === "P",
×
2704
                            )
×
2705
                            if (primaryConstraint) {
×
2706
                                tableColumn.isPrimary = true
×
2707
                                // find another columns involved in primary key constraint
×
2708
                                const anotherPrimaryConstraints =
×
2709
                                    dbConstraints.filter(
×
2710
                                        (constraint) =>
×
2711
                                            constraint["OWNER"] ===
×
2712
                                                dbColumn["OWNER"] &&
×
2713
                                            constraint["TABLE_NAME"] ===
×
2714
                                                dbColumn["TABLE_NAME"] &&
×
2715
                                            constraint["COLUMN_NAME"] !==
×
2716
                                                dbColumn["COLUMN_NAME"] &&
×
2717
                                            constraint["CONSTRAINT_TYPE"] ===
×
2718
                                                "P",
×
2719
                                    )
×
2720

×
2721
                                // collect all column names
×
2722
                                const columnNames =
×
2723
                                    anotherPrimaryConstraints.map(
×
2724
                                        (constraint) =>
×
2725
                                            constraint["COLUMN_NAME"],
×
2726
                                    )
×
2727
                                columnNames.push(dbColumn["COLUMN_NAME"])
×
2728

×
2729
                                // build default primary key constraint name
×
2730
                                const pkName =
×
2731
                                    this.connection.namingStrategy.primaryKeyName(
×
2732
                                        table,
×
2733
                                        columnNames,
×
2734
                                    )
×
2735

×
2736
                                // if primary key has user-defined constraint name, write it in table column
×
2737
                                if (
×
2738
                                    primaryConstraint["CONSTRAINT_NAME"] !==
×
2739
                                    pkName
×
2740
                                ) {
×
2741
                                    tableColumn.primaryKeyConstraintName =
×
2742
                                        primaryConstraint["CONSTRAINT_NAME"]
×
2743
                                }
×
2744
                            }
×
2745

×
2746
                            tableColumn.isNullable =
×
2747
                                dbColumn["NULLABLE"] === "Y"
×
2748
                            tableColumn.isUnique =
×
2749
                                uniqueConstraints.length > 0 &&
×
2750
                                !isConstraintComposite
×
2751
                            tableColumn.isGenerated =
×
2752
                                dbColumn["IDENTITY_COLUMN"] === "YES"
×
2753
                            if (tableColumn.isGenerated) {
×
2754
                                tableColumn.generationStrategy = "increment"
×
2755
                                tableColumn.default = undefined
×
2756
                            }
×
2757
                            tableColumn.comment = "" // todo
×
2758

×
2759
                            if (dbColumn["VIRTUAL_COLUMN"] === "YES") {
×
2760
                                tableColumn.generatedType = "VIRTUAL"
×
2761

×
2762
                                const asExpressionQuery =
×
2763
                                    this.selectTypeormMetadataSql({
×
2764
                                        table: dbTable["TABLE_NAME"],
×
2765
                                        type: MetadataTableType.GENERATED_COLUMN,
×
2766
                                        name: tableColumn.name,
×
2767
                                    })
×
2768

×
2769
                                const results = await this.query(
×
2770
                                    asExpressionQuery.query,
×
2771
                                    asExpressionQuery.parameters,
×
2772
                                )
×
2773
                                if (results[0] && results[0].value) {
×
2774
                                    tableColumn.asExpression = results[0].value
×
2775
                                } else {
×
2776
                                    tableColumn.asExpression = ""
×
2777
                                }
×
2778
                            }
×
2779

×
2780
                            return tableColumn
×
2781
                        }),
×
2782
                )
×
2783

×
2784
                // find unique constraints of table, group them by constraint name and build TableUnique.
×
2785
                const tableUniqueConstraints = OrmUtils.uniq(
×
2786
                    dbConstraints.filter((dbConstraint) => {
×
2787
                        return (
×
2788
                            dbConstraint["TABLE_NAME"] ===
×
2789
                                dbTable["TABLE_NAME"] &&
×
2790
                            dbConstraint["OWNER"] === dbTable["OWNER"] &&
×
2791
                            dbConstraint["CONSTRAINT_TYPE"] === "U"
×
2792
                        )
×
2793
                    }),
×
2794
                    (dbConstraint) => dbConstraint["CONSTRAINT_NAME"],
×
2795
                )
×
2796

×
2797
                table.uniques = tableUniqueConstraints.map((constraint) => {
×
2798
                    const uniques = dbConstraints.filter(
×
2799
                        (dbC) =>
×
2800
                            dbC["CONSTRAINT_NAME"] ===
×
2801
                            constraint["CONSTRAINT_NAME"],
×
2802
                    )
×
2803
                    return new TableUnique({
×
2804
                        name: constraint["CONSTRAINT_NAME"],
×
2805
                        columnNames: uniques.map((u) => u["COLUMN_NAME"]),
×
2806
                    })
×
2807
                })
×
2808

×
2809
                // find check constraints of table, group them by constraint name and build TableCheck.
×
2810
                const tableCheckConstraints = OrmUtils.uniq(
×
2811
                    dbConstraints.filter((dbConstraint) => {
×
2812
                        return (
×
2813
                            dbConstraint["TABLE_NAME"] ===
×
2814
                                dbTable["TABLE_NAME"] &&
×
2815
                            dbConstraint["OWNER"] === dbTable["OWNER"] &&
×
2816
                            dbConstraint["CONSTRAINT_TYPE"] === "C"
×
2817
                        )
×
2818
                    }),
×
2819
                    (dbConstraint) => dbConstraint["CONSTRAINT_NAME"],
×
2820
                )
×
2821

×
2822
                table.checks = tableCheckConstraints.map((constraint) => {
×
2823
                    const checks = dbConstraints.filter(
×
2824
                        (dbC) =>
×
2825
                            dbC["TABLE_NAME"] === constraint["TABLE_NAME"] &&
×
2826
                            dbC["OWNER"] === constraint["OWNER"] &&
×
2827
                            dbC["CONSTRAINT_NAME"] ===
×
2828
                                constraint["CONSTRAINT_NAME"],
×
2829
                    )
×
2830
                    return new TableCheck({
×
2831
                        name: constraint["CONSTRAINT_NAME"],
×
2832
                        columnNames: checks.map((c) => c["COLUMN_NAME"]),
×
2833
                        expression: constraint["SEARCH_CONDITION"],
×
2834
                    })
×
2835
                })
×
2836

×
2837
                // find foreign key constraints of table, group them by constraint name and build TableForeignKey.
×
2838
                const tableForeignKeyConstraints = OrmUtils.uniq(
×
2839
                    dbForeignKeys.filter(
×
2840
                        (dbForeignKey) =>
×
2841
                            dbForeignKey["OWNER"] === dbTable["OWNER"] &&
×
2842
                            dbForeignKey["TABLE_NAME"] ===
×
2843
                                dbTable["TABLE_NAME"],
×
2844
                    ),
×
2845
                    (dbForeignKey) => dbForeignKey["CONSTRAINT_NAME"],
×
2846
                )
×
2847

×
2848
                table.foreignKeys = tableForeignKeyConstraints.map(
×
2849
                    (dbForeignKey) => {
×
2850
                        const foreignKeys = dbForeignKeys.filter(
×
2851
                            (dbFk) =>
×
2852
                                dbFk["TABLE_NAME"] ===
×
2853
                                    dbForeignKey["TABLE_NAME"] &&
×
2854
                                dbFk["OWNER"] === dbForeignKey["OWNER"] &&
×
2855
                                dbFk["CONSTRAINT_NAME"] ===
×
2856
                                    dbForeignKey["CONSTRAINT_NAME"],
×
2857
                        )
×
2858
                        return new TableForeignKey({
×
2859
                            name: dbForeignKey["CONSTRAINT_NAME"],
×
2860
                            columnNames: foreignKeys.map(
×
2861
                                (dbFk) => dbFk["COLUMN_NAME"],
×
2862
                            ),
×
2863
                            referencedDatabase: table.database,
×
2864
                            referencedSchema: dbForeignKey["OWNER"],
×
2865
                            referencedTableName:
×
2866
                                dbForeignKey["REFERENCED_TABLE_NAME"],
×
2867
                            referencedColumnNames: foreignKeys.map(
×
2868
                                (dbFk) => dbFk["REFERENCED_COLUMN_NAME"],
×
2869
                            ),
×
2870
                            onDelete: dbForeignKey["ON_DELETE"],
×
2871
                            onUpdate: "NO ACTION", // Oracle does not have onUpdate option in FK's, but we need it for proper synchronization
×
2872
                        })
×
2873
                    },
×
2874
                )
×
2875

×
2876
                // Attempt to map auto-generated virtual columns to their
×
2877
                // referenced columns, through its 'DATA_DEFAULT' property.
×
2878
                //
×
2879
                // An example of this happening is when a column of type
×
2880
                // TIMESTAMP WITH TIME ZONE is indexed. Oracle will create a
×
2881
                // virtual column of type TIMESTAMP with a default value of
×
2882
                // SYS_EXTRACT_UTC(<column>).
×
2883
                const autoGenVirtualDbColumns = dbColumns
×
2884
                    .filter(
×
2885
                        (dbColumn) =>
×
2886
                            dbColumn["OWNER"] === dbTable["OWNER"] &&
×
2887
                            dbColumn["TABLE_NAME"] === dbTable["TABLE_NAME"] &&
×
2888
                            dbColumn["VIRTUAL_COLUMN"] === "YES" &&
×
2889
                            dbColumn["USER_GENERATED"] === "NO",
×
2890
                    )
×
2891
                    .reduce((acc, x) => {
×
2892
                        const referencedDbColumn = dbColumns.find((dbColumn) =>
×
2893
                            x["DATA_DEFAULT"].includes(dbColumn["COLUMN_NAME"]),
×
2894
                        )
×
2895

×
2896
                        if (!referencedDbColumn) return acc
×
2897

×
2898
                        return {
×
2899
                            ...acc,
×
2900
                            [x["COLUMN_NAME"]]:
×
2901
                                referencedDbColumn["COLUMN_NAME"],
×
2902
                        }
×
2903
                    }, {})
×
2904

×
2905
                // create TableIndex objects from the loaded indices
×
2906
                table.indices = dbIndices
×
2907
                    .filter(
×
2908
                        (dbIndex) =>
×
2909
                            dbIndex["TABLE_NAME"] === dbTable["TABLE_NAME"] &&
×
2910
                            dbIndex["OWNER"] === dbTable["OWNER"],
×
2911
                    )
×
2912
                    .map((dbIndex) => {
×
2913
                        //
×
2914
                        const columnNames = dbIndex["COLUMN_NAMES"]
×
2915
                            .split(",")
×
2916
                            .map(
×
2917
                                (
×
2918
                                    columnName: keyof typeof autoGenVirtualDbColumns,
×
2919
                                ) =>
×
2920
                                    autoGenVirtualDbColumns[columnName] ??
×
2921
                                    columnName,
×
2922
                            )
×
2923

×
2924
                        return new TableIndex({
×
2925
                            name: dbIndex["INDEX_NAME"],
×
2926
                            columnNames,
×
2927
                            isUnique: dbIndex["UNIQUENESS"] === "UNIQUE",
×
2928
                        })
×
2929
                    })
×
2930

×
2931
                return table
×
2932
            }),
×
2933
        )
×
2934
    }
×
2935

28✔
2936
    /**
28✔
2937
     * Builds and returns SQL for create table.
28✔
2938
     * @param table
28✔
2939
     * @param createForeignKeys
28✔
2940
     */
28✔
2941
    protected createTableSql(table: Table, createForeignKeys?: boolean): Query {
28✔
2942
        const columnDefinitions = table.columns
12✔
2943
            .map((column) => this.buildCreateColumnSql(column))
12✔
2944
            .join(", ")
12✔
2945
        let sql = `CREATE TABLE ${this.escapePath(table)} (${columnDefinitions}`
12✔
2946

12✔
2947
        table.columns
12✔
2948
            .filter((column) => column.isUnique)
12✔
2949
            .forEach((column) => {
12✔
2950
                const isUniqueExist = table.uniques.some(
×
2951
                    (unique) =>
×
2952
                        unique.columnNames.length === 1 &&
×
2953
                        unique.columnNames[0] === column.name,
×
2954
                )
×
2955
                if (!isUniqueExist)
×
2956
                    table.uniques.push(
×
2957
                        new TableUnique({
×
2958
                            name: this.connection.namingStrategy.uniqueConstraintName(
×
2959
                                table,
×
2960
                                [column.name],
×
2961
                            ),
×
2962
                            columnNames: [column.name],
×
2963
                        }),
×
2964
                    )
×
2965
            })
12✔
2966

12✔
2967
        if (table.uniques.length > 0) {
12!
2968
            const uniquesSql = table.uniques
×
2969
                .map((unique) => {
×
2970
                    const uniqueName = unique.name
×
2971
                        ? unique.name
×
2972
                        : this.connection.namingStrategy.uniqueConstraintName(
×
2973
                              table,
×
2974
                              unique.columnNames,
×
2975
                          )
×
2976
                    const columnNames = unique.columnNames
×
2977
                        .map((columnName) => `"${columnName}"`)
×
2978
                        .join(", ")
×
2979
                    return `CONSTRAINT "${uniqueName}" UNIQUE (${columnNames})`
×
2980
                })
×
2981
                .join(", ")
×
2982

×
2983
            sql += `, ${uniquesSql}`
×
2984
        }
×
2985

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

×
2999
            sql += `, ${checksSql}`
×
3000
        }
×
3001

12✔
3002
        if (table.foreignKeys.length > 0 && createForeignKeys) {
12!
3003
            const foreignKeysSql = table.foreignKeys
×
3004
                .map((fk) => {
×
3005
                    const columnNames = fk.columnNames
×
3006
                        .map((columnName) => `"${columnName}"`)
×
3007
                        .join(", ")
×
3008
                    if (!fk.name)
×
3009
                        fk.name = this.connection.namingStrategy.foreignKeyName(
×
3010
                            table,
×
3011
                            fk.columnNames,
×
3012
                            this.getTablePath(fk),
×
3013
                            fk.referencedColumnNames,
×
3014
                        )
×
3015
                    const referencedColumnNames = fk.referencedColumnNames
×
3016
                        .map((columnName) => `"${columnName}"`)
×
3017
                        .join(", ")
×
3018
                    let constraint = `CONSTRAINT "${
×
3019
                        fk.name
×
3020
                    }" FOREIGN KEY (${columnNames}) REFERENCES ${this.escapePath(
×
3021
                        this.getTablePath(fk),
×
3022
                    )} (${referencedColumnNames})`
×
3023
                    if (fk.onDelete && fk.onDelete !== "NO ACTION") {
×
3024
                        // Oracle does not support NO ACTION, but we set NO ACTION by default in EntityMetadata
×
3025
                        constraint += ` ON DELETE ${fk.onDelete}`
×
3026
                    }
×
3027
                    return constraint
×
3028
                })
×
3029
                .join(", ")
×
3030

×
3031
            sql += `, ${foreignKeysSql}`
×
3032
        }
×
3033

12✔
3034
        const primaryColumns = table.columns.filter(
12✔
3035
            (column) => column.isPrimary,
12✔
3036
        )
12✔
3037
        if (primaryColumns.length > 0) {
12✔
3038
            const primaryKeyName = primaryColumns[0].primaryKeyConstraintName
12✔
3039
                ? primaryColumns[0].primaryKeyConstraintName
12!
3040
                : this.connection.namingStrategy.primaryKeyName(
12✔
3041
                      table,
12✔
3042
                      primaryColumns.map((column) => column.name),
12✔
3043
                  )
12✔
3044

12✔
3045
            const columnNames = primaryColumns
12✔
3046
                .map((column) => `"${column.name}"`)
12✔
3047
                .join(", ")
12✔
3048
            sql += `, CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNames})`
12✔
3049
        }
12✔
3050

12✔
3051
        sql += `)`
12✔
3052

12✔
3053
        return new Query(sql)
12✔
3054
    }
12✔
3055

28✔
3056
    /**
28✔
3057
     * Builds drop table sql.
28✔
3058
     * @param tableOrName
28✔
3059
     * @param ifExists
28✔
3060
     */
28✔
3061
    protected dropTableSql(
28✔
3062
        tableOrName: Table | string,
12✔
3063
        ifExists?: boolean,
12✔
3064
    ): Query {
12✔
3065
        const query = ifExists
12✔
3066
            ? `DROP TABLE IF EXISTS ${this.escapePath(tableOrName)}`
12!
3067
            : `DROP TABLE ${this.escapePath(tableOrName)}`
12✔
3068
        return new Query(query)
12✔
3069
    }
12✔
3070

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

28✔
3088
    protected insertViewDefinitionSql(view: View): Query {
28✔
3089
        const expression =
×
3090
            typeof view.expression === "string"
×
3091
                ? view.expression.trim()
×
3092
                : view.expression(this.connection).getQuery()
×
3093
        const type = view.materialized
×
3094
            ? MetadataTableType.MATERIALIZED_VIEW
×
3095
            : MetadataTableType.VIEW
×
3096
        const { schema, tableName } = this.driver.parseTableName(view)
×
3097
        return this.insertTypeormMetadataSql({
×
3098
            type: type,
×
3099
            name: tableName,
×
3100
            schema: schema,
×
3101
            value: expression,
×
3102
        })
×
3103
    }
×
3104

28✔
3105
    /**
28✔
3106
     * Builds drop view sql.
28✔
3107
     * @param view
28✔
3108
     */
28✔
3109
    protected dropViewSql(view: View): Query {
28✔
3110
        const materializedClause = view.materialized ? "MATERIALIZED " : ""
×
3111
        return new Query(
×
3112
            `DROP ${materializedClause}VIEW ${this.escapePath(view)}`,
×
3113
        )
×
3114
    }
×
3115

28✔
3116
    /**
28✔
3117
     * Builds remove view sql.
28✔
3118
     * @param view
28✔
3119
     */
28✔
3120
    protected deleteViewDefinitionSql(view: View): Query {
28✔
3121
        const type = view.materialized
×
3122
            ? MetadataTableType.MATERIALIZED_VIEW
×
3123
            : MetadataTableType.VIEW
×
3124
        return this.deleteTypeormMetadataSql({ type, name: view.name })
×
3125
    }
×
3126

28✔
3127
    /**
28✔
3128
     * Builds create index sql.
28✔
3129
     * @param table
28✔
3130
     * @param index
28✔
3131
     */
28✔
3132
    protected createIndexSql(table: Table, index: TableIndex): Query {
28✔
3133
        const columns = index.columnNames
8✔
3134
            .map((columnName) => `"${columnName}"`)
8✔
3135
            .join(", ")
8✔
3136
        return new Query(
8✔
3137
            `CREATE ${index.isUnique ? "UNIQUE " : ""}INDEX "${
8!
3138
                index.name
8✔
3139
            }" ON ${this.escapePath(table)} (${columns})`,
8✔
3140
        )
8✔
3141
    }
8✔
3142

28✔
3143
    /**
28✔
3144
     * Builds drop index sql.
28✔
3145
     * @param indexOrName
28✔
3146
     */
28✔
3147
    protected dropIndexSql(indexOrName: TableIndex | string): Query {
28✔
3148
        const indexName = InstanceChecker.isTableIndex(indexOrName)
8✔
3149
            ? indexOrName.name
8✔
3150
            : indexOrName
8!
3151
        return new Query(`DROP INDEX "${indexName}"`)
8✔
3152
    }
8✔
3153

28✔
3154
    /**
28✔
3155
     * Builds create primary key sql.
28✔
3156
     * @param table
28✔
3157
     * @param columnNames
28✔
3158
     * @param constraintName
28✔
3159
     */
28✔
3160
    protected createPrimaryKeySql(
28✔
3161
        table: Table,
×
3162
        columnNames: string[],
×
3163
        constraintName?: string,
×
3164
    ): Query {
×
3165
        const primaryKeyName = constraintName
×
3166
            ? constraintName
×
3167
            : this.connection.namingStrategy.primaryKeyName(table, columnNames)
×
3168

×
3169
        const columnNamesString = columnNames
×
3170
            .map((columnName) => `"${columnName}"`)
×
3171
            .join(", ")
×
3172

×
3173
        return new Query(
×
3174
            `ALTER TABLE ${this.escapePath(
×
3175
                table,
×
3176
            )} ADD CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNamesString})`,
×
3177
        )
×
3178
    }
×
3179

28✔
3180
    /**
28✔
3181
     * Builds drop primary key sql.
28✔
3182
     * @param table
28✔
3183
     */
28✔
3184
    protected dropPrimaryKeySql(table: Table): Query {
28✔
3185
        if (!table.primaryColumns.length)
×
3186
            throw new TypeORMError(`Table ${table} has no primary keys.`)
×
3187

×
3188
        const columnNames = table.primaryColumns.map((column) => column.name)
×
3189
        const constraintName = table.primaryColumns[0].primaryKeyConstraintName
×
3190
        const primaryKeyName = constraintName
×
3191
            ? constraintName
×
3192
            : this.connection.namingStrategy.primaryKeyName(table, columnNames)
×
3193

×
3194
        return new Query(
×
3195
            `ALTER TABLE ${this.escapePath(
×
3196
                table,
×
3197
            )} DROP CONSTRAINT "${primaryKeyName}"`,
×
3198
        )
×
3199
    }
×
3200

28✔
3201
    /**
28✔
3202
     * Builds create unique constraint sql.
28✔
3203
     * @param table
28✔
3204
     * @param uniqueConstraint
28✔
3205
     */
28✔
3206
    protected createUniqueConstraintSql(
28✔
3207
        table: Table,
×
3208
        uniqueConstraint: TableUnique,
×
3209
    ): Query {
×
3210
        const columnNames = uniqueConstraint.columnNames
×
3211
            .map((column) => `"` + column + `"`)
×
3212
            .join(", ")
×
3213
        return new Query(
×
3214
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
×
3215
                uniqueConstraint.name
×
3216
            }" UNIQUE (${columnNames})`,
×
3217
        )
×
3218
    }
×
3219

28✔
3220
    /**
28✔
3221
     * Builds drop unique constraint sql.
28✔
3222
     * @param table
28✔
3223
     * @param uniqueOrName
28✔
3224
     */
28✔
3225
    protected dropUniqueConstraintSql(
28✔
3226
        table: Table,
×
3227
        uniqueOrName: TableUnique | string,
×
3228
    ): Query {
×
3229
        const uniqueName = InstanceChecker.isTableUnique(uniqueOrName)
×
3230
            ? uniqueOrName.name
×
3231
            : uniqueOrName
×
3232
        return new Query(
×
3233
            `ALTER TABLE ${this.escapePath(
×
3234
                table,
×
3235
            )} DROP CONSTRAINT "${uniqueName}"`,
×
3236
        )
×
3237
    }
×
3238

28✔
3239
    /**
28✔
3240
     * Builds create check constraint sql.
28✔
3241
     * @param table
28✔
3242
     * @param checkConstraint
28✔
3243
     */
28✔
3244
    protected createCheckConstraintSql(
28✔
3245
        table: Table,
×
3246
        checkConstraint: TableCheck,
×
3247
    ): Query {
×
3248
        return new Query(
×
3249
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
×
3250
                checkConstraint.name
×
3251
            }" CHECK (${checkConstraint.expression})`,
×
3252
        )
×
3253
    }
×
3254

28✔
3255
    /**
28✔
3256
     * Builds drop check constraint sql.
28✔
3257
     * @param table
28✔
3258
     * @param checkOrName
28✔
3259
     */
28✔
3260
    protected dropCheckConstraintSql(
28✔
3261
        table: Table,
×
3262
        checkOrName: TableCheck | string,
×
3263
    ): Query {
×
3264
        const checkName = InstanceChecker.isTableCheck(checkOrName)
×
3265
            ? checkOrName.name
×
3266
            : checkOrName
×
3267
        return new Query(
×
3268
            `ALTER TABLE ${this.escapePath(
×
3269
                table,
×
3270
            )} DROP CONSTRAINT "${checkName}"`,
×
3271
        )
×
3272
    }
×
3273

28✔
3274
    /**
28✔
3275
     * Builds create foreign key sql.
28✔
3276
     * @param table
28✔
3277
     * @param foreignKey
28✔
3278
     */
28✔
3279
    protected createForeignKeySql(
28✔
3280
        table: Table,
12✔
3281
        foreignKey: TableForeignKey,
12✔
3282
    ): Query {
12✔
3283
        const columnNames = foreignKey.columnNames
12✔
3284
            .map((column) => `"` + column + `"`)
12✔
3285
            .join(", ")
12✔
3286
        const referencedColumnNames = foreignKey.referencedColumnNames
12✔
3287
            .map((column) => `"` + column + `"`)
12✔
3288
            .join(",")
12✔
3289
        let sql =
12✔
3290
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
12✔
3291
                foreignKey.name
12✔
3292
            }" FOREIGN KEY (${columnNames}) ` +
12✔
3293
            `REFERENCES ${this.escapePath(
12✔
3294
                this.getTablePath(foreignKey),
12✔
3295
            )} (${referencedColumnNames})`
12✔
3296
        // Oracle does not support NO ACTION, but we set NO ACTION by default in EntityMetadata
12✔
3297
        if (foreignKey.onDelete && foreignKey.onDelete !== "NO ACTION") {
12✔
3298
            sql += ` ON DELETE ${foreignKey.onDelete}`
8✔
3299
        }
8✔
3300
        return new Query(sql)
12✔
3301
    }
12✔
3302

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

28✔
3324
    /**
28✔
3325
     * Builds a query for create column.
28✔
3326
     * @param column
28✔
3327
     */
28✔
3328
    protected buildCreateColumnSql(column: TableColumn) {
28✔
3329
        let c =
36✔
3330
            `"${column.name}" ` + this.connection.driver.createFullType(column)
36✔
3331
        if (column.charset) c += " CHARACTER SET " + column.charset
36!
3332
        if (column.collation) c += " COLLATE " + column.collation
36!
3333

36✔
3334
        if (column.asExpression) c += ` AS (${column.asExpression}) VIRTUAL`
36!
3335

36✔
3336
        if (column.default !== undefined && column.default !== null)
36!
3337
            // DEFAULT must be placed before NOT NULL
36✔
3338
            c += " DEFAULT " + column.default
36!
3339
        if (column.isNullable !== true && !column.isGenerated)
36✔
3340
            // NOT NULL is not supported with GENERATED
36✔
3341
            c += " NOT NULL"
36✔
3342
        if (
36✔
3343
            column.isGenerated === true &&
36✔
3344
            column.generationStrategy === "increment"
4✔
3345
        )
36✔
3346
            c += " GENERATED BY DEFAULT AS IDENTITY"
36✔
3347

36✔
3348
        return c
36✔
3349
    }
36✔
3350

28✔
3351
    /**
28✔
3352
     * Escapes given table or view path.
28✔
3353
     * @param target
28✔
3354
     */
28✔
3355
    protected escapePath(target: Table | View | string): string {
28✔
3356
        // Ignore database when escaping paths
68✔
3357
        const { schema, tableName } = this.driver.parseTableName(target)
68✔
3358

68✔
3359
        if (schema && schema !== this.driver.schema) {
68!
3360
            return `"${schema}"."${tableName}"`
×
3361
        }
×
3362

68✔
3363
        return `"${tableName}"`
68✔
3364
    }
68✔
3365

28✔
3366
    /**
28✔
3367
     * Change table comment.
28✔
3368
     * @param tableOrName
28✔
3369
     * @param comment
28✔
3370
     */
28✔
3371
    changeTableComment(
28✔
3372
        tableOrName: Table | string,
×
3373
        comment?: string,
×
3374
    ): Promise<void> {
×
3375
        throw new TypeORMError(
×
3376
            `oracle driver does not support change table comment.`,
×
3377
        )
×
3378
    }
×
3379
}
28✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc