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

typeorm / typeorm / 23191639722

17 Mar 2026 11:18AM UTC coverage: 73.246% (+0.06%) from 73.185%
23191639722

push

github

web-flow
fix(query-runner): parameterize queries and escape identifiers to prevent SQL injection (#12207)

37395 of 47676 branches covered (78.44%)

Branch coverage included in aggregate %.

111 of 139 new or added lines in 19 files covered. (79.86%)

1 existing line in 1 file now uncovered.

83861 of 117870 relevant lines covered (71.15%)

64276.48 hits per line

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

92.38
/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()
24,532✔
57
        this.driver = driver
24,532✔
58
        this.connection = driver.connection
24,532✔
59
        this.broadcaster = new Broadcaster(this)
24,532✔
60
        this.mode = mode
24,532✔
61
    }
24,532✔
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)
124,524✔
73
            return Promise.resolve(this.databaseConnection)
124,524✔
74

23,624✔
75
        if (this.databaseConnectionPromise)
23,624✔
76
            return this.databaseConnectionPromise
124,524✔
77

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

23,496✔
95
        return this.databaseConnectionPromise
23,496✔
96
    }
23,496✔
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
24,532✔
104

24,532✔
105
        if (!this.databaseConnection) {
24,532✔
106
            return
1,036✔
107
        }
1,036✔
108

23,496✔
109
        await this.databaseConnection.close()
23,496✔
110
    }
23,496✔
111

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

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

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

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

17,746✔
148
        await this.broadcaster.broadcast("AfterTransactionStart")
17,746✔
149
    }
17,746✔
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()
17,698!
157

17,698✔
158
        await this.broadcaster.broadcast("BeforeTransactionCommit")
17,698✔
159

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

17,698✔
166
        await this.broadcaster.broadcast("AfterTransactionCommit")
17,698✔
167
    }
17,698✔
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()
48!
175

48✔
176
        await this.broadcaster.broadcast("BeforeTransactionRollback")
48✔
177

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

48✔
188
        await this.broadcaster.broadcast("AfterTransactionRollback")
48✔
189
    }
48✔
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,
124,514✔
199
        parameters?: any[],
124,514✔
200
        useStructuredResult = false,
124,514✔
201
    ): Promise<any> {
124,514✔
202
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
124,514!
203

124,514✔
204
        const databaseConnection = await this.connect()
124,514✔
205

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

124,514✔
209
        const broadcasterResult = new BroadcasterResult()
124,514✔
210
        const queryStartTime = Date.now()
124,514✔
211

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

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

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

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

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

124,422✔
251
            const result = new QueryResult()
124,422✔
252

124,422✔
253
            result.raw =
124,422✔
254
                raw.rows ||
124,422✔
255
                raw.outBinds ||
124,514✔
256
                raw.rowsAffected ||
124,514✔
257
                raw.implicitResults
64,228✔
258

124,514✔
259
            if (raw?.hasOwnProperty("rows") && Array.isArray(raw.rows)) {
124,514✔
260
                result.records = raw.rows
39,326✔
261
            }
39,326✔
262

124,422✔
263
            if (
124,422✔
264
                raw?.hasOwnProperty("outBinds") &&
124,514✔
265
                Array.isArray(raw.outBinds)
7,560✔
266
            ) {
124,514✔
267
                result.records = raw.outBinds
7,560✔
268
            }
7,560✔
269

124,422✔
270
            if (
124,422✔
271
                raw?.hasOwnProperty("implicitResults") &&
124,514✔
272
                Array.isArray(raw.implicitResults)
2✔
273
            ) {
124,514✔
274
                result.records = raw.implicitResults
2✔
275
            }
2✔
276

124,422✔
277
            if (raw?.hasOwnProperty("rowsAffected")) {
124,514✔
278
                result.affected = raw.rowsAffected
85,094✔
279
            }
85,094✔
280

124,422✔
281
            if (useStructuredResult) {
124,514✔
282
                return result
33,958✔
283
            } else {
124,514✔
284
                return result.raw
90,464✔
285
            }
90,464✔
286
        } catch (err) {
124,514✔
287
            this.driver.connection.logger.logQueryError(
92✔
288
                err,
92✔
289
                query,
92✔
290
                parameters,
92✔
291
                this,
92✔
292
            )
92✔
293
            this.broadcaster.broadcastAfterQueryEvent(
92✔
294
                broadcasterResult,
92✔
295
                query,
92✔
296
                parameters,
92✔
297
                false,
92✔
298
                undefined,
92✔
299
                undefined,
92✔
300
                err,
92✔
301
            )
92✔
302

92✔
303
            throw new QueryFailedError(query, parameters, err)
92✔
304
        } finally {
124,514!
305
            await broadcasterResult.wait()
124,514✔
306
        }
124,514✔
307
    }
124,514✔
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,
2✔
318
        parameters?: any[],
2✔
319
        onEnd?: Function,
2✔
320
        onError?: Function,
2✔
321
    ): Promise<ReadStream> {
2✔
322
        if (this.isReleased) {
2!
323
            throw new QueryRunnerAlreadyReleasedError()
×
324
        }
×
325

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

2✔
331
        const databaseConnection = await this.connect()
2✔
332

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

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

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

2✔
349
            return stream
2✔
350
        } catch (err) {
2!
351
            this.driver.connection.logger.logQueryError(
×
352
                err,
×
353
                query,
×
354
                parameters,
×
355
                this,
×
356
            )
×
357
            throw new QueryFailedError(query, parameters, err)
×
358
        }
×
359
    }
2✔
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 {
24✔
383
            const query = await this.query(
24✔
384
                `SELECT 1 AS "exists" FROM global_name@${this.driver.escape(database)}`,
24✔
385
            )
24✔
386

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

28✔
393
    /**
28✔
394
     * Loads currently using database
28✔
395
     */
28✔
396
    async getCurrentDatabase(): Promise<undefined> {
28✔
397
        const query = await this.query(
4,336✔
398
            `SELECT SYS_CONTEXT('USERENV','DB_NAME') AS "db_name" FROM dual`,
4,336✔
399
        )
4,336✔
400
        return query[0]["db_name"]
4,336✔
401
    }
4,336✔
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)
24✔
409
    }
24✔
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(
4,336✔
416
            `SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') AS "schema_name" FROM dual`,
4,336✔
417
        )
4,336✔
418
        return query[0]["schema_name"]
4,336✔
419
    }
4,336✔
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)
3,026✔
427
        const sql = `SELECT "TABLE_NAME" FROM "USER_TABLES" WHERE "TABLE_NAME" = :1`
3,026✔
428
        const result = await this.query(sql, [tableName])
3,026✔
429
        return result.length ? true : false
3,026✔
430
    }
3,026✔
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,
56✔
439
        columnName: string,
56✔
440
    ): Promise<boolean> {
56✔
441
        const { tableName } = this.driver.parseTableName(tableOrName)
56✔
442
        const sql = `SELECT "COLUMN_NAME" FROM "USER_TAB_COLS" WHERE "TABLE_NAME" = :1 AND "COLUMN_NAME" = :2`
56✔
443
        const result = await this.query(sql, [tableName, columnName])
56✔
444
        return result.length ? true : false
56✔
445
    }
56✔
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,
24✔
454
        ifNotExists?: boolean,
24✔
455
    ): Promise<void> {
24✔
456
        // Even with `IF NOT EXISTS` we get:
24✔
457
        //   ORA-01501: CREATE DATABASE failed
24✔
458
        //   ORA-01100: database already mounted
24✔
459
        if (ifNotExists) {
24✔
460
            try {
24✔
461
                await this.query(
24✔
462
                    `CREATE DATABASE IF NOT EXISTS ${this.driver.escape(database)};`,
24✔
463
                )
24✔
464
            } catch (e) {
24!
465
                // if (e instanceof QueryFailedError) {
24✔
466
                if (e.message.includes("ORA-01100: database already mounted")) {
24✔
467
                    return
24✔
468
                }
24✔
469
                // }
×
470

×
471
                throw e
×
472
            }
×
473
        } else {
24!
NEW
474
            await this.query(`CREATE DATABASE ${this.driver.escape(database)}`)
×
475
        }
×
476
    }
24✔
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()
24✔
485
    }
24✔
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,
26✔
494
        ifNotExists?: boolean,
26✔
495
    ): Promise<void> {
26✔
496
        throw new TypeORMError(
26✔
497
            `Schema create queries are not supported by Oracle driver.`,
26✔
498
        )
26✔
499
    }
26✔
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(
24✔
508
            `Schema drop queries are not supported by Oracle driver.`,
24✔
509
        )
24✔
510
    }
24✔
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,
9,464✔
521
        ifNotExists: boolean = false,
9,464✔
522
        createForeignKeys: boolean = true,
9,464✔
523
        createIndices: boolean = true,
9,464✔
524
    ): Promise<void> {
9,464✔
525
        if (ifNotExists) {
9,464✔
526
            const isTableExist = await this.hasTable(table)
82✔
527
            if (isTableExist) return Promise.resolve()
82✔
528
        }
82✔
529
        const upQueries: Query[] = []
9,462✔
530
        const downQueries: Query[] = []
9,462✔
531

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

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

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

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

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

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

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

9,462✔
579
        await this.executeQueries(upQueries, downQueries)
9,462✔
580
    }
9,462✔
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,
46✔
591
        ifExists?: boolean,
46✔
592
        dropForeignKeys: boolean = true,
46✔
593
        dropIndices: boolean = true,
46✔
594
    ): Promise<void> {
46✔
595
        // It needs because if table does not exist and dropForeignKeys or dropIndices is true, we don't need
46✔
596
        // to perform drop queries for foreign keys and indices.
46✔
597
        if (ifExists) {
46✔
598
            const isTableExist = await this.hasTable(tableOrName)
26✔
599
            if (!isTableExist) return Promise.resolve()
26✔
600
        }
26✔
601

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

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

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

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

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

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

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

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

20✔
650
        await this.executeQueries(upQueries, downQueries)
20✔
651
    }
20✔
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,
56✔
660
        syncWithMetadata: boolean = false,
56✔
661
    ): Promise<void> {
56✔
662
        const upQueries: Query[] = []
56✔
663
        const downQueries: Query[] = []
56✔
664
        upQueries.push(this.createViewSql(view))
56✔
665
        if (syncWithMetadata) upQueries.push(this.insertViewDefinitionSql(view))
56✔
666
        downQueries.push(this.dropViewSql(view))
56✔
667
        if (syncWithMetadata)
56✔
668
            downQueries.push(this.deleteViewDefinitionSql(view))
56✔
669
        await this.executeQueries(upQueries, downQueries)
56✔
670
    }
56✔
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
6✔
679
        const view = ifExists
6✔
680
            ? await this.getCachedView(viewName).catch(() => undefined)
6✔
681
            : await this.getCachedView(viewName)
6✔
682
        if (!view) return
4✔
683

4✔
684
        const upQueries: Query[] = []
4✔
685
        const downQueries: Query[] = []
4✔
686
        upQueries.push(this.deleteViewDefinitionSql(view))
4✔
687
        upQueries.push(this.dropViewSql(view))
4✔
688
        downQueries.push(this.insertViewDefinitionSql(view))
4✔
689
        downQueries.push(this.createViewSql(view))
4✔
690
        await this.executeQueries(upQueries, downQueries)
4✔
691
    }
4✔
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,
34✔
700
        newTableName: string,
34✔
701
    ): Promise<void> {
34✔
702
        const upQueries: Query[] = []
34✔
703
        const downQueries: Query[] = []
34✔
704
        const oldTable = InstanceChecker.isTable(oldTableOrName)
34✔
705
            ? oldTableOrName
34✔
706
            : await this.getCachedTable(oldTableOrName)
34✔
707
        const newTable = oldTable.clone()
30✔
708

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

30✔
712
        newTable.name = dbName ? `${dbName}.${newTableName}` : newTableName
34!
713

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

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

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

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

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

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

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

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

6✔
803
            // replace constraint name
6✔
804
            unique.name = newUniqueName
6✔
805
        })
34✔
806

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

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

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

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

10✔
837
            // replace constraint name
10✔
838
            index.name = newIndexName
10✔
839
        })
34✔
840

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

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

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

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

2✔
883
            // replace constraint name
2✔
884
            foreignKey.name = newForeignKeyName
2✔
885
        })
34✔
886

34✔
887
        await this.executeQueries(upQueries, downQueries)
34✔
888

34✔
889
        // rename old table and replace it in cached tabled;
34✔
890
        oldTable.name = newTable.name
34✔
891
        this.replaceCachedTable(oldTable, newTable)
34✔
892
    }
34✔
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,
50✔
901
        column: TableColumn,
50✔
902
    ): Promise<void> {
50✔
903
        const table = InstanceChecker.isTable(tableOrName)
50✔
904
            ? tableOrName
50✔
905
            : await this.getCachedTable(tableOrName)
50✔
906
        const clonedTable = table.clone()
4✔
907
        const upQueries: Query[] = []
4✔
908
        const downQueries: Query[] = []
4✔
909

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

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

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

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

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

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

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

50✔
985
        // create column index
50✔
986
        const columnIndex = clonedTable.indices.find(
50✔
987
            (index) =>
50✔
988
                index.columnNames.length === 1 &&
2✔
989
                index.columnNames[0] === column.name,
50✔
990
        )
50✔
991
        if (columnIndex) {
50!
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

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

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

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

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

50✔
1044
        await this.executeQueries(upQueries, downQueries)
50✔
1045

48✔
1046
        clonedTable.addColumn(column)
48✔
1047
        this.replaceCachedTable(table, clonedTable)
48✔
1048
    }
48✔
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,
8✔
1057
        columns: TableColumn[],
8✔
1058
    ): Promise<void> {
8✔
1059
        for (const column of columns) {
8✔
1060
            await this.addColumn(tableOrName, column)
10✔
1061
        }
10✔
1062
    }
8✔
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,
26✔
1072
        oldTableColumnOrName: TableColumn | string,
26✔
1073
        newTableColumnOrName: TableColumn | string,
26✔
1074
    ): Promise<void> {
26✔
1075
        const table = InstanceChecker.isTable(tableOrName)
26✔
1076
            ? tableOrName
26✔
1077
            : await this.getCachedTable(tableOrName)
26✔
1078
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
4✔
1079
            ? oldTableColumnOrName
26✔
1080
            : table.columns.find((c) => c.name === oldTableColumnOrName)
26✔
1081
        if (!oldColumn)
26✔
1082
            throw new TypeORMError(
26!
1083
                `Column "${oldTableColumnOrName}" was not found in the ${this.escapePath(
×
1084
                    table,
×
1085
                )} table.`,
×
1086
            )
×
1087

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

26✔
1096
        await this.changeColumn(table, oldColumn, newColumn)
26✔
1097
    }
26✔
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,
98✔
1107
        oldTableColumnOrName: TableColumn | string,
98✔
1108
        newColumn: TableColumn,
98✔
1109
    ): Promise<void> {
98✔
1110
        const table = InstanceChecker.isTable(tableOrName)
98✔
1111
            ? tableOrName
98✔
1112
            : await this.getCachedTable(tableOrName)
98!
1113
        let clonedTable = table.clone()
×
1114
        const upQueries: Query[] = []
×
1115
        const downQueries: Query[] = []
×
1116

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

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

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

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

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

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

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

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

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

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

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

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

8✔
1249
                    // replace constraint name
8✔
1250
                    unique.name = newUniqueName
8✔
1251
                })
44✔
1252

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

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

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

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

10✔
1290
                    // replace constraint name
10✔
1291
                    index.name = newIndexName
10✔
1292
                })
44✔
1293

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

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

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

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

2✔
1343
                        // replace constraint name
2✔
1344
                        foreignKey.name = newForeignKeyName
2✔
1345
                    })
44✔
1346

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

66✔
1528
            if (newColumn.isUnique !== oldColumn.isUnique) {
66✔
1529
                if (newColumn.isUnique === true) {
2✔
1530
                    const uniqueConstraint = new TableUnique({
2✔
1531
                        name: this.connection.namingStrategy.uniqueConstraintName(
2✔
1532
                            table,
2✔
1533
                            [newColumn.name],
2✔
1534
                        ),
2✔
1535
                        columnNames: [newColumn.name],
2✔
1536
                    })
2✔
1537
                    clonedTable.uniques.push(uniqueConstraint)
2✔
1538
                    upQueries.push(
2✔
1539
                        new Query(
2✔
1540
                            `ALTER TABLE ${this.escapePath(
2✔
1541
                                table,
2✔
1542
                            )} ADD CONSTRAINT "${
2✔
1543
                                uniqueConstraint.name
2✔
1544
                            }" UNIQUE ("${newColumn.name}")`,
2✔
1545
                        ),
2✔
1546
                    )
2✔
1547
                    downQueries.push(
2✔
1548
                        new Query(
2✔
1549
                            `ALTER TABLE ${this.escapePath(
2✔
1550
                                table,
2✔
1551
                            )} DROP CONSTRAINT "${uniqueConstraint.name}"`,
2✔
1552
                        ),
2✔
1553
                    )
2✔
1554
                } else {
2!
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
            }
2✔
1588

66✔
1589
            await this.executeQueries(upQueries, downQueries)
66✔
1590
            this.replaceCachedTable(table, clonedTable)
66✔
1591
        }
66✔
1592
    }
98✔
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,
48✔
1601
        changedColumns: { newColumn: TableColumn; oldColumn: TableColumn }[],
48✔
1602
    ): Promise<void> {
48✔
1603
        for (const { oldColumn, newColumn } of changedColumns) {
48✔
1604
            await this.changeColumn(tableOrName, oldColumn, newColumn)
60✔
1605
        }
60✔
1606
    }
48✔
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,
68✔
1616
        columnOrName: TableColumn | string,
68✔
1617
        ifExists?: boolean,
68✔
1618
    ): Promise<void> {
68✔
1619
        const table = InstanceChecker.isTable(tableOrName)
68✔
1620
            ? tableOrName
68✔
1621
            : await this.getCachedTable(tableOrName)
68✔
1622
        const column = InstanceChecker.isTableColumn(columnOrName)
8✔
1623
            ? columnOrName
68✔
1624
            : table.findColumnByName(columnOrName)
68✔
1625
        if (!column) {
68✔
1626
            if (ifExists) return
4✔
1627
            throw new TypeORMError(
2✔
1628
                `Column "${columnOrName}" was not found in table ${this.escapePath(
2✔
1629
                    table,
2✔
1630
                )}`,
2✔
1631
            )
2✔
1632
        }
2✔
1633

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

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

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

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

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

14✔
1670
            // if primary key have multiple columns, we must recreate it without dropped column
14✔
1671
            if (clonedTable.primaryColumns.length > 0) {
14!
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
        }
14✔
1702

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

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

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

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

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

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

64✔
1779
        await this.executeQueries(upQueries, downQueries)
64✔
1780

64✔
1781
        clonedTable.removeColumn(column)
64✔
1782
        this.replaceCachedTable(table, clonedTable)
64✔
1783
    }
64✔
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,
10✔
1793
        columns: TableColumn[] | string[],
10✔
1794
        ifExists?: boolean,
10✔
1795
    ): Promise<void> {
10✔
1796
        for (const column of [...columns]) {
10✔
1797
            await this.dropColumn(tableOrName, column, ifExists)
26✔
1798
        }
26✔
1799
    }
10✔
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,
4✔
1809
        columnNames: string[],
4✔
1810
        constraintName?: string,
4✔
1811
    ): Promise<void> {
4✔
1812
        const table = InstanceChecker.isTable(tableOrName)
4✔
1813
            ? tableOrName
4!
1814
            : await this.getCachedTable(tableOrName)
4✔
1815
        const clonedTable = table.clone()
4✔
1816

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

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

4✔
1826
        await this.executeQueries(up, down)
4✔
1827
        this.replaceCachedTable(table, clonedTable)
4✔
1828
    }
4✔
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,
4✔
1837
        columns: TableColumn[],
4✔
1838
    ): Promise<void> {
4✔
1839
        const table = InstanceChecker.isTable(tableOrName)
4✔
1840
            ? tableOrName
4✔
1841
            : await this.getCachedTable(tableOrName)
4!
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) {
4✔
1850
            const pkName = primaryColumns[0].primaryKeyConstraintName
4✔
1851
                ? primaryColumns[0].primaryKeyConstraintName
4!
1852
                : this.connection.namingStrategy.primaryKeyName(
4✔
1853
                      clonedTable,
4✔
1854
                      primaryColumns.map((column) => column.name),
4✔
1855
                  )
4✔
1856

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

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

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

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

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

4✔
1909
        await this.executeQueries(upQueries, downQueries)
4✔
1910
        this.replaceCachedTable(table, clonedTable)
4✔
1911
    }
4✔
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,
8✔
1921
        constraintName?: string,
8✔
1922
        ifExists?: boolean,
8✔
1923
    ): Promise<void> {
8✔
1924
        const table = InstanceChecker.isTable(tableOrName)
8✔
1925
            ? tableOrName
8✔
1926
            : await this.getCachedTable(tableOrName)
8✔
1927
        if (ifExists && table.primaryColumns.length === 0) return
8!
1928

8✔
1929
        const up = this.dropPrimaryKeySql(table)
8✔
1930
        const down = this.createPrimaryKeySql(
8✔
1931
            table,
8✔
1932
            table.primaryColumns.map((column) => column.name),
8✔
1933
            constraintName,
8✔
1934
        )
8✔
1935
        await this.executeQueries(up, down)
8✔
1936
        table.primaryColumns.forEach((column) => {
8✔
1937
            column.isPrimary = false
8✔
1938
        })
8✔
1939
    }
8✔
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,
16✔
1948
        uniqueConstraint: TableUnique,
16✔
1949
    ): Promise<void> {
16✔
1950
        const table = InstanceChecker.isTable(tableOrName)
16✔
1951
            ? tableOrName
16✔
1952
            : await this.getCachedTable(tableOrName)
16✔
1953

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

16✔
1962
        const up = this.createUniqueConstraintSql(table, uniqueConstraint)
16✔
1963
        const down = this.dropUniqueConstraintSql(table, uniqueConstraint)
16✔
1964
        await this.executeQueries(up, down)
16✔
1965
        table.addUniqueConstraint(uniqueConstraint)
16✔
1966
    }
16✔
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,
6✔
1975
        uniqueConstraints: TableUnique[],
6✔
1976
    ): Promise<void> {
6✔
1977
        const promises = uniqueConstraints.map((uniqueConstraint) =>
6✔
1978
            this.createUniqueConstraint(tableOrName, uniqueConstraint),
6✔
1979
        )
6✔
1980
        await Promise.all(promises)
6✔
1981
    }
6✔
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,
18✔
1991
        uniqueOrName: TableUnique | string,
18✔
1992
        ifExists?: boolean,
18✔
1993
    ): Promise<void> {
18✔
1994
        const table = InstanceChecker.isTable(tableOrName)
18✔
1995
            ? tableOrName
18✔
1996
            : await this.getCachedTable(tableOrName)
18✔
1997
        const uniqueConstraint = InstanceChecker.isTableUnique(uniqueOrName)
10✔
1998
            ? uniqueOrName
18✔
1999
            : table.uniques.find((u) => u.name === uniqueOrName)
18✔
2000
        if (!uniqueConstraint) {
18✔
2001
            if (ifExists) return
2✔
2002
            throw new TypeORMError(
×
2003
                `Supplied unique constraint was not found in table ${table.name}`,
×
2004
            )
×
2005
        }
×
2006

16✔
2007
        const up = this.dropUniqueConstraintSql(table, uniqueConstraint)
16✔
2008
        const down = this.createUniqueConstraintSql(table, uniqueConstraint)
16✔
2009
        await this.executeQueries(up, down)
16✔
2010
        table.removeUniqueConstraint(uniqueConstraint)
16✔
2011
    }
16✔
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,
8✔
2021
        uniqueConstraints: TableUnique[],
8✔
2022
        ifExists?: boolean,
8✔
2023
    ): Promise<void> {
8✔
2024
        const promises = uniqueConstraints.map((uniqueConstraint) =>
8✔
2025
            this.dropUniqueConstraint(tableOrName, uniqueConstraint, ifExists),
8✔
2026
        )
8✔
2027
        await Promise.all(promises)
8✔
2028
    }
8✔
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,
10✔
2037
        checkConstraint: TableCheck,
10✔
2038
    ): Promise<void> {
10✔
2039
        const table = InstanceChecker.isTable(tableOrName)
10✔
2040
            ? tableOrName
10✔
2041
            : await this.getCachedTable(tableOrName)
10✔
2042

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

10✔
2051
        const up = this.createCheckConstraintSql(table, checkConstraint)
10✔
2052
        const down = this.dropCheckConstraintSql(table, checkConstraint)
10✔
2053
        await this.executeQueries(up, down)
10✔
2054
        table.addCheckConstraint(checkConstraint)
10✔
2055
    }
10✔
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,
4✔
2064
        checkConstraints: TableCheck[],
4✔
2065
    ): Promise<void> {
4✔
2066
        const promises = checkConstraints.map((checkConstraint) =>
4✔
2067
            this.createCheckConstraint(tableOrName, checkConstraint),
4✔
2068
        )
4✔
2069
        await Promise.all(promises)
4✔
2070
    }
4✔
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,
8✔
2080
        checkOrName: TableCheck | string,
8✔
2081
        ifExists?: boolean,
8✔
2082
    ): Promise<void> {
8✔
2083
        const table = InstanceChecker.isTable(tableOrName)
8✔
2084
            ? tableOrName
8✔
2085
            : await this.getCachedTable(tableOrName)
8✔
2086
        const checkConstraint = InstanceChecker.isTableCheck(checkOrName)
2✔
2087
            ? checkOrName
8✔
2088
            : table.checks.find((c) => c.name === checkOrName)
8✔
2089
        if (!checkConstraint) {
8✔
2090
            if (ifExists) return
2✔
2091
            throw new TypeORMError(
×
2092
                `Supplied check constraint was not found in table ${table.name}`,
×
2093
            )
×
2094
        }
×
2095

6✔
2096
        const up = this.dropCheckConstraintSql(table, checkConstraint)
6✔
2097
        const down = this.createCheckConstraintSql(table, checkConstraint)
6✔
2098
        await this.executeQueries(up, down)
6✔
2099
        table.removeCheckConstraint(checkConstraint)
6✔
2100
    }
6✔
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,
4✔
2110
        checkConstraints: TableCheck[],
4✔
2111
        ifExists?: boolean,
4✔
2112
    ): Promise<void> {
4✔
2113
        const promises = checkConstraints.map((checkConstraint) =>
4✔
2114
            this.dropCheckConstraint(tableOrName, checkConstraint, ifExists),
4✔
2115
        )
4✔
2116
        await Promise.all(promises)
4✔
2117
    }
4✔
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,
5,916✔
2178
        foreignKey: TableForeignKey,
5,916✔
2179
    ): Promise<void> {
5,916✔
2180
        const table = InstanceChecker.isTable(tableOrName)
5,916✔
2181
            ? tableOrName
5,916✔
2182
            : await this.getCachedTable(tableOrName)
5,916✔
2183

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

5,916✔
2193
        const up = this.createForeignKeySql(table, foreignKey)
5,916✔
2194
        const down = this.dropForeignKeySql(table, foreignKey)
5,916✔
2195
        await this.executeQueries(up, down)
5,916✔
2196
        table.addForeignKey(foreignKey)
5,916✔
2197
    }
5,916✔
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,
3,528✔
2206
        foreignKeys: TableForeignKey[],
3,528✔
2207
    ): Promise<void> {
3,528✔
2208
        const promises = foreignKeys.map((foreignKey) =>
3,528✔
2209
            this.createForeignKey(tableOrName, foreignKey),
3,528✔
2210
        )
3,528✔
2211
        await Promise.all(promises)
3,528✔
2212
    }
3,528✔
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,
22✔
2222
        foreignKeyOrName: TableForeignKey | string,
22✔
2223
        ifExists?: boolean,
22✔
2224
    ): Promise<void> {
22✔
2225
        const table = InstanceChecker.isTable(tableOrName)
22✔
2226
            ? tableOrName
22✔
2227
            : await this.getCachedTable(tableOrName)
22✔
2228
        const foreignKey = InstanceChecker.isTableForeignKey(foreignKeyOrName)
10✔
2229
            ? foreignKeyOrName
22✔
2230
            : table.foreignKeys.find((fk) => fk.name === foreignKeyOrName)
22✔
2231
        if (!foreignKey) {
22✔
2232
            if (ifExists) return
2✔
2233
            throw new TypeORMError(
×
2234
                `Supplied foreign key was not found in table ${table.name}`,
×
2235
            )
×
2236
        }
×
2237

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

20✔
2247
        const up = this.dropForeignKeySql(table, foreignKey)
20✔
2248
        const down = this.createForeignKeySql(table, foreignKey)
20✔
2249
        await this.executeQueries(up, down)
20✔
2250
        table.removeForeignKey(foreignKey)
20✔
2251
    }
20✔
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,
12✔
2261
        foreignKeys: TableForeignKey[],
12✔
2262
        ifExists?: boolean,
12✔
2263
    ): Promise<void> {
12✔
2264
        const promises = foreignKeys.map((foreignKey) =>
12✔
2265
            this.dropForeignKey(tableOrName, foreignKey, ifExists),
12✔
2266
        )
12✔
2267
        await Promise.all(promises)
12✔
2268
    }
12✔
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,
28✔
2277
        index: TableIndex,
28✔
2278
    ): Promise<void> {
28✔
2279
        const table = InstanceChecker.isTable(tableOrName)
28✔
2280
            ? tableOrName
28✔
2281
            : await this.getCachedTable(tableOrName)
28✔
2282

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

28✔
2286
        const up = this.createIndexSql(table, index)
28✔
2287
        const down = this.dropIndexSql(index)
28✔
2288
        await this.executeQueries(up, down)
28✔
2289
        table.addIndex(index)
28✔
2290
    }
28✔
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,
14✔
2299
        indices: TableIndex[],
14✔
2300
    ): Promise<void> {
14✔
2301
        const promises = indices.map((index) =>
14✔
2302
            this.createIndex(tableOrName, index),
14✔
2303
        )
14✔
2304
        await Promise.all(promises)
14✔
2305
    }
14✔
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,
30✔
2315
        indexOrName: TableIndex | string,
30✔
2316
        ifExists?: boolean,
30✔
2317
    ): Promise<void> {
30✔
2318
        const table = InstanceChecker.isTable(tableOrName)
30✔
2319
            ? tableOrName
30✔
2320
            : await this.getCachedTable(tableOrName)
30✔
2321
        const index = InstanceChecker.isTableIndex(indexOrName)
12✔
2322
            ? indexOrName
30✔
2323
            : table.indices.find((i) => i.name === indexOrName)
30✔
2324
        if (!index) {
30✔
2325
            if (ifExists) return
2✔
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.
28✔
2331
        if (!index.name) index.name = this.generateIndexName(table, index)
30✔
2332

28✔
2333
        const up = this.dropIndexSql(index)
28✔
2334
        const down = this.createIndexSql(table, index)
28✔
2335
        await this.executeQueries(up, down)
28✔
2336
        table.removeIndex(index)
28✔
2337
    }
28✔
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,
2✔
2347
        indices: TableIndex[],
2✔
2348
        ifExists?: boolean,
2✔
2349
    ): Promise<void> {
2✔
2350
        const promises = indices.map((index) =>
2✔
2351
            this.dropIndex(tableOrName, index, ifExists),
2✔
2352
        )
2✔
2353
        await Promise.all(promises)
2✔
2354
    }
2✔
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,
16✔
2365
        options?: { cascade?: boolean },
16✔
2366
    ): Promise<void> {
16✔
2367
        const cascade = options?.cascade ? " CASCADE" : ""
16✔
2368
        await this.query(
16✔
2369
            `TRUNCATE TABLE ${this.escapePath(tableName)}${cascade}`,
16✔
2370
        )
16✔
2371
    }
10✔
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
2,712✔
2378
        if (!isAnotherTransactionActive) await this.startTransaction()
2,712✔
2379
        try {
2,712✔
2380
            // drop views
2,712✔
2381
            const dropViewsQuery = `SELECT 'DROP VIEW "' || VIEW_NAME || '"' AS "query" FROM "USER_VIEWS"`
2,712✔
2382
            const dropViewQueries: ObjectLiteral[] =
2,712✔
2383
                await this.query(dropViewsQuery)
2,712✔
2384
            await Promise.all(
2,712✔
2385
                dropViewQueries.map((query) => this.query(query["query"])),
2,712✔
2386
            )
2,712✔
2387

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

2,712✔
2396
            // drop tables
2,712✔
2397
            const dropTablesQuery = `SELECT 'DROP TABLE "' || TABLE_NAME || '" CASCADE CONSTRAINTS' AS "query" FROM "USER_TABLES"`
2,712✔
2398
            const dropTableQueries: ObjectLiteral[] =
2,712✔
2399
                await this.query(dropTablesQuery)
2,712✔
2400
            await Promise.all(
2,712✔
2401
                dropTableQueries.map((query) => this.query(query["query"])),
2,712✔
2402
            )
2,712✔
2403
            if (!isAnotherTransactionActive) await this.commitTransaction()
2,712✔
2404
        } catch (error) {
2,712!
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
    }
2,712✔
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())
2,840✔
2420
        if (!hasTable) {
2,840✔
2421
            return []
2,768✔
2422
        }
2,768✔
2423

72✔
2424
        if (!viewNames) {
2,840!
2425
            viewNames = []
×
2426
        }
×
2427

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

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

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

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

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

20✔
2454
            const view = new View()
20✔
2455
            view.database =
20✔
2456
                parsedName.database || dbView["database"] || currentDatabase
20!
2457
            view.schema = parsedName.schema || dbView["schema"] || currentSchema
20!
2458
            view.name = parsedName.tableName
20✔
2459
            view.expression = dbView["value"]
20✔
2460
            view.materialized =
20✔
2461
                dbView["type"] === MetadataTableType.MATERIALIZED_VIEW
20✔
2462
            return view
20✔
2463
        })
72✔
2464
    }
72✔
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) {
3,364✔
2472
            return []
12✔
2473
        }
12✔
2474

3,352✔
2475
        const dbTables: { TABLE_NAME: string; OWNER: string }[] = []
3,352✔
2476

3,352✔
2477
        const currentSchema = await this.getCurrentSchema()
3,352✔
2478
        const currentDatabase = await this.getCurrentDatabase()
3,352✔
2479

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3,398✔
2759
                            if (dbColumn["VIRTUAL_COLUMN"] === "YES") {
3,398✔
2760
                                tableColumn.generatedType = "VIRTUAL"
70✔
2761

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

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

3,398✔
2780
                            return tableColumn
3,398✔
2781
                        }),
938✔
2782
                )
938✔
2783

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

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

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

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

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

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

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

4✔
2896
                        if (!referencedDbColumn) return acc
4!
2897

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

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

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

938✔
2931
                return table
938✔
2932
            }),
618✔
2933
        )
618✔
2934
    }
618✔
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
9,482✔
2943
            .map((column) => this.buildCreateColumnSql(column))
9,482✔
2944
            .join(", ")
9,482✔
2945
        let sql = `CREATE TABLE ${this.escapePath(table)} (${columnDefinitions}`
9,482✔
2946

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

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

1,212✔
2983
            sql += `, ${uniquesSql}`
1,212✔
2984
        }
1,212✔
2985

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

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

9,482✔
3002
        if (table.foreignKeys.length > 0 && createForeignKeys) {
9,482✔
3003
            const foreignKeysSql = table.foreignKeys
10✔
3004
                .map((fk) => {
10✔
3005
                    const columnNames = fk.columnNames
12✔
3006
                        .map((columnName) => `"${columnName}"`)
12✔
3007
                        .join(", ")
12✔
3008
                    if (!fk.name)
12✔
3009
                        fk.name = this.connection.namingStrategy.foreignKeyName(
12✔
3010
                            table,
8✔
3011
                            fk.columnNames,
8✔
3012
                            this.getTablePath(fk),
8✔
3013
                            fk.referencedColumnNames,
8✔
3014
                        )
8✔
3015
                    const referencedColumnNames = fk.referencedColumnNames
12✔
3016
                        .map((columnName) => `"${columnName}"`)
12✔
3017
                        .join(", ")
12✔
3018
                    let constraint = `CONSTRAINT "${
12✔
3019
                        fk.name
12✔
3020
                    }" FOREIGN KEY (${columnNames}) REFERENCES ${this.escapePath(
12✔
3021
                        this.getTablePath(fk),
12✔
3022
                    )} (${referencedColumnNames})`
12✔
3023
                    if (fk.onDelete && fk.onDelete !== "NO ACTION") {
12!
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
12✔
3028
                })
10✔
3029
                .join(", ")
10✔
3030

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

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

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

9,482✔
3051
        sql += `)`
9,482✔
3052

9,482✔
3053
        return new Query(sql)
9,482✔
3054
    }
9,482✔
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,
9,482✔
3063
        ifExists?: boolean,
9,482✔
3064
    ): Query {
9,482✔
3065
        const query = ifExists
9,482✔
3066
            ? `DROP TABLE IF EXISTS ${this.escapePath(tableOrName)}`
9,482!
3067
            : `DROP TABLE ${this.escapePath(tableOrName)}`
9,482✔
3068
        return new Query(query)
9,482✔
3069
    }
9,482✔
3070

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

28✔
3088
    protected insertViewDefinitionSql(view: View): Query {
28✔
3089
        const expression =
60✔
3090
            typeof view.expression === "string"
60✔
3091
                ? view.expression.trim()
60✔
3092
                : view.expression(this.connection).getQuery()
60✔
3093
        const type = view.materialized
60✔
3094
            ? MetadataTableType.MATERIALIZED_VIEW
60✔
3095
            : MetadataTableType.VIEW
60✔
3096
        const { schema, tableName } = this.driver.parseTableName(view)
60✔
3097
        return this.insertTypeormMetadataSql({
60✔
3098
            type: type,
60✔
3099
            name: tableName,
60✔
3100
            schema: schema,
60✔
3101
            value: expression,
60✔
3102
        })
60✔
3103
    }
60✔
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 " : ""
60✔
3111
        return new Query(
60✔
3112
            `DROP ${materializedClause}VIEW ${this.escapePath(view)}`,
60✔
3113
        )
60✔
3114
    }
60✔
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
60✔
3122
            ? MetadataTableType.MATERIALIZED_VIEW
60✔
3123
            : MetadataTableType.VIEW
60✔
3124
        return this.deleteTypeormMetadataSql({ type, name: view.name })
60✔
3125
    }
60✔
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
3,178✔
3134
            .map((columnName) => `"${columnName}"`)
3,178✔
3135
            .join(", ")
3,178✔
3136
        return new Query(
3,178✔
3137
            `CREATE ${index.isUnique ? "UNIQUE " : ""}INDEX "${
3,178✔
3138
                index.name
3,178✔
3139
            }" ON ${this.escapePath(table)} (${columns})`,
3,178✔
3140
        )
3,178✔
3141
    }
3,178✔
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)
3,178✔
3149
            ? indexOrName.name
3,178✔
3150
            : indexOrName
3,178!
3151
        return new Query(`DROP INDEX "${indexName}"`)
3,178✔
3152
    }
3,178✔
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,
12✔
3162
        columnNames: string[],
12✔
3163
        constraintName?: string,
12✔
3164
    ): Query {
12✔
3165
        const primaryKeyName = constraintName
12✔
3166
            ? constraintName
12✔
3167
            : this.connection.namingStrategy.primaryKeyName(table, columnNames)
12✔
3168

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

12✔
3173
        return new Query(
12✔
3174
            `ALTER TABLE ${this.escapePath(
12✔
3175
                table,
12✔
3176
            )} ADD CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNamesString})`,
12✔
3177
        )
12✔
3178
    }
12✔
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)
12✔
3186
            throw new TypeORMError(`Table ${table} has no primary keys.`)
12!
3187

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

12✔
3194
        return new Query(
12✔
3195
            `ALTER TABLE ${this.escapePath(
12✔
3196
                table,
12✔
3197
            )} DROP CONSTRAINT "${primaryKeyName}"`,
12✔
3198
        )
12✔
3199
    }
12✔
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,
38✔
3208
        uniqueConstraint: TableUnique,
38✔
3209
    ): Query {
38✔
3210
        const columnNames = uniqueConstraint.columnNames
38✔
3211
            .map((column) => `"` + column + `"`)
38✔
3212
            .join(", ")
38✔
3213
        return new Query(
38✔
3214
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
38✔
3215
                uniqueConstraint.name
38✔
3216
            }" UNIQUE (${columnNames})`,
38✔
3217
        )
38✔
3218
    }
38✔
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,
38✔
3227
        uniqueOrName: TableUnique | string,
38✔
3228
    ): Query {
38✔
3229
        const uniqueName = InstanceChecker.isTableUnique(uniqueOrName)
38✔
3230
            ? uniqueOrName.name
38✔
3231
            : uniqueOrName
38!
3232
        return new Query(
38✔
3233
            `ALTER TABLE ${this.escapePath(
38✔
3234
                table,
38✔
3235
            )} DROP CONSTRAINT "${uniqueName}"`,
38✔
3236
        )
38✔
3237
    }
38✔
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,
20✔
3246
        checkConstraint: TableCheck,
20✔
3247
    ): Query {
20✔
3248
        return new Query(
20✔
3249
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
20✔
3250
                checkConstraint.name
20✔
3251
            }" CHECK (${checkConstraint.expression})`,
20✔
3252
        )
20✔
3253
    }
20✔
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,
20✔
3262
        checkOrName: TableCheck | string,
20✔
3263
    ): Query {
20✔
3264
        const checkName = InstanceChecker.isTableCheck(checkOrName)
20✔
3265
            ? checkOrName.name
20✔
3266
            : checkOrName
20!
3267
        return new Query(
20✔
3268
            `ALTER TABLE ${this.escapePath(
20✔
3269
                table,
20✔
3270
            )} DROP CONSTRAINT "${checkName}"`,
20✔
3271
        )
20✔
3272
    }
20✔
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,
5,936✔
3281
        foreignKey: TableForeignKey,
5,936✔
3282
    ): Query {
5,936✔
3283
        const columnNames = foreignKey.columnNames
5,936✔
3284
            .map((column) => `"` + column + `"`)
5,936✔
3285
            .join(", ")
5,936✔
3286
        const referencedColumnNames = foreignKey.referencedColumnNames
5,936✔
3287
            .map((column) => `"` + column + `"`)
5,936✔
3288
            .join(",")
5,936✔
3289
        let sql =
5,936✔
3290
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
5,936✔
3291
                foreignKey.name
5,936✔
3292
            }" FOREIGN KEY (${columnNames}) ` +
5,936✔
3293
            `REFERENCES ${this.escapePath(
5,936✔
3294
                this.getTablePath(foreignKey),
5,936✔
3295
            )} (${referencedColumnNames})`
5,936✔
3296
        // Oracle does not support NO ACTION, but we set NO ACTION by default in EntityMetadata
5,936✔
3297
        if (foreignKey.onDelete && foreignKey.onDelete !== "NO ACTION") {
5,936✔
3298
            sql += ` ON DELETE ${foreignKey.onDelete}`
2,000✔
3299
        }
2,000✔
3300
        return new Query(sql)
5,936✔
3301
    }
5,936✔
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,
5,948✔
3310
        foreignKeyOrName: TableForeignKey | string,
5,948✔
3311
    ): Query {
5,948✔
3312
        const foreignKeyName = InstanceChecker.isTableForeignKey(
5,948✔
3313
            foreignKeyOrName,
5,948✔
3314
        )
5,948✔
3315
            ? foreignKeyOrName.name
5,948✔
3316
            : foreignKeyOrName
5,948!
3317
        return new Query(
5,948✔
3318
            `ALTER TABLE ${this.escapePath(
5,948✔
3319
                table,
5,948✔
3320
            )} DROP CONSTRAINT "${foreignKeyName}"`,
5,948✔
3321
        )
5,948✔
3322
    }
5,948✔
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 =
30,096✔
3330
            `"${column.name}" ` + this.connection.driver.createFullType(column)
30,096✔
3331
        if (column.charset) c += " CHARACTER SET " + column.charset
30,096!
3332
        if (column.collation) c += " COLLATE " + column.collation
30,096!
3333

30,096✔
3334
        if (column.asExpression) c += ` AS (${column.asExpression}) VIRTUAL`
30,096✔
3335

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

30,096✔
3348
        return c
30,096✔
3349
    }
30,096✔
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
40,940✔
3357
        const { schema, tableName } = this.driver.parseTableName(target)
40,940✔
3358

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

40,940✔
3363
        return `"${tableName}"`
40,940✔
3364
    }
40,940✔
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