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

typeorm / typeorm / 22831820825

08 Mar 2026 10:57PM UTC coverage: 74.734% (+0.07%) from 74.664%
22831820825

Pull #12121

github

web-flow
Merge 1a6f8d65b into cd72d2a03
Pull Request #12121: feat(QueryRunner): add ifExists parameter to all drop methods

26613 of 32154 branches covered (82.77%)

Branch coverage included in aggregate %.

631 of 907 new or added lines in 11 files covered. (69.57%)

5 existing lines in 4 files now uncovered.

84313 of 116273 relevant lines covered (72.51%)

65684.32 hits per line

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

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

23✔
32
/**
23✔
33
 * Runs queries on a single postgres database connection.
23✔
34
 */
23✔
35
export class PostgresQueryRunner
23✔
36
    extends BaseQueryRunner
23✔
37
    implements QueryRunner
23✔
38
{
23✔
39
    // -------------------------------------------------------------------------
23✔
40
    // Public Implemented Properties
23✔
41
    // -------------------------------------------------------------------------
23✔
42

23✔
43
    /**
23✔
44
     * Database driver used by connection.
23✔
45
     */
23✔
46
    driver: PostgresDriver
23✔
47

23✔
48
    // -------------------------------------------------------------------------
23✔
49
    // Protected Properties
23✔
50
    // -------------------------------------------------------------------------
23✔
51

23✔
52
    /**
23✔
53
     * Promise used to obtain a database connection for a first time.
23✔
54
     */
23✔
55
    protected databaseConnectionPromise: Promise<any>
23✔
56

23✔
57
    /**
23✔
58
     * Special callback provided by a driver used to release a created connection.
23✔
59
     */
23✔
60
    protected releaseCallback?: (err: any) => void
23✔
61

23✔
62
    // -------------------------------------------------------------------------
23✔
63
    // Constructor
23✔
64
    // -------------------------------------------------------------------------
23✔
65

23✔
66
    constructor(driver: PostgresDriver, mode: ReplicationMode) {
23✔
67
        super()
60,988✔
68
        this.driver = driver
60,988✔
69
        this.connection = driver.connection
60,988✔
70
        this.mode = mode
60,988✔
71
        this.broadcaster = new Broadcaster(this)
60,988✔
72
    }
60,988✔
73

23✔
74
    // -------------------------------------------------------------------------
23✔
75
    // Public Methods
23✔
76
    // -------------------------------------------------------------------------
23✔
77

23✔
78
    /**
23✔
79
     * Creates/uses database connection from the connection pool to perform further operations.
23✔
80
     * Returns obtained database connection.
23✔
81
     */
23✔
82
    connect(): Promise<any> {
23✔
83
        if (this.databaseConnection)
325,448✔
84
            return Promise.resolve(this.databaseConnection)
325,448✔
85

58,476✔
86
        if (this.databaseConnectionPromise)
58,476✔
87
            return this.databaseConnectionPromise
325,448✔
88

58,220✔
89
        if (this.mode === "slave" && this.driver.isReplicated) {
325,448✔
90
            this.databaseConnectionPromise = this.driver
12✔
91
                .obtainSlaveConnection()
12✔
92
                .then(([connection, release]: any[]) => {
12✔
93
                    this.driver.connectedQueryRunners.push(this)
12✔
94
                    this.databaseConnection = connection
12✔
95

12✔
96
                    const onErrorCallback = (err: Error) =>
12✔
97
                        this.releasePostgresConnection(err)
×
98
                    this.releaseCallback = (err?: Error) => {
12✔
99
                        this.databaseConnection.removeListener(
12✔
100
                            "error",
12✔
101
                            onErrorCallback,
12✔
102
                        )
12✔
103
                        release(err)
12✔
104
                    }
12✔
105
                    this.databaseConnection.on("error", onErrorCallback)
12✔
106

12✔
107
                    return this.databaseConnection
12✔
108
                })
12✔
109
        } else {
325,448✔
110
            // master
58,208✔
111
            this.databaseConnectionPromise = this.driver
58,208✔
112
                .obtainMasterConnection()
58,208✔
113
                .then(([connection, release]: any[]) => {
58,208✔
114
                    this.driver.connectedQueryRunners.push(this)
58,204✔
115
                    this.databaseConnection = connection
58,204✔
116

58,204✔
117
                    const onErrorCallback = (err: Error) =>
58,204✔
118
                        this.releasePostgresConnection(err)
×
119
                    this.releaseCallback = (err?: Error) => {
58,204✔
120
                        this.databaseConnection.removeListener(
58,204✔
121
                            "error",
58,204✔
122
                            onErrorCallback,
58,204✔
123
                        )
58,204✔
124
                        release(err)
58,204✔
125
                    }
58,204✔
126
                    this.databaseConnection.on("error", onErrorCallback)
58,204✔
127

58,204✔
128
                    return this.databaseConnection
58,204✔
129
                })
58,208✔
130
        }
58,208✔
131

58,220✔
132
        return this.databaseConnectionPromise
58,220✔
133
    }
58,220✔
134

23✔
135
    /**
23✔
136
     * Release a connection back to the pool, optionally specifying an Error to release with.
23✔
137
     * Per pg-pool documentation this will prevent the pool from re-using the broken connection.
23✔
138
     * @param err
23✔
139
     */
23✔
140
    private async releasePostgresConnection(err?: Error) {
23✔
141
        if (this.isReleased) {
60,988!
142
            return
×
143
        }
×
144

60,988✔
145
        this.isReleased = true
60,988✔
146
        if (this.releaseCallback) {
60,988✔
147
            this.releaseCallback(err)
58,216✔
148
            this.releaseCallback = undefined
58,216✔
149
        }
58,216✔
150

60,988✔
151
        const index = this.driver.connectedQueryRunners.indexOf(this)
60,988✔
152

60,988✔
153
        if (index !== -1) {
60,988✔
154
            this.driver.connectedQueryRunners.splice(index, 1)
58,216✔
155
        }
58,216✔
156
    }
60,988✔
157

23✔
158
    /**
23✔
159
     * Releases used database connection.
23✔
160
     * You cannot use query runner methods once its released.
23✔
161
     */
23✔
162
    release(): Promise<void> {
23✔
163
        return this.releasePostgresConnection()
60,988✔
164
    }
60,988✔
165

23✔
166
    /**
23✔
167
     * Starts transaction.
23✔
168
     * @param isolationLevel
23✔
169
     */
23✔
170
    async startTransaction(isolationLevel?: IsolationLevel): Promise<void> {
23✔
171
        this.isTransactionActive = true
43,904✔
172
        try {
43,904✔
173
            await this.broadcaster.broadcast("BeforeTransactionStart")
43,904✔
174
        } catch (err) {
43,904!
175
            this.isTransactionActive = false
×
176
            throw err
×
177
        }
×
178

43,904✔
179
        if (this.transactionDepth === 0) {
43,904✔
180
            await this.query("START TRANSACTION")
43,840✔
181
            if (isolationLevel) {
43,840✔
182
                await this.query(
28✔
183
                    "SET TRANSACTION ISOLATION LEVEL " + isolationLevel,
28✔
184
                )
28✔
185
            }
28✔
186
        } else {
43,904✔
187
            await this.query(`SAVEPOINT typeorm_${this.transactionDepth}`)
64✔
188
        }
64✔
189
        this.transactionDepth += 1
43,904✔
190

43,904✔
191
        await this.broadcaster.broadcast("AfterTransactionStart")
43,904✔
192
    }
43,904✔
193

23✔
194
    /**
23✔
195
     * Commits transaction.
23✔
196
     * Error will be thrown if transaction was not started.
23✔
197
     */
23✔
198
    async commitTransaction(): Promise<void> {
23✔
199
        if (!this.isTransactionActive) throw new TransactionNotStartedError()
43,752!
200

43,752✔
201
        await this.broadcaster.broadcast("BeforeTransactionCommit")
43,752✔
202

43,752✔
203
        if (this.transactionDepth > 1) {
43,752✔
204
            await this.query(
40✔
205
                `RELEASE SAVEPOINT typeorm_${this.transactionDepth - 1}`,
40✔
206
            )
40✔
207
        } else {
43,752✔
208
            await this.query("COMMIT")
43,712✔
209
            this.isTransactionActive = false
43,704✔
210
        }
43,704✔
211
        this.transactionDepth -= 1
43,744✔
212

43,744✔
213
        await this.broadcaster.broadcast("AfterTransactionCommit")
43,744✔
214
    }
43,744✔
215

23✔
216
    /**
23✔
217
     * Rollbacks transaction.
23✔
218
     * Error will be thrown if transaction was not started.
23✔
219
     */
23✔
220
    async rollbackTransaction(): Promise<void> {
23✔
221
        if (!this.isTransactionActive) throw new TransactionNotStartedError()
156!
222

156✔
223
        await this.broadcaster.broadcast("BeforeTransactionRollback")
156✔
224

156✔
225
        if (this.transactionDepth > 1) {
156✔
226
            await this.query(
24✔
227
                `ROLLBACK TO SAVEPOINT typeorm_${this.transactionDepth - 1}`,
24✔
228
            )
24✔
229
        } else {
156✔
230
            await this.query("ROLLBACK")
132✔
231
            this.isTransactionActive = false
132✔
232
        }
132✔
233
        this.transactionDepth -= 1
156✔
234

156✔
235
        await this.broadcaster.broadcast("AfterTransactionRollback")
156✔
236
    }
156✔
237

23✔
238
    /**
23✔
239
     * Executes a given SQL query.
23✔
240
     * @param query
23✔
241
     * @param parameters
23✔
242
     * @param useStructuredResult
23✔
243
     */
23✔
244
    async query(
23✔
245
        query: string,
325,416✔
246
        parameters?: any[],
325,416✔
247
        useStructuredResult: boolean = false,
325,416✔
248
    ): Promise<any> {
325,416✔
249
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
325,416!
250

325,416✔
251
        const databaseConnection = await this.connect()
325,416✔
252

325,412✔
253
        this.driver.connection.logger.logQuery(query, parameters, this)
325,412✔
254
        await this.broadcaster.broadcast("BeforeQuery", query, parameters)
325,412✔
255

325,412✔
256
        const broadcasterResult = new BroadcasterResult()
325,412✔
257

325,412✔
258
        try {
325,412✔
259
            const queryStartTime = Date.now()
325,412✔
260
            const raw = await databaseConnection.query(query, parameters)
325,412✔
261
            // log slow queries if maxQueryExecution time is set
325,332✔
262
            const maxQueryExecutionTime =
325,332✔
263
                this.driver.options.maxQueryExecutionTime
325,332✔
264
            const queryEndTime = Date.now()
325,332✔
265
            const queryExecutionTime = queryEndTime - queryStartTime
325,332✔
266

325,332✔
267
            this.broadcaster.broadcastAfterQueryEvent(
325,332✔
268
                broadcasterResult,
325,332✔
269
                query,
325,332✔
270
                parameters,
325,332✔
271
                true,
325,332✔
272
                queryExecutionTime,
325,332✔
273
                raw,
325,332✔
274
                undefined,
325,332✔
275
            )
325,332✔
276

325,332✔
277
            if (
325,332✔
278
                maxQueryExecutionTime &&
325,332!
279
                queryExecutionTime > maxQueryExecutionTime
×
280
            )
325,416✔
281
                this.driver.connection.logger.logQuerySlow(
325,416!
282
                    queryExecutionTime,
×
283
                    query,
×
284
                    parameters,
×
285
                    this,
×
286
                )
×
287

325,332✔
288
            const result = new QueryResult()
325,332✔
289
            if (raw) {
325,332✔
290
                if (raw.hasOwnProperty("rows")) {
325,332✔
291
                    result.records = raw.rows
325,184✔
292
                }
325,184✔
293

325,332✔
294
                if (raw.hasOwnProperty("rowCount")) {
325,332✔
295
                    result.affected = raw.rowCount
325,184✔
296
                }
325,184✔
297

325,332✔
298
                switch (raw.command) {
325,332✔
299
                    case "DELETE":
325,332✔
300
                    case "UPDATE":
325,332✔
301
                        // for UPDATE and DELETE query additionally return number of affected rows
4,392✔
302
                        result.raw = [raw.rows, raw.rowCount]
4,392✔
303
                        break
4,392✔
304
                    default:
325,332✔
305
                        result.raw = raw.rows
320,940✔
306
                }
325,332✔
307

325,332✔
308
                if (!useStructuredResult) {
325,332✔
309
                    return result.raw
256,980✔
310
                }
256,980✔
311
            }
325,332✔
312

68,352✔
313
            return result
68,352✔
314
        } catch (err) {
325,416✔
315
            this.driver.connection.logger.logQueryError(
80✔
316
                err,
80✔
317
                query,
80✔
318
                parameters,
80✔
319
                this,
80✔
320
            )
80✔
321
            this.broadcaster.broadcastAfterQueryEvent(
80✔
322
                broadcasterResult,
80✔
323
                query,
80✔
324
                parameters,
80✔
325
                false,
80✔
326
                undefined,
80✔
327
                undefined,
80✔
328
                err,
80✔
329
            )
80✔
330

80✔
331
            throw new QueryFailedError(query, parameters, err)
80✔
332
        } finally {
325,416!
333
            await broadcasterResult.wait()
325,412✔
334
        }
325,412✔
335
    }
325,416✔
336

23✔
337
    /**
23✔
338
     * Returns raw data stream.
23✔
339
     * @param query
23✔
340
     * @param parameters
23✔
341
     * @param onEnd
23✔
342
     * @param onError
23✔
343
     */
23✔
344
    async stream(
23✔
345
        query: string,
8✔
346
        parameters?: any[],
8✔
347
        onEnd?: Function,
8✔
348
        onError?: Function,
8✔
349
    ): Promise<ReadStream> {
8✔
350
        const QueryStream = this.driver.loadStreamDependency()
8✔
351
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
8!
352

8✔
353
        const databaseConnection = await this.connect()
8✔
354
        this.driver.connection.logger.logQuery(query, parameters, this)
8✔
355
        const stream = databaseConnection.query(
8✔
356
            new QueryStream(query, parameters),
8✔
357
        )
8✔
358
        if (onEnd) stream.on("end", onEnd)
8✔
359
        if (onError) stream.on("error", onError)
8✔
360

8✔
361
        return stream
8✔
362
    }
8✔
363

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

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

23✔
380
    /**
23✔
381
     * Checks if database with the given name exist.
23✔
382
     * @param database
23✔
383
     */
23✔
384
    async hasDatabase(database: string): Promise<boolean> {
23✔
385
        const result = await this.query(
20✔
386
            `SELECT * FROM pg_database WHERE datname='${database}';`,
20✔
387
        )
20✔
388
        return result.length ? true : false
20✔
389
    }
20✔
390

23✔
391
    /**
23✔
392
     * Loads currently using database
23✔
393
     */
23✔
394
    async getCurrentDatabase(): Promise<string> {
23✔
395
        const query = await this.query(`SELECT * FROM current_database()`)
9,920✔
396
        return query[0]["current_database"]
9,920✔
397
    }
9,920✔
398

23✔
399
    /**
23✔
400
     * Checks if schema with the given name exist.
23✔
401
     * @param schema
23✔
402
     */
23✔
403
    async hasSchema(schema: string): Promise<boolean> {
23✔
404
        const result = await this.query(
12✔
405
            `SELECT * FROM "information_schema"."schemata" WHERE "schema_name" = '${schema}'`,
12✔
406
        )
12✔
407
        return result.length ? true : false
12✔
408
    }
12✔
409

23✔
410
    /**
23✔
411
     * Loads currently using database schema
23✔
412
     */
23✔
413
    async getCurrentSchema(): Promise<string> {
23✔
414
        const query = await this.query(`SELECT * FROM current_schema()`)
13,044✔
415
        return query[0]["current_schema"]
13,044✔
416
    }
13,044✔
417

23✔
418
    /**
23✔
419
     * Checks if table with the given name exist in the database.
23✔
420
     * @param tableOrName
23✔
421
     */
23✔
422
    async hasTable(tableOrName: Table | string): Promise<boolean> {
23✔
423
        const parsedTableName = this.driver.parseTableName(tableOrName)
8,648✔
424

8,648✔
425
        if (!parsedTableName.schema) {
8,648!
426
            parsedTableName.schema = await this.getCurrentSchema()
×
427
        }
×
428

8,648✔
429
        const sql = `SELECT * FROM "information_schema"."tables" WHERE "table_schema" = '${parsedTableName.schema}' AND "table_name" = '${parsedTableName.tableName}'`
8,648✔
430
        const result = await this.query(sql)
8,648✔
431
        return result.length ? true : false
8,648✔
432
    }
8,648✔
433

23✔
434
    /**
23✔
435
     * Checks if column with the given name exist in the given table.
23✔
436
     * @param tableOrName
23✔
437
     * @param columnName
23✔
438
     */
23✔
439
    async hasColumn(
23✔
440
        tableOrName: Table | string,
16✔
441
        columnName: string,
16✔
442
    ): Promise<boolean> {
16✔
443
        const parsedTableName = this.driver.parseTableName(tableOrName)
16✔
444

16✔
445
        if (!parsedTableName.schema) {
16!
446
            parsedTableName.schema = await this.getCurrentSchema()
×
447
        }
×
448

16✔
449
        const sql = `SELECT * FROM "information_schema"."columns" WHERE "table_schema" = '${parsedTableName.schema}' AND "table_name" = '${parsedTableName.tableName}' AND "column_name" = '${columnName}'`
16✔
450
        const result = await this.query(sql)
16✔
451
        return result.length ? true : false
16✔
452
    }
16✔
453

23✔
454
    /**
23✔
455
     * Creates a new database.
23✔
456
     * Note: Postgres does not support database creation inside a transaction block.
23✔
457
     * @param database
23✔
458
     * @param ifNotExists
23✔
459
     */
23✔
460
    async createDatabase(
23✔
461
        database: string,
8✔
462
        ifNotExists?: boolean,
8✔
463
    ): Promise<void> {
8✔
464
        if (ifNotExists) {
8✔
465
            const databaseAlreadyExists = await this.hasDatabase(database)
8✔
466

8✔
467
            if (databaseAlreadyExists) return Promise.resolve()
8!
468
        }
8✔
469

8✔
470
        const up = `CREATE DATABASE "${database}"`
8✔
471
        const down = `DROP DATABASE "${database}"`
8✔
472
        await this.executeQueries(new Query(up), new Query(down))
8✔
473
    }
8✔
474

23✔
475
    /**
23✔
476
     * Drops database.
23✔
477
     * Note: Postgres does not support database dropping inside a transaction block.
23✔
478
     * @param database
23✔
479
     * @param ifExists
23✔
480
     */
23✔
481
    async dropDatabase(database: string, ifExists?: boolean): Promise<void> {
23✔
482
        const up = ifExists
16✔
483
            ? `DROP DATABASE IF EXISTS "${database}"`
16✔
484
            : `DROP DATABASE "${database}"`
16✔
485
        const down = `CREATE DATABASE "${database}"`
16✔
486
        await this.executeQueries(new Query(up), new Query(down))
16✔
487
    }
16✔
488

23✔
489
    /**
23✔
490
     * Creates a new table schema.
23✔
491
     * @param schemaPath
23✔
492
     * @param ifNotExists
23✔
493
     */
23✔
494
    async createSchema(
23✔
495
        schemaPath: string,
160✔
496
        ifNotExists?: boolean,
160✔
497
    ): Promise<void> {
160✔
498
        const schema =
160✔
499
            schemaPath.indexOf(".") === -1
160✔
500
                ? schemaPath
160✔
501
                : schemaPath.split(".")[1]
160!
502

160✔
503
        const up = ifNotExists
160✔
504
            ? `CREATE SCHEMA IF NOT EXISTS "${schema}"`
160✔
505
            : `CREATE SCHEMA "${schema}"`
160✔
506
        const down = `DROP SCHEMA "${schema}" CASCADE`
160✔
507
        await this.executeQueries(new Query(up), new Query(down))
160✔
508
    }
160✔
509

23✔
510
    /**
23✔
511
     * Drops table schema.
23✔
512
     * @param schemaPath
23✔
513
     * @param ifExists
23✔
514
     * @param isCascade
23✔
515
     */
23✔
516
    async dropSchema(
23✔
517
        schemaPath: string,
104✔
518
        ifExists?: boolean,
104✔
519
        isCascade?: boolean,
104✔
520
    ): Promise<void> {
104✔
521
        const schema =
104✔
522
            schemaPath.indexOf(".") === -1
104✔
523
                ? schemaPath
104✔
524
                : schemaPath.split(".")[1]
104!
525

104✔
526
        const up = ifExists
104✔
527
            ? `DROP SCHEMA IF EXISTS "${schema}" ${isCascade ? "CASCADE" : ""}`
104✔
528
            : `DROP SCHEMA "${schema}" ${isCascade ? "CASCADE" : ""}`
104!
529
        const down = `CREATE SCHEMA "${schema}"`
104✔
530
        await this.executeQueries(new Query(up), new Query(down))
104✔
531
    }
104✔
532

23✔
533
    /**
23✔
534
     * Creates a new table.
23✔
535
     * @param table
23✔
536
     * @param ifNotExists
23✔
537
     * @param createForeignKeys
23✔
538
     * @param createIndices
23✔
539
     */
23✔
540
    async createTable(
23✔
541
        table: Table,
24,436✔
542
        ifNotExists: boolean = false,
24,436✔
543
        createForeignKeys: boolean = true,
24,436✔
544
        createIndices: boolean = true,
24,436✔
545
    ): Promise<void> {
24,436✔
546
        if (ifNotExists) {
24,436✔
547
            const isTableExist = await this.hasTable(table)
204✔
548
            if (isTableExist) return Promise.resolve()
204✔
549
        }
204✔
550
        const upQueries: Query[] = []
24,412✔
551
        const downQueries: Query[] = []
24,412✔
552

24,412✔
553
        // if table have column with ENUM type, we must create this type in postgres.
24,412✔
554
        const enumColumns = table.columns.filter(
24,412✔
555
            (column) => column.type === "enum" || column.type === "simple-enum",
24,412✔
556
        )
24,412✔
557
        const createdEnumTypes: string[] = []
24,412✔
558
        for (const column of enumColumns) {
24,436✔
559
            // TODO: Should also check if values of existing type matches expected ones
1,000✔
560
            const hasEnum = await this.hasEnumType(table, column)
1,000✔
561
            const enumName = this.buildEnumName(table, column)
1,000✔
562

1,000✔
563
            // if enum with the same "enumName" is defined more then once, me must prevent double creation
1,000✔
564
            if (!hasEnum && createdEnumTypes.indexOf(enumName) === -1) {
1,000✔
565
                createdEnumTypes.push(enumName)
956✔
566
                upQueries.push(this.createEnumTypeSql(table, column, enumName))
956✔
567
                downQueries.push(this.dropEnumTypeSql(table, column, enumName))
956✔
568
            }
956✔
569
        }
1,000✔
570

24,412✔
571
        // if table have column with generated type, we must add the expression to the metadata table
24,412✔
572
        const generatedColumns = table.columns.filter(
24,412✔
573
            (column) =>
24,412✔
574
                column.generatedType === "STORED" && column.asExpression,
24,412✔
575
        )
24,412✔
576
        for (const column of generatedColumns) {
24,436✔
577
            const tableNameWithSchema = (
76✔
578
                await this.getTableNameWithSchema(table.name)
76✔
579
            ).split(".")
76✔
580
            const tableName = tableNameWithSchema[1]
76✔
581
            const schema = tableNameWithSchema[0]
76✔
582

76✔
583
            const insertQuery = this.insertTypeormMetadataSql({
76✔
584
                database: this.driver.database,
76✔
585
                schema,
76✔
586
                table: tableName,
76✔
587
                type: MetadataTableType.GENERATED_COLUMN,
76✔
588
                name: column.name,
76✔
589
                value: column.asExpression,
76✔
590
            })
76✔
591

76✔
592
            const deleteQuery = this.deleteTypeormMetadataSql({
76✔
593
                database: this.driver.database,
76✔
594
                schema,
76✔
595
                table: tableName,
76✔
596
                type: MetadataTableType.GENERATED_COLUMN,
76✔
597
                name: column.name,
76✔
598
            })
76✔
599

76✔
600
            upQueries.push(insertQuery)
76✔
601
            downQueries.push(deleteQuery)
76✔
602
        }
76✔
603

24,412✔
604
        upQueries.push(this.createTableSql(table, createForeignKeys))
24,412✔
605
        downQueries.push(this.dropTableSql(table))
24,412✔
606

24,412✔
607
        // if createForeignKeys is true, we must drop created foreign keys in down query.
24,412✔
608
        // createTable does not need separate method to create foreign keys, because it create fk's in the same query with table creation.
24,412✔
609
        if (createForeignKeys)
24,412✔
610
            table.foreignKeys.forEach((foreignKey) =>
24,436✔
611
                downQueries.push(this.dropForeignKeySql(table, foreignKey)),
400✔
612
            )
400✔
613

24,412✔
614
        if (createIndices) {
24,412✔
615
            table.indices.forEach((index) => {
24,412✔
616
                // new index may be passed without name. In this case we generate index name manually.
8,300✔
617
                if (!index.name)
8,300✔
618
                    index.name = this.connection.namingStrategy.indexName(
8,300✔
619
                        table,
12✔
620
                        index.columnNames,
12✔
621
                        index.where,
12✔
622
                    )
12✔
623
                upQueries.push(this.createIndexSql(table, index))
8,300✔
624
                downQueries.push(this.dropIndexSql(table, index))
8,300✔
625
            })
24,412✔
626
        }
24,412✔
627

24,412✔
628
        if (table.comment) {
24,436✔
629
            upQueries.push(
232✔
630
                new Query(
232✔
631
                    `COMMENT ON TABLE ${this.escapePath(table)}` +
232✔
632
                        ` IS ${this.escapeComment(table.comment)}`,
232✔
633
                ),
232✔
634
            )
232✔
635
            downQueries.push(
232✔
636
                new Query(
232✔
637
                    `COMMENT ON TABLE ${this.escapePath(table)}` + ` IS NULL`,
232✔
638
                ),
232✔
639
            )
232✔
640
        }
232✔
641

24,412✔
642
        await this.executeQueries(upQueries, downQueries)
24,412✔
643
    }
24,412✔
644

23✔
645
    /**
23✔
646
     * Drops the table.
23✔
647
     * @param target
23✔
648
     * @param ifExists
23✔
649
     * @param dropForeignKeys
23✔
650
     * @param dropIndices
23✔
651
     */
23✔
652
    async dropTable(
23✔
653
        target: Table | string,
68✔
654
        ifExists?: boolean,
68✔
655
        dropForeignKeys: boolean = true,
68✔
656
        dropIndices: boolean = true,
68✔
657
    ): Promise<void> {
68✔
658
        // It needs because if table does not exist and dropForeignKeys or dropIndices is true, we don't need
68✔
659
        // to perform drop queries for foreign keys and indices.
68✔
660
        if (ifExists) {
68✔
661
            const isTableExist = await this.hasTable(target)
20✔
662
            if (!isTableExist) return Promise.resolve()
20✔
663
        }
20✔
664

64✔
665
        // if dropTable called with dropForeignKeys = true, we must create foreign keys in down query.
64✔
666
        const createForeignKeys: boolean = dropForeignKeys
64✔
667
        const tablePath = this.getTablePath(target)
64✔
668
        const table = await this.getCachedTable(tablePath)
64✔
669
        const upQueries: Query[] = []
64✔
670
        const downQueries: Query[] = []
64✔
671

64✔
672
        if (dropIndices) {
64✔
673
            table.indices.forEach((index) => {
64✔
674
                upQueries.push(this.dropIndexSql(table, index))
4✔
675
                downQueries.push(this.createIndexSql(table, index))
4✔
676
            })
64✔
677
        }
64✔
678

64✔
679
        if (dropForeignKeys)
64✔
680
            table.foreignKeys.forEach((foreignKey) =>
68✔
681
                upQueries.push(this.dropForeignKeySql(table, foreignKey)),
48✔
682
            )
48✔
683

64✔
684
        upQueries.push(this.dropTableSql(table))
64✔
685
        downQueries.push(this.createTableSql(table, createForeignKeys))
64✔
686

64✔
687
        // if table had columns with generated type, we must remove the expression from the metadata table
64✔
688
        const generatedColumns = table.columns.filter(
64✔
689
            (column) => column.generatedType && column.asExpression,
64✔
690
        )
64✔
691
        for (const column of generatedColumns) {
68✔
692
            const tableNameWithSchema = (
12✔
693
                await this.getTableNameWithSchema(table.name)
12✔
694
            ).split(".")
12✔
695
            const tableName = tableNameWithSchema[1]
12✔
696
            const schema = tableNameWithSchema[0]
12✔
697

12✔
698
            const deleteQuery = this.deleteTypeormMetadataSql({
12✔
699
                database: this.driver.database,
12✔
700
                schema,
12✔
701
                table: tableName,
12✔
702
                type: MetadataTableType.GENERATED_COLUMN,
12✔
703
                name: column.name,
12✔
704
            })
12✔
705

12✔
706
            const insertQuery = this.insertTypeormMetadataSql({
12✔
707
                database: this.driver.database,
12✔
708
                schema,
12✔
709
                table: tableName,
12✔
710
                type: MetadataTableType.GENERATED_COLUMN,
12✔
711
                name: column.name,
12✔
712
                value: column.asExpression,
12✔
713
            })
12✔
714

12✔
715
            upQueries.push(deleteQuery)
12✔
716
            downQueries.push(insertQuery)
12✔
717
        }
12✔
718

64✔
719
        await this.executeQueries(upQueries, downQueries)
64✔
720
    }
64✔
721

23✔
722
    /**
23✔
723
     * Creates a new view.
23✔
724
     * @param view
23✔
725
     * @param syncWithMetadata
23✔
726
     */
23✔
727
    async createView(
23✔
728
        view: View,
152✔
729
        syncWithMetadata: boolean = false,
152✔
730
    ): Promise<void> {
152✔
731
        const upQueries: Query[] = []
152✔
732
        const downQueries: Query[] = []
152✔
733
        upQueries.push(this.createViewSql(view))
152✔
734
        if (syncWithMetadata)
152✔
735
            upQueries.push(await this.insertViewDefinitionSql(view))
152✔
736
        downQueries.push(this.dropViewSql(view))
152✔
737
        if (syncWithMetadata)
152✔
738
            downQueries.push(await this.deleteViewDefinitionSql(view))
152✔
739
        await this.executeQueries(upQueries, downQueries)
152✔
740
    }
152✔
741

23✔
742
    /**
23✔
743
     * Drops the view.
23✔
744
     * @param target
23✔
745
     * @param ifExists
23✔
746
     */
23✔
747
    async dropView(target: View | string, ifExists?: boolean): Promise<void> {
23✔
748
        const viewName = InstanceChecker.isView(target) ? target.name : target
24✔
749

24✔
750
        let view: View
24✔
751
        try {
24✔
752
            view = await this.getCachedView(viewName)
24✔
753
        } catch {
24✔
754
            if (ifExists) return
4✔
NEW
755
            throw new TypeORMError(`View "${viewName}" does not exist.`)
×
NEW
756
        }
×
757

20✔
758
        await this.executeQueries(
20✔
759
            [
20✔
760
                await this.deleteViewDefinitionSql(view),
20✔
761
                this.dropViewSql(view, ifExists),
20✔
762
            ],
20✔
763
            [
20✔
764
                await this.insertViewDefinitionSql(view),
20✔
765
                this.createViewSql(view),
20✔
766
            ],
20✔
767
        )
20✔
768
    }
20✔
769

23✔
770
    /**
23✔
771
     * Renames the given table.
23✔
772
     * @param oldTableOrName
23✔
773
     * @param newTableName
23✔
774
     */
23✔
775
    async renameTable(
23✔
776
        oldTableOrName: Table | string,
76✔
777
        newTableName: string,
76✔
778
    ): Promise<void> {
76✔
779
        const upQueries: Query[] = []
76✔
780
        const downQueries: Query[] = []
76✔
781
        const oldTable = InstanceChecker.isTable(oldTableOrName)
76✔
782
            ? oldTableOrName
76✔
783
            : await this.getCachedTable(oldTableOrName)
76✔
784
        const newTable = oldTable.clone()
60✔
785

60✔
786
        const { schema: schemaName, tableName: oldTableName } =
60✔
787
            this.driver.parseTableName(oldTable)
60✔
788

60✔
789
        newTable.name = schemaName
60✔
790
            ? `${schemaName}.${newTableName}`
76✔
791
            : newTableName
76!
792

76✔
793
        upQueries.push(
76✔
794
            new Query(
76✔
795
                `ALTER TABLE ${this.escapePath(
76✔
796
                    oldTable,
76✔
797
                )} RENAME TO "${newTableName}"`,
76✔
798
            ),
76✔
799
        )
76✔
800
        downQueries.push(
76✔
801
            new Query(
76✔
802
                `ALTER TABLE ${this.escapePath(
76✔
803
                    newTable,
76✔
804
                )} RENAME TO "${oldTableName}"`,
76✔
805
            ),
76✔
806
        )
76✔
807

76✔
808
        // rename column primary key constraint if it has default constraint name
76✔
809
        if (
76✔
810
            newTable.primaryColumns.length > 0 &&
76✔
811
            !newTable.primaryColumns[0].primaryKeyConstraintName
76✔
812
        ) {
76✔
813
            const columnNames = newTable.primaryColumns.map(
60✔
814
                (column) => column.name,
60✔
815
            )
60✔
816

60✔
817
            const oldPkName = this.connection.namingStrategy.primaryKeyName(
60✔
818
                oldTable,
60✔
819
                columnNames,
60✔
820
            )
60✔
821

60✔
822
            const newPkName = this.connection.namingStrategy.primaryKeyName(
60✔
823
                newTable,
60✔
824
                columnNames,
60✔
825
            )
60✔
826

60✔
827
            upQueries.push(
60✔
828
                new Query(
60✔
829
                    `ALTER TABLE ${this.escapePath(
60✔
830
                        newTable,
60✔
831
                    )} RENAME CONSTRAINT "${oldPkName}" TO "${newPkName}"`,
60✔
832
                ),
60✔
833
            )
60✔
834
            downQueries.push(
60✔
835
                new Query(
60✔
836
                    `ALTER TABLE ${this.escapePath(
60✔
837
                        newTable,
60✔
838
                    )} RENAME CONSTRAINT "${newPkName}" TO "${oldPkName}"`,
60✔
839
                ),
60✔
840
            )
60✔
841
        }
60✔
842

76✔
843
        // rename sequences
76✔
844
        newTable.columns.map((col) => {
76✔
845
            if (col.isGenerated && col.generationStrategy === "increment") {
192✔
846
                const sequencePath = this.buildSequencePath(oldTable, col.name)
52✔
847
                const sequenceName = this.buildSequenceName(oldTable, col.name)
52✔
848

52✔
849
                const newSequencePath = this.buildSequencePath(
52✔
850
                    newTable,
52✔
851
                    col.name,
52✔
852
                )
52✔
853
                const newSequenceName = this.buildSequenceName(
52✔
854
                    newTable,
52✔
855
                    col.name,
52✔
856
                )
52✔
857

52✔
858
                const up = `ALTER SEQUENCE ${this.escapePath(
52✔
859
                    sequencePath,
52✔
860
                )} RENAME TO "${newSequenceName}"`
52✔
861
                const down = `ALTER SEQUENCE ${this.escapePath(
52✔
862
                    newSequencePath,
52✔
863
                )} RENAME TO "${sequenceName}"`
52✔
864

52✔
865
                upQueries.push(new Query(up))
52✔
866
                downQueries.push(new Query(down))
52✔
867
            }
52✔
868
        })
76✔
869

76✔
870
        // rename unique constraints
76✔
871
        newTable.uniques.forEach((unique) => {
76✔
872
            const oldUniqueName =
28✔
873
                this.connection.namingStrategy.uniqueConstraintName(
28✔
874
                    oldTable,
28✔
875
                    unique.columnNames,
28✔
876
                )
28✔
877

28✔
878
            // Skip renaming if Unique has user defined constraint name
28✔
879
            if (unique.name !== oldUniqueName) return
28✔
880

12✔
881
            // build new constraint name
12✔
882
            const newUniqueName =
12✔
883
                this.connection.namingStrategy.uniqueConstraintName(
12✔
884
                    newTable,
12✔
885
                    unique.columnNames,
12✔
886
                )
12✔
887

12✔
888
            // build queries
12✔
889
            upQueries.push(
12✔
890
                new Query(
12✔
891
                    `ALTER TABLE ${this.escapePath(
12✔
892
                        newTable,
12✔
893
                    )} RENAME CONSTRAINT "${
12✔
894
                        unique.name
12✔
895
                    }" TO "${newUniqueName}"`,
12✔
896
                ),
12✔
897
            )
12✔
898
            downQueries.push(
12✔
899
                new Query(
12✔
900
                    `ALTER TABLE ${this.escapePath(
12✔
901
                        newTable,
12✔
902
                    )} RENAME CONSTRAINT "${newUniqueName}" TO "${
12✔
903
                        unique.name
12✔
904
                    }"`,
12✔
905
                ),
12✔
906
            )
12✔
907

12✔
908
            // replace constraint name
12✔
909
            unique.name = newUniqueName
12✔
910
        })
76✔
911

76✔
912
        // rename index constraints
76✔
913
        newTable.indices.forEach((index) => {
76✔
914
            const oldIndexName = this.connection.namingStrategy.indexName(
36✔
915
                oldTable,
36✔
916
                index.columnNames,
36✔
917
                index.where,
36✔
918
            )
36✔
919

36✔
920
            // Skip renaming if Index has user defined constraint name
36✔
921
            if (index.name !== oldIndexName) return
36✔
922

20✔
923
            // build new constraint name
20✔
924
            const { schema } = this.driver.parseTableName(newTable)
20✔
925
            const newIndexName = this.connection.namingStrategy.indexName(
20✔
926
                newTable,
20✔
927
                index.columnNames,
20✔
928
                index.where,
20✔
929
            )
20✔
930

20✔
931
            // build queries
20✔
932
            const up = schema
20✔
933
                ? `ALTER INDEX "${schema}"."${index.name}" RENAME TO "${newIndexName}"`
20✔
934
                : `ALTER INDEX "${index.name}" RENAME TO "${newIndexName}"`
36!
935
            const down = schema
36✔
936
                ? `ALTER INDEX "${schema}"."${newIndexName}" RENAME TO "${index.name}"`
36✔
937
                : `ALTER INDEX "${newIndexName}" RENAME TO "${index.name}"`
36!
938
            upQueries.push(new Query(up))
36✔
939
            downQueries.push(new Query(down))
36✔
940

36✔
941
            // replace constraint name
36✔
942
            index.name = newIndexName
36✔
943
        })
76✔
944

76✔
945
        // rename foreign key constraints
76✔
946
        newTable.foreignKeys.forEach((foreignKey) => {
76✔
947
            const oldForeignKeyName =
36✔
948
                this.connection.namingStrategy.foreignKeyName(
36✔
949
                    oldTable,
36✔
950
                    foreignKey.columnNames,
36✔
951
                    this.getTablePath(foreignKey),
36✔
952
                    foreignKey.referencedColumnNames,
36✔
953
                )
36✔
954

36✔
955
            // Skip renaming if foreign key has user defined constraint name
36✔
956
            if (foreignKey.name !== oldForeignKeyName) return
36✔
957

4✔
958
            // build new constraint name
4✔
959
            const newForeignKeyName =
4✔
960
                this.connection.namingStrategy.foreignKeyName(
4✔
961
                    newTable,
4✔
962
                    foreignKey.columnNames,
4✔
963
                    this.getTablePath(foreignKey),
4✔
964
                    foreignKey.referencedColumnNames,
4✔
965
                )
4✔
966

4✔
967
            // build queries
4✔
968
            upQueries.push(
4✔
969
                new Query(
4✔
970
                    `ALTER TABLE ${this.escapePath(
4✔
971
                        newTable,
4✔
972
                    )} RENAME CONSTRAINT "${
4✔
973
                        foreignKey.name
4✔
974
                    }" TO "${newForeignKeyName}"`,
4✔
975
                ),
4✔
976
            )
4✔
977
            downQueries.push(
4✔
978
                new Query(
4✔
979
                    `ALTER TABLE ${this.escapePath(
4✔
980
                        newTable,
4✔
981
                    )} RENAME CONSTRAINT "${newForeignKeyName}" TO "${
4✔
982
                        foreignKey.name
4✔
983
                    }"`,
4✔
984
                ),
4✔
985
            )
4✔
986

4✔
987
            // replace constraint name
4✔
988
            foreignKey.name = newForeignKeyName
4✔
989
        })
76✔
990

76✔
991
        // rename ENUM types
76✔
992
        const enumColumns = newTable.columns.filter(
76✔
993
            (column) => column.type === "enum" || column.type === "simple-enum",
76✔
994
        )
76✔
995
        for (const column of enumColumns) {
76✔
996
            // skip renaming for user-defined enum name
20✔
997
            if (column.enumName) continue
20✔
998

16✔
999
            const oldEnumType = await this.getUserDefinedTypeName(
16✔
1000
                oldTable,
16✔
1001
                column,
16✔
1002
            )
16✔
1003
            upQueries.push(
16✔
1004
                new Query(
16✔
1005
                    `ALTER TYPE "${oldEnumType.schema}"."${
16✔
1006
                        oldEnumType.name
16✔
1007
                    }" RENAME TO ${this.buildEnumName(
16✔
1008
                        newTable,
16✔
1009
                        column,
16✔
1010
                        false,
16✔
1011
                    )}`,
16✔
1012
                ),
16✔
1013
            )
16✔
1014
            downQueries.push(
16✔
1015
                new Query(
16✔
1016
                    `ALTER TYPE ${this.buildEnumName(
16✔
1017
                        newTable,
16✔
1018
                        column,
16✔
1019
                    )} RENAME TO "${oldEnumType.name}"`,
16✔
1020
                ),
16✔
1021
            )
16✔
1022
        }
16✔
1023
        await this.executeQueries(upQueries, downQueries)
76✔
1024
    }
76✔
1025

23✔
1026
    /**
23✔
1027
     * Creates a new column from the column in the table.
23✔
1028
     * @param tableOrName
23✔
1029
     * @param column
23✔
1030
     */
23✔
1031
    async addColumn(
23✔
1032
        tableOrName: Table | string,
152✔
1033
        column: TableColumn,
152✔
1034
    ): Promise<void> {
152✔
1035
        const table = InstanceChecker.isTable(tableOrName)
152✔
1036
            ? tableOrName
152✔
1037
            : await this.getCachedTable(tableOrName)
152✔
1038
        const clonedTable = table.clone()
16✔
1039
        const upQueries: Query[] = []
16✔
1040
        const downQueries: Query[] = []
16✔
1041

16✔
1042
        if (column.type === "enum" || column.type === "simple-enum") {
152✔
1043
            const hasEnum = await this.hasEnumType(table, column)
20✔
1044
            if (!hasEnum) {
20✔
1045
                upQueries.push(this.createEnumTypeSql(table, column))
20✔
1046
                downQueries.push(this.dropEnumTypeSql(table, column))
20✔
1047
            }
20✔
1048
        }
20✔
1049

152✔
1050
        upQueries.push(
152✔
1051
            new Query(
152✔
1052
                `ALTER TABLE ${this.escapePath(
152✔
1053
                    table,
152✔
1054
                )} ADD ${this.buildCreateColumnSql(table, column)}`,
152✔
1055
            ),
152✔
1056
        )
152✔
1057
        downQueries.push(
152✔
1058
            new Query(
152✔
1059
                `ALTER TABLE ${this.escapePath(table)} DROP COLUMN "${
152✔
1060
                    column.name
152✔
1061
                }"`,
152✔
1062
            ),
152✔
1063
        )
152✔
1064

152✔
1065
        // create or update primary key constraint
152✔
1066
        if (column.isPrimary) {
152✔
1067
            const primaryColumns = clonedTable.primaryColumns
12✔
1068
            // if table already have primary key, me must drop it and recreate again
12✔
1069
            if (primaryColumns.length > 0) {
12✔
1070
                const pkName = primaryColumns[0].primaryKeyConstraintName
4✔
1071
                    ? primaryColumns[0].primaryKeyConstraintName
4!
1072
                    : this.connection.namingStrategy.primaryKeyName(
4✔
1073
                          clonedTable,
4✔
1074
                          primaryColumns.map((column) => column.name),
4✔
1075
                      )
4✔
1076

4✔
1077
                const columnNames = primaryColumns
4✔
1078
                    .map((column) => `"${column.name}"`)
4✔
1079
                    .join(", ")
4✔
1080

4✔
1081
                upQueries.push(
4✔
1082
                    new Query(
4✔
1083
                        `ALTER TABLE ${this.escapePath(
4✔
1084
                            table,
4✔
1085
                        )} DROP CONSTRAINT "${pkName}"`,
4✔
1086
                    ),
4✔
1087
                )
4✔
1088
                downQueries.push(
4✔
1089
                    new Query(
4✔
1090
                        `ALTER TABLE ${this.escapePath(
4✔
1091
                            table,
4✔
1092
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
4✔
1093
                    ),
4✔
1094
                )
4✔
1095
            }
4✔
1096

12✔
1097
            primaryColumns.push(column)
12✔
1098
            const pkName = primaryColumns[0].primaryKeyConstraintName
12✔
1099
                ? primaryColumns[0].primaryKeyConstraintName
12!
1100
                : this.connection.namingStrategy.primaryKeyName(
12✔
1101
                      clonedTable,
12✔
1102
                      primaryColumns.map((column) => column.name),
12✔
1103
                  )
12✔
1104

12✔
1105
            const columnNames = primaryColumns
12✔
1106
                .map((column) => `"${column.name}"`)
12✔
1107
                .join(", ")
12✔
1108

12✔
1109
            upQueries.push(
12✔
1110
                new Query(
12✔
1111
                    `ALTER TABLE ${this.escapePath(
12✔
1112
                        table,
12✔
1113
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
12✔
1114
                ),
12✔
1115
            )
12✔
1116
            downQueries.push(
12✔
1117
                new Query(
12✔
1118
                    `ALTER TABLE ${this.escapePath(
12✔
1119
                        table,
12✔
1120
                    )} DROP CONSTRAINT "${pkName}"`,
12✔
1121
                ),
12✔
1122
            )
12✔
1123
        }
12✔
1124

152✔
1125
        // create column index
152✔
1126
        const columnIndex = clonedTable.indices.find(
152✔
1127
            (index) =>
152✔
1128
                index.columnNames.length === 1 &&
4✔
1129
                index.columnNames[0] === column.name,
152✔
1130
        )
152✔
1131
        if (columnIndex) {
152!
1132
            upQueries.push(this.createIndexSql(table, columnIndex))
×
1133
            downQueries.push(this.dropIndexSql(table, columnIndex))
×
1134
        }
×
1135

152✔
1136
        // create unique constraint
152✔
1137
        if (column.isUnique) {
152✔
1138
            const uniqueConstraint = new TableUnique({
12✔
1139
                name: this.connection.namingStrategy.uniqueConstraintName(
12✔
1140
                    table,
12✔
1141
                    [column.name],
12✔
1142
                ),
12✔
1143
                columnNames: [column.name],
12✔
1144
            })
12✔
1145
            clonedTable.uniques.push(uniqueConstraint)
12✔
1146
            upQueries.push(
12✔
1147
                new Query(
12✔
1148
                    `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
12✔
1149
                        uniqueConstraint.name
12✔
1150
                    }" UNIQUE ("${column.name}")`,
12✔
1151
                ),
12✔
1152
            )
12✔
1153
            downQueries.push(
12✔
1154
                new Query(
12✔
1155
                    `ALTER TABLE ${this.escapePath(table)} DROP CONSTRAINT "${
12✔
1156
                        uniqueConstraint.name
12✔
1157
                    }"`,
12✔
1158
                ),
12✔
1159
            )
12✔
1160
        }
12✔
1161

152✔
1162
        if (column.generatedType === "STORED" && column.asExpression) {
152✔
1163
            const tableNameWithSchema = (
24✔
1164
                await this.getTableNameWithSchema(table.name)
24✔
1165
            ).split(".")
24✔
1166
            const tableName = tableNameWithSchema[1]
24✔
1167
            const schema = tableNameWithSchema[0]
24✔
1168

24✔
1169
            const insertQuery = this.insertTypeormMetadataSql({
24✔
1170
                database: this.driver.database,
24✔
1171
                schema,
24✔
1172
                table: tableName,
24✔
1173
                type: MetadataTableType.GENERATED_COLUMN,
24✔
1174
                name: column.name,
24✔
1175
                value: column.asExpression,
24✔
1176
            })
24✔
1177

24✔
1178
            const deleteQuery = this.deleteTypeormMetadataSql({
24✔
1179
                database: this.driver.database,
24✔
1180
                schema,
24✔
1181
                table: tableName,
24✔
1182
                type: MetadataTableType.GENERATED_COLUMN,
24✔
1183
                name: column.name,
24✔
1184
            })
24✔
1185

24✔
1186
            upQueries.push(insertQuery)
24✔
1187
            downQueries.push(deleteQuery)
24✔
1188
        }
24✔
1189

152✔
1190
        // create column's comment
152✔
1191
        if (column.comment) {
152!
1192
            upQueries.push(
×
1193
                new Query(
×
1194
                    `COMMENT ON COLUMN ${this.escapePath(table)}."${
×
1195
                        column.name
×
1196
                    }" IS ${this.escapeComment(column.comment)}`,
×
1197
                ),
×
1198
            )
×
1199
            downQueries.push(
×
1200
                new Query(
×
1201
                    `COMMENT ON COLUMN ${this.escapePath(table)}."${
×
1202
                        column.name
×
1203
                    }" IS ${this.escapeComment(column.comment)}`,
×
1204
                ),
×
1205
            )
×
1206
        }
×
1207

152✔
1208
        await this.executeQueries(upQueries, downQueries)
152✔
1209

148✔
1210
        clonedTable.addColumn(column)
148✔
1211
        this.replaceCachedTable(table, clonedTable)
148✔
1212
    }
148✔
1213

23✔
1214
    /**
23✔
1215
     * Creates a new columns from the column in the table.
23✔
1216
     * @param tableOrName
23✔
1217
     * @param columns
23✔
1218
     */
23✔
1219
    async addColumns(
23✔
1220
        tableOrName: Table | string,
16✔
1221
        columns: TableColumn[],
16✔
1222
    ): Promise<void> {
16✔
1223
        for (const column of columns) {
16✔
1224
            await this.addColumn(tableOrName, column)
20✔
1225
        }
20✔
1226
    }
16✔
1227

23✔
1228
    /**
23✔
1229
     * Renames column in the given table.
23✔
1230
     * @param tableOrName
23✔
1231
     * @param oldTableColumnOrName
23✔
1232
     * @param newTableColumnOrName
23✔
1233
     */
23✔
1234
    async renameColumn(
23✔
1235
        tableOrName: Table | string,
64✔
1236
        oldTableColumnOrName: TableColumn | string,
64✔
1237
        newTableColumnOrName: TableColumn | string,
64✔
1238
    ): Promise<void> {
64✔
1239
        const table = InstanceChecker.isTable(tableOrName)
64✔
1240
            ? tableOrName
64✔
1241
            : await this.getCachedTable(tableOrName)
64✔
1242
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
8✔
1243
            ? oldTableColumnOrName
64✔
1244
            : table.columns.find((c) => c.name === oldTableColumnOrName)
64✔
1245
        if (!oldColumn)
64✔
1246
            throw new TypeORMError(
64!
1247
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
×
1248
            )
×
1249

64✔
1250
        let newColumn
64✔
1251
        if (InstanceChecker.isTableColumn(newTableColumnOrName)) {
64✔
1252
            newColumn = newTableColumnOrName
44✔
1253
        } else {
64✔
1254
            newColumn = oldColumn.clone()
20✔
1255
            newColumn.name = newTableColumnOrName
20✔
1256
        }
20✔
1257

64✔
1258
        return this.changeColumn(table, oldColumn, newColumn)
64✔
1259
    }
64✔
1260

23✔
1261
    /**
23✔
1262
     * Changes a column in the table.
23✔
1263
     * @param tableOrName
23✔
1264
     * @param oldTableColumnOrName
23✔
1265
     * @param newColumn
23✔
1266
     */
23✔
1267
    async changeColumn(
23✔
1268
        tableOrName: Table | string,
508✔
1269
        oldTableColumnOrName: TableColumn | string,
508✔
1270
        newColumn: TableColumn,
508✔
1271
    ): Promise<void> {
508✔
1272
        const table = InstanceChecker.isTable(tableOrName)
508✔
1273
            ? tableOrName
508✔
1274
            : await this.getCachedTable(tableOrName)
508!
1275
        let clonedTable = table.clone()
×
1276
        const upQueries: Query[] = []
×
1277
        const downQueries: Query[] = []
×
1278
        let defaultValueChanged = false
×
1279

×
1280
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
×
1281
            ? oldTableColumnOrName
508✔
1282
            : table.columns.find(
508!
1283
                  (column) => column.name === oldTableColumnOrName,
×
1284
              )
508✔
1285
        if (!oldColumn)
508✔
1286
            throw new TypeORMError(
508!
1287
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
×
1288
            )
×
1289

508✔
1290
        if (
508✔
1291
            oldColumn.type !== newColumn.type ||
508✔
1292
            oldColumn.length !== newColumn.length ||
508✔
1293
            newColumn.isArray !== oldColumn.isArray ||
508✔
1294
            (!oldColumn.generatedType &&
420✔
1295
                newColumn.generatedType === "STORED") ||
508✔
1296
            (oldColumn.asExpression !== newColumn.asExpression &&
416✔
1297
                newColumn.generatedType === "STORED")
416✔
1298
        ) {
508✔
1299
            // To avoid data conversion, we just recreate column
100✔
1300
            await this.dropColumn(table, oldColumn)
100✔
1301
            await this.addColumn(table, newColumn)
100✔
1302

100✔
1303
            // update cloned table
100✔
1304
            clonedTable = table.clone()
100✔
1305
        } else {
508✔
1306
            if (oldColumn.name !== newColumn.name) {
408✔
1307
                // rename column
100✔
1308
                upQueries.push(
100✔
1309
                    new Query(
100✔
1310
                        `ALTER TABLE ${this.escapePath(table)} RENAME COLUMN "${
100✔
1311
                            oldColumn.name
100✔
1312
                        }" TO "${newColumn.name}"`,
100✔
1313
                    ),
100✔
1314
                )
100✔
1315
                downQueries.push(
100✔
1316
                    new Query(
100✔
1317
                        `ALTER TABLE ${this.escapePath(table)} RENAME COLUMN "${
100✔
1318
                            newColumn.name
100✔
1319
                        }" TO "${oldColumn.name}"`,
100✔
1320
                    ),
100✔
1321
                )
100✔
1322

100✔
1323
                // rename ENUM type
100✔
1324
                if (
100✔
1325
                    oldColumn.type === "enum" ||
100✔
1326
                    oldColumn.type === "simple-enum"
92✔
1327
                ) {
100✔
1328
                    const oldEnumType = await this.getUserDefinedTypeName(
8✔
1329
                        table,
8✔
1330
                        oldColumn,
8✔
1331
                    )
8✔
1332
                    upQueries.push(
8✔
1333
                        new Query(
8✔
1334
                            `ALTER TYPE "${oldEnumType.schema}"."${
8✔
1335
                                oldEnumType.name
8✔
1336
                            }" RENAME TO ${this.buildEnumName(
8✔
1337
                                table,
8✔
1338
                                newColumn,
8✔
1339
                                false,
8✔
1340
                            )}`,
8✔
1341
                        ),
8✔
1342
                    )
8✔
1343
                    downQueries.push(
8✔
1344
                        new Query(
8✔
1345
                            `ALTER TYPE ${this.buildEnumName(
8✔
1346
                                table,
8✔
1347
                                newColumn,
8✔
1348
                            )} RENAME TO "${oldEnumType.name}"`,
8✔
1349
                        ),
8✔
1350
                    )
8✔
1351
                }
8✔
1352

100✔
1353
                // rename column primary key constraint
100✔
1354
                if (
100✔
1355
                    oldColumn.isPrimary === true &&
100✔
1356
                    !oldColumn.primaryKeyConstraintName
28✔
1357
                ) {
100✔
1358
                    const primaryColumns = clonedTable.primaryColumns
20✔
1359

20✔
1360
                    // build old primary constraint name
20✔
1361
                    const columnNames = primaryColumns.map(
20✔
1362
                        (column) => column.name,
20✔
1363
                    )
20✔
1364
                    const oldPkName =
20✔
1365
                        this.connection.namingStrategy.primaryKeyName(
20✔
1366
                            clonedTable,
20✔
1367
                            columnNames,
20✔
1368
                        )
20✔
1369

20✔
1370
                    // replace old column name with new column name
20✔
1371
                    columnNames.splice(columnNames.indexOf(oldColumn.name), 1)
20✔
1372
                    columnNames.push(newColumn.name)
20✔
1373

20✔
1374
                    // build new primary constraint name
20✔
1375
                    const newPkName =
20✔
1376
                        this.connection.namingStrategy.primaryKeyName(
20✔
1377
                            clonedTable,
20✔
1378
                            columnNames,
20✔
1379
                        )
20✔
1380

20✔
1381
                    upQueries.push(
20✔
1382
                        new Query(
20✔
1383
                            `ALTER TABLE ${this.escapePath(
20✔
1384
                                table,
20✔
1385
                            )} RENAME CONSTRAINT "${oldPkName}" TO "${newPkName}"`,
20✔
1386
                        ),
20✔
1387
                    )
20✔
1388
                    downQueries.push(
20✔
1389
                        new Query(
20✔
1390
                            `ALTER TABLE ${this.escapePath(
20✔
1391
                                table,
20✔
1392
                            )} RENAME CONSTRAINT "${newPkName}" TO "${oldPkName}"`,
20✔
1393
                        ),
20✔
1394
                    )
20✔
1395
                }
20✔
1396

100✔
1397
                // rename column sequence
100✔
1398
                if (
100✔
1399
                    oldColumn.isGenerated === true &&
100!
1400
                    newColumn.generationStrategy === "increment"
×
1401
                ) {
100!
1402
                    const sequencePath = this.buildSequencePath(
×
1403
                        table,
×
1404
                        oldColumn.name,
×
1405
                    )
×
1406
                    const sequenceName = this.buildSequenceName(
×
1407
                        table,
×
1408
                        oldColumn.name,
×
1409
                    )
×
1410

×
1411
                    const newSequencePath = this.buildSequencePath(
×
1412
                        table,
×
1413
                        newColumn.name,
×
1414
                    )
×
1415
                    const newSequenceName = this.buildSequenceName(
×
1416
                        table,
×
1417
                        newColumn.name,
×
1418
                    )
×
1419

×
1420
                    const up = `ALTER SEQUENCE ${this.escapePath(
×
1421
                        sequencePath,
×
1422
                    )} RENAME TO "${newSequenceName}"`
×
1423
                    const down = `ALTER SEQUENCE ${this.escapePath(
×
1424
                        newSequencePath,
×
1425
                    )} RENAME TO "${sequenceName}"`
×
1426

×
1427
                    upQueries.push(new Query(up))
×
1428
                    downQueries.push(new Query(down))
×
1429
                }
×
1430

100✔
1431
                // rename unique constraints
100✔
1432
                clonedTable.findColumnUniques(oldColumn).forEach((unique) => {
100✔
1433
                    const oldUniqueName =
32✔
1434
                        this.connection.namingStrategy.uniqueConstraintName(
32✔
1435
                            clonedTable,
32✔
1436
                            unique.columnNames,
32✔
1437
                        )
32✔
1438

32✔
1439
                    // Skip renaming if Unique has user defined constraint name
32✔
1440
                    if (unique.name !== oldUniqueName) return
32✔
1441

16✔
1442
                    // build new constraint name
16✔
1443
                    unique.columnNames.splice(
16✔
1444
                        unique.columnNames.indexOf(oldColumn.name),
16✔
1445
                        1,
16✔
1446
                    )
16✔
1447
                    unique.columnNames.push(newColumn.name)
16✔
1448
                    const newUniqueName =
16✔
1449
                        this.connection.namingStrategy.uniqueConstraintName(
16✔
1450
                            clonedTable,
16✔
1451
                            unique.columnNames,
16✔
1452
                        )
16✔
1453

16✔
1454
                    // build queries
16✔
1455
                    upQueries.push(
16✔
1456
                        new Query(
16✔
1457
                            `ALTER TABLE ${this.escapePath(
16✔
1458
                                table,
16✔
1459
                            )} RENAME CONSTRAINT "${
16✔
1460
                                unique.name
16✔
1461
                            }" TO "${newUniqueName}"`,
16✔
1462
                        ),
16✔
1463
                    )
16✔
1464
                    downQueries.push(
16✔
1465
                        new Query(
16✔
1466
                            `ALTER TABLE ${this.escapePath(
16✔
1467
                                table,
16✔
1468
                            )} RENAME CONSTRAINT "${newUniqueName}" TO "${
16✔
1469
                                unique.name
16✔
1470
                            }"`,
16✔
1471
                        ),
16✔
1472
                    )
16✔
1473

16✔
1474
                    // replace constraint name
16✔
1475
                    unique.name = newUniqueName
16✔
1476
                })
100✔
1477

100✔
1478
                // rename index constraints
100✔
1479
                clonedTable.findColumnIndices(oldColumn).forEach((index) => {
100✔
1480
                    const oldIndexName =
28✔
1481
                        this.connection.namingStrategy.indexName(
28✔
1482
                            clonedTable,
28✔
1483
                            index.columnNames,
28✔
1484
                            index.where,
28✔
1485
                        )
28✔
1486

28✔
1487
                    // Skip renaming if Index has user defined constraint name
28✔
1488
                    if (index.name !== oldIndexName) return
28✔
1489

20✔
1490
                    // build new constraint name
20✔
1491
                    index.columnNames.splice(
20✔
1492
                        index.columnNames.indexOf(oldColumn.name),
20✔
1493
                        1,
20✔
1494
                    )
20✔
1495
                    index.columnNames.push(newColumn.name)
20✔
1496
                    const { schema } = this.driver.parseTableName(table)
20✔
1497
                    const newIndexName =
20✔
1498
                        this.connection.namingStrategy.indexName(
20✔
1499
                            clonedTable,
20✔
1500
                            index.columnNames,
20✔
1501
                            index.where,
20✔
1502
                        )
20✔
1503

20✔
1504
                    // build queries
20✔
1505
                    const up = schema
20✔
1506
                        ? `ALTER INDEX "${schema}"."${index.name}" RENAME TO "${newIndexName}"`
20✔
1507
                        : `ALTER INDEX "${index.name}" RENAME TO "${newIndexName}"`
28!
1508
                    const down = schema
28✔
1509
                        ? `ALTER INDEX "${schema}"."${newIndexName}" RENAME TO "${index.name}"`
28✔
1510
                        : `ALTER INDEX "${newIndexName}" RENAME TO "${index.name}"`
28!
1511

28✔
1512
                    upQueries.push(new Query(up))
28✔
1513
                    downQueries.push(new Query(down))
28✔
1514

28✔
1515
                    // replace constraint name
28✔
1516
                    index.name = newIndexName
28✔
1517
                })
100✔
1518

100✔
1519
                // rename foreign key constraints
100✔
1520
                clonedTable
100✔
1521
                    .findColumnForeignKeys(oldColumn)
100✔
1522
                    .forEach((foreignKey) => {
100✔
1523
                        const foreignKeyName =
36✔
1524
                            this.connection.namingStrategy.foreignKeyName(
36✔
1525
                                clonedTable,
36✔
1526
                                foreignKey.columnNames,
36✔
1527
                                this.getTablePath(foreignKey),
36✔
1528
                                foreignKey.referencedColumnNames,
36✔
1529
                            )
36✔
1530

36✔
1531
                        // Skip renaming if foreign key has user defined constraint name
36✔
1532
                        if (foreignKey.name !== foreignKeyName) return
36✔
1533

4✔
1534
                        // build new constraint name
4✔
1535
                        foreignKey.columnNames.splice(
4✔
1536
                            foreignKey.columnNames.indexOf(oldColumn.name),
4✔
1537
                            1,
4✔
1538
                        )
4✔
1539
                        foreignKey.columnNames.push(newColumn.name)
4✔
1540
                        const newForeignKeyName =
4✔
1541
                            this.connection.namingStrategy.foreignKeyName(
4✔
1542
                                clonedTable,
4✔
1543
                                foreignKey.columnNames,
4✔
1544
                                this.getTablePath(foreignKey),
4✔
1545
                                foreignKey.referencedColumnNames,
4✔
1546
                            )
4✔
1547

4✔
1548
                        // build queries
4✔
1549
                        upQueries.push(
4✔
1550
                            new Query(
4✔
1551
                                `ALTER TABLE ${this.escapePath(
4✔
1552
                                    table,
4✔
1553
                                )} RENAME CONSTRAINT "${
4✔
1554
                                    foreignKey.name
4✔
1555
                                }" TO "${newForeignKeyName}"`,
4✔
1556
                            ),
4✔
1557
                        )
4✔
1558
                        downQueries.push(
4✔
1559
                            new Query(
4✔
1560
                                `ALTER TABLE ${this.escapePath(
4✔
1561
                                    table,
4✔
1562
                                )} RENAME CONSTRAINT "${newForeignKeyName}" TO "${
4✔
1563
                                    foreignKey.name
4✔
1564
                                }"`,
4✔
1565
                            ),
4✔
1566
                        )
4✔
1567

4✔
1568
                        // replace constraint name
4✔
1569
                        foreignKey.name = newForeignKeyName
4✔
1570
                    })
100✔
1571

100✔
1572
                // rename old column in the Table object
100✔
1573
                const oldTableColumn = clonedTable.columns.find(
100✔
1574
                    (column) => column.name === oldColumn.name,
100✔
1575
                )
100✔
1576
                clonedTable.columns[
100✔
1577
                    clonedTable.columns.indexOf(oldTableColumn!)
100✔
1578
                ].name = newColumn.name
100✔
1579
                oldColumn.name = newColumn.name
100✔
1580
            }
100✔
1581

408✔
1582
            if (
408✔
1583
                newColumn.precision !== oldColumn.precision ||
408✔
1584
                newColumn.scale !== oldColumn.scale
408✔
1585
            ) {
408!
1586
                upQueries.push(
×
1587
                    new Query(
×
1588
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
×
1589
                            newColumn.name
×
1590
                        }" TYPE ${this.driver.createFullType(newColumn)}`,
×
1591
                    ),
×
1592
                )
×
1593
                downQueries.push(
×
1594
                    new Query(
×
1595
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
×
1596
                            newColumn.name
×
1597
                        }" TYPE ${this.driver.createFullType(oldColumn)}`,
×
1598
                    ),
×
1599
                )
×
1600
            }
×
1601

408✔
1602
            if (
408✔
1603
                (newColumn.type === "enum" ||
408✔
1604
                    newColumn.type === "simple-enum") &&
408✔
1605
                (oldColumn.type === "enum" ||
80!
1606
                    oldColumn.type === "simple-enum") &&
408✔
1607
                (!OrmUtils.isArraysEqual(newColumn.enum!, oldColumn.enum!) ||
80✔
1608
                    newColumn.enumName !== oldColumn.enumName)
80✔
1609
            ) {
408✔
1610
                const arraySuffix = newColumn.isArray ? "[]" : ""
68✔
1611

68✔
1612
                const { extraItems, missingItems } = OrmUtils.getArraysDiff(
68✔
1613
                    newColumn.enum!,
68✔
1614
                    oldColumn.enum!,
68✔
1615
                )
68✔
1616

68✔
1617
                const version = this.driver.version
68✔
1618

68✔
1619
                // when the only change is new enum value(s) we can use ADD VALUE syntax
68✔
1620
                const useAddValueForUp =
68✔
1621
                    VersionUtils.isGreaterOrEqual(version, "12.0") &&
68✔
1622
                    missingItems.length === 0 &&
68✔
1623
                    extraItems.length > 0
24✔
1624

68✔
1625
                // "public"."new_enum"
68✔
1626
                const newEnumName = this.buildEnumName(table, newColumn)
68✔
1627

68✔
1628
                // "public"."old_enum"
68✔
1629
                const oldEnumName = this.buildEnumName(table, oldColumn)
68✔
1630

68✔
1631
                // "old_enum"
68✔
1632
                const oldEnumNameWithoutSchema = this.buildEnumName(
68✔
1633
                    table,
68✔
1634
                    oldColumn,
68✔
1635
                    false,
68✔
1636
                )
68✔
1637

68✔
1638
                //"public"."old_enum_old"
68✔
1639
                const oldEnumNameWithSchema_old = this.buildEnumName(
68✔
1640
                    table,
68✔
1641
                    oldColumn,
68✔
1642
                    true,
68✔
1643
                    false,
68✔
1644
                    true,
68✔
1645
                )
68✔
1646

68✔
1647
                //"old_enum_old"
68✔
1648
                const oldEnumNameWithoutSchema_old = this.buildEnumName(
68✔
1649
                    table,
68✔
1650
                    oldColumn,
68✔
1651
                    false,
68✔
1652
                    false,
68✔
1653
                    true,
68✔
1654
                )
68✔
1655

68✔
1656
                // ADD VALUE allows us to just add new enum values without any type changes, but can of course only be used when only new values were added
68✔
1657
                if (useAddValueForUp) {
68✔
1658
                    // Add values for up - that's all we need
8✔
1659
                    for (const item of extraItems) {
8✔
1660
                        const escapedValue = item.replaceAll("'", "''")
12✔
1661

12✔
1662
                        upQueries.push(
12✔
1663
                            new Query(
12✔
1664
                                `ALTER TYPE ${oldEnumName} ADD VALUE '${escapedValue}'`,
12✔
1665
                            ),
12✔
1666
                        )
12✔
1667
                    }
12✔
1668

8✔
1669
                    // For down query, we're be doing the doing the same as normal enum change (create new type, alter column, drop old type)
8✔
1670

8✔
1671
                    downQueries.push(
8✔
1672
                        new Query(
8✔
1673
                            `ALTER TYPE ${oldEnumNameWithSchema_old} RENAME TO ${oldEnumNameWithoutSchema}`,
8✔
1674
                        ),
8✔
1675
                    )
8✔
1676

8✔
1677
                    downQueries.push(
8✔
1678
                        this.dropEnumTypeSql(table, newColumn, newEnumName),
8✔
1679
                    )
8✔
1680

8✔
1681
                    const downType = `${oldEnumNameWithSchema_old}${arraySuffix} USING "${newColumn.name}"::"text"::${oldEnumNameWithSchema_old}${arraySuffix}`
8✔
1682

8✔
1683
                    downQueries.push(
8✔
1684
                        new Query(
8✔
1685
                            `ALTER TABLE ${this.escapePath(
8✔
1686
                                table,
8✔
1687
                            )} ALTER COLUMN "${
8✔
1688
                                newColumn.name
8✔
1689
                            }" TYPE ${downType}`,
8✔
1690
                        ),
8✔
1691
                    )
8✔
1692

8✔
1693
                    downQueries.push(
8✔
1694
                        this.createEnumTypeSql(
8✔
1695
                            table,
8✔
1696
                            oldColumn,
8✔
1697
                            oldEnumNameWithSchema_old,
8✔
1698
                        ),
8✔
1699
                    )
8✔
1700
                } else {
68✔
1701
                    // rename old ENUM
60✔
1702
                    upQueries.push(
60✔
1703
                        new Query(
60✔
1704
                            `ALTER TYPE ${oldEnumName} RENAME TO ${oldEnumNameWithoutSchema_old}`,
60✔
1705
                        ),
60✔
1706
                    )
60✔
1707
                    downQueries.push(
60✔
1708
                        new Query(
60✔
1709
                            `ALTER TYPE ${oldEnumNameWithSchema_old} RENAME TO ${oldEnumNameWithoutSchema}`,
60✔
1710
                        ),
60✔
1711
                    )
60✔
1712

60✔
1713
                    // create new ENUM
60✔
1714
                    upQueries.push(
60✔
1715
                        this.createEnumTypeSql(table, newColumn, newEnumName),
60✔
1716
                    )
60✔
1717
                    downQueries.push(
60✔
1718
                        this.dropEnumTypeSql(table, newColumn, newEnumName),
60✔
1719
                    )
60✔
1720

60✔
1721
                    // if column have default value, we must drop it to avoid issues with type casting
60✔
1722
                    if (
60✔
1723
                        oldColumn.default !== null &&
60✔
1724
                        oldColumn.default !== undefined
60✔
1725
                    ) {
60✔
1726
                        // mark default as changed to prevent double update
20✔
1727
                        defaultValueChanged = true
20✔
1728
                        upQueries.push(
20✔
1729
                            new Query(
20✔
1730
                                `ALTER TABLE ${this.escapePath(
20✔
1731
                                    table,
20✔
1732
                                )} ALTER COLUMN "${
20✔
1733
                                    oldColumn.name
20✔
1734
                                }" DROP DEFAULT`,
20✔
1735
                            ),
20✔
1736
                        )
20✔
1737
                        downQueries.push(
20✔
1738
                            new Query(
20✔
1739
                                `ALTER TABLE ${this.escapePath(
20✔
1740
                                    table,
20✔
1741
                                )} ALTER COLUMN "${
20✔
1742
                                    oldColumn.name
20✔
1743
                                }" SET DEFAULT ${oldColumn.default}`,
20✔
1744
                            ),
20✔
1745
                        )
20✔
1746
                    }
20✔
1747

60✔
1748
                    // build column types
60✔
1749
                    const upType = `${newEnumName}${arraySuffix} USING "${newColumn.name}"::"text"::${newEnumName}${arraySuffix}`
60✔
1750
                    const downType = `${oldEnumNameWithSchema_old}${arraySuffix} USING "${newColumn.name}"::"text"::${oldEnumNameWithSchema_old}${arraySuffix}`
60✔
1751

60✔
1752
                    // update column to use new type
60✔
1753
                    upQueries.push(
60✔
1754
                        new Query(
60✔
1755
                            `ALTER TABLE ${this.escapePath(
60✔
1756
                                table,
60✔
1757
                            )} ALTER COLUMN "${newColumn.name}" TYPE ${upType}`,
60✔
1758
                        ),
60✔
1759
                    )
60✔
1760
                    downQueries.push(
60✔
1761
                        new Query(
60✔
1762
                            `ALTER TABLE ${this.escapePath(
60✔
1763
                                table,
60✔
1764
                            )} ALTER COLUMN "${
60✔
1765
                                newColumn.name
60✔
1766
                            }" TYPE ${downType}`,
60✔
1767
                        ),
60✔
1768
                    )
60✔
1769

60✔
1770
                    // restore column default or create new one
60✔
1771
                    if (
60✔
1772
                        newColumn.default !== null &&
60✔
1773
                        newColumn.default !== undefined
60✔
1774
                    ) {
60✔
1775
                        upQueries.push(
20✔
1776
                            new Query(
20✔
1777
                                `ALTER TABLE ${this.escapePath(
20✔
1778
                                    table,
20✔
1779
                                )} ALTER COLUMN "${
20✔
1780
                                    newColumn.name
20✔
1781
                                }" SET DEFAULT ${newColumn.default}`,
20✔
1782
                            ),
20✔
1783
                        )
20✔
1784
                        downQueries.push(
20✔
1785
                            new Query(
20✔
1786
                                `ALTER TABLE ${this.escapePath(
20✔
1787
                                    table,
20✔
1788
                                )} ALTER COLUMN "${
20✔
1789
                                    newColumn.name
20✔
1790
                                }" DROP DEFAULT`,
20✔
1791
                            ),
20✔
1792
                        )
20✔
1793
                    }
20✔
1794

60✔
1795
                    // remove old ENUM
60✔
1796
                    upQueries.push(
60✔
1797
                        this.dropEnumTypeSql(
60✔
1798
                            table,
60✔
1799
                            oldColumn,
60✔
1800
                            oldEnumNameWithSchema_old,
60✔
1801
                        ),
60✔
1802
                    )
60✔
1803
                    downQueries.push(
60✔
1804
                        this.createEnumTypeSql(
60✔
1805
                            table,
60✔
1806
                            oldColumn,
60✔
1807
                            oldEnumNameWithSchema_old,
60✔
1808
                        ),
60✔
1809
                    )
60✔
1810
                }
60✔
1811
            }
68✔
1812

408✔
1813
            if (oldColumn.isNullable !== newColumn.isNullable) {
408✔
1814
                if (newColumn.isNullable) {
8✔
1815
                    upQueries.push(
4✔
1816
                        new Query(
4✔
1817
                            `ALTER TABLE ${this.escapePath(
4✔
1818
                                table,
4✔
1819
                            )} ALTER COLUMN "${oldColumn.name}" DROP NOT NULL`,
4✔
1820
                        ),
4✔
1821
                    )
4✔
1822
                    downQueries.push(
4✔
1823
                        new Query(
4✔
1824
                            `ALTER TABLE ${this.escapePath(
4✔
1825
                                table,
4✔
1826
                            )} ALTER COLUMN "${oldColumn.name}" SET NOT NULL`,
4✔
1827
                        ),
4✔
1828
                    )
4✔
1829
                } else {
4✔
1830
                    upQueries.push(
4✔
1831
                        new Query(
4✔
1832
                            `ALTER TABLE ${this.escapePath(
4✔
1833
                                table,
4✔
1834
                            )} ALTER COLUMN "${oldColumn.name}" SET NOT NULL`,
4✔
1835
                        ),
4✔
1836
                    )
4✔
1837
                    downQueries.push(
4✔
1838
                        new Query(
4✔
1839
                            `ALTER TABLE ${this.escapePath(
4✔
1840
                                table,
4✔
1841
                            )} ALTER COLUMN "${oldColumn.name}" DROP NOT NULL`,
4✔
1842
                        ),
4✔
1843
                    )
4✔
1844
                }
4✔
1845
            }
8✔
1846

408✔
1847
            if (oldColumn.comment !== newColumn.comment) {
408✔
1848
                upQueries.push(
20✔
1849
                    new Query(
20✔
1850
                        `COMMENT ON COLUMN ${this.escapePath(table)}."${
20✔
1851
                            oldColumn.name
20✔
1852
                        }" IS ${this.escapeComment(newColumn.comment)}`,
20✔
1853
                    ),
20✔
1854
                )
20✔
1855
                downQueries.push(
20✔
1856
                    new Query(
20✔
1857
                        `COMMENT ON COLUMN ${this.escapePath(table)}."${
20✔
1858
                            newColumn.name
20✔
1859
                        }" IS ${this.escapeComment(oldColumn.comment)}`,
20✔
1860
                    ),
20✔
1861
                )
20✔
1862
            }
20✔
1863

408✔
1864
            if (newColumn.isPrimary !== oldColumn.isPrimary) {
408✔
1865
                const primaryColumns = clonedTable.primaryColumns
20✔
1866

20✔
1867
                // if primary column state changed, we must always drop existed constraint.
20✔
1868
                if (primaryColumns.length > 0) {
20✔
1869
                    const pkName = primaryColumns[0].primaryKeyConstraintName
12✔
1870
                        ? primaryColumns[0].primaryKeyConstraintName
12!
1871
                        : this.connection.namingStrategy.primaryKeyName(
12✔
1872
                              clonedTable,
12✔
1873
                              primaryColumns.map((column) => column.name),
12✔
1874
                          )
12✔
1875

12✔
1876
                    const columnNames = primaryColumns
12✔
1877
                        .map((column) => `"${column.name}"`)
12✔
1878
                        .join(", ")
12✔
1879

12✔
1880
                    upQueries.push(
12✔
1881
                        new Query(
12✔
1882
                            `ALTER TABLE ${this.escapePath(
12✔
1883
                                table,
12✔
1884
                            )} DROP CONSTRAINT "${pkName}"`,
12✔
1885
                        ),
12✔
1886
                    )
12✔
1887
                    downQueries.push(
12✔
1888
                        new Query(
12✔
1889
                            `ALTER TABLE ${this.escapePath(
12✔
1890
                                table,
12✔
1891
                            )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
12✔
1892
                        ),
12✔
1893
                    )
12✔
1894
                }
12✔
1895

20✔
1896
                if (newColumn.isPrimary === true) {
20✔
1897
                    primaryColumns.push(newColumn)
12✔
1898
                    // update column in table
12✔
1899
                    const column = clonedTable.columns.find(
12✔
1900
                        (column) => column.name === newColumn.name,
12✔
1901
                    )
12✔
1902
                    column!.isPrimary = true
12✔
1903
                    const pkName = primaryColumns[0].primaryKeyConstraintName
12✔
1904
                        ? primaryColumns[0].primaryKeyConstraintName
12!
1905
                        : this.connection.namingStrategy.primaryKeyName(
12✔
1906
                              clonedTable,
12✔
1907
                              primaryColumns.map((column) => column.name),
12✔
1908
                          )
12✔
1909

12✔
1910
                    const columnNames = primaryColumns
12✔
1911
                        .map((column) => `"${column.name}"`)
12✔
1912
                        .join(", ")
12✔
1913

12✔
1914
                    upQueries.push(
12✔
1915
                        new Query(
12✔
1916
                            `ALTER TABLE ${this.escapePath(
12✔
1917
                                table,
12✔
1918
                            )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
12✔
1919
                        ),
12✔
1920
                    )
12✔
1921
                    downQueries.push(
12✔
1922
                        new Query(
12✔
1923
                            `ALTER TABLE ${this.escapePath(
12✔
1924
                                table,
12✔
1925
                            )} DROP CONSTRAINT "${pkName}"`,
12✔
1926
                        ),
12✔
1927
                    )
12✔
1928
                } else {
20✔
1929
                    const primaryColumn = primaryColumns.find(
8✔
1930
                        (c) => c.name === newColumn.name,
8✔
1931
                    )
8✔
1932
                    primaryColumns.splice(
8✔
1933
                        primaryColumns.indexOf(primaryColumn!),
8✔
1934
                        1,
8✔
1935
                    )
8✔
1936

8✔
1937
                    // update column in table
8✔
1938
                    const column = clonedTable.columns.find(
8✔
1939
                        (column) => column.name === newColumn.name,
8✔
1940
                    )
8✔
1941
                    column!.isPrimary = false
8✔
1942

8✔
1943
                    // if we have another primary keys, we must recreate constraint.
8✔
1944
                    if (primaryColumns.length > 0) {
8✔
1945
                        const pkName = primaryColumns[0]
4✔
1946
                            .primaryKeyConstraintName
4✔
1947
                            ? primaryColumns[0].primaryKeyConstraintName
4!
1948
                            : this.connection.namingStrategy.primaryKeyName(
4✔
1949
                                  clonedTable,
4✔
1950
                                  primaryColumns.map((column) => column.name),
4✔
1951
                              )
4✔
1952

4✔
1953
                        const columnNames = primaryColumns
4✔
1954
                            .map((column) => `"${column.name}"`)
4✔
1955
                            .join(", ")
4✔
1956

4✔
1957
                        upQueries.push(
4✔
1958
                            new Query(
4✔
1959
                                `ALTER TABLE ${this.escapePath(
4✔
1960
                                    table,
4✔
1961
                                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
4✔
1962
                            ),
4✔
1963
                        )
4✔
1964
                        downQueries.push(
4✔
1965
                            new Query(
4✔
1966
                                `ALTER TABLE ${this.escapePath(
4✔
1967
                                    table,
4✔
1968
                                )} DROP CONSTRAINT "${pkName}"`,
4✔
1969
                            ),
4✔
1970
                        )
4✔
1971
                    }
4✔
1972
                }
8✔
1973
            }
20✔
1974

408✔
1975
            if (newColumn.isUnique !== oldColumn.isUnique) {
408✔
1976
                if (newColumn.isUnique === true) {
4✔
1977
                    const uniqueConstraint = new TableUnique({
4✔
1978
                        name: this.connection.namingStrategy.uniqueConstraintName(
4✔
1979
                            table,
4✔
1980
                            [newColumn.name],
4✔
1981
                        ),
4✔
1982
                        columnNames: [newColumn.name],
4✔
1983
                    })
4✔
1984
                    clonedTable.uniques.push(uniqueConstraint)
4✔
1985
                    upQueries.push(
4✔
1986
                        new Query(
4✔
1987
                            `ALTER TABLE ${this.escapePath(
4✔
1988
                                table,
4✔
1989
                            )} ADD CONSTRAINT "${
4✔
1990
                                uniqueConstraint.name
4✔
1991
                            }" UNIQUE ("${newColumn.name}")`,
4✔
1992
                        ),
4✔
1993
                    )
4✔
1994
                    downQueries.push(
4✔
1995
                        new Query(
4✔
1996
                            `ALTER TABLE ${this.escapePath(
4✔
1997
                                table,
4✔
1998
                            )} DROP CONSTRAINT "${uniqueConstraint.name}"`,
4✔
1999
                        ),
4✔
2000
                    )
4✔
2001
                } else {
4!
2002
                    const uniqueConstraint = clonedTable.uniques.find(
×
2003
                        (unique) => {
×
2004
                            return (
×
2005
                                unique.columnNames.length === 1 &&
×
2006
                                !!unique.columnNames.find(
×
2007
                                    (columnName) =>
×
2008
                                        columnName === newColumn.name,
×
2009
                                )
×
2010
                            )
×
2011
                        },
×
2012
                    )
×
2013
                    clonedTable.uniques.splice(
×
2014
                        clonedTable.uniques.indexOf(uniqueConstraint!),
×
2015
                        1,
×
2016
                    )
×
2017
                    upQueries.push(
×
2018
                        new Query(
×
2019
                            `ALTER TABLE ${this.escapePath(
×
2020
                                table,
×
2021
                            )} DROP CONSTRAINT "${uniqueConstraint!.name}"`,
×
2022
                        ),
×
2023
                    )
×
2024
                    downQueries.push(
×
2025
                        new Query(
×
2026
                            `ALTER TABLE ${this.escapePath(
×
2027
                                table,
×
2028
                            )} ADD CONSTRAINT "${
×
2029
                                uniqueConstraint!.name
×
2030
                            }" UNIQUE ("${newColumn.name}")`,
×
2031
                        ),
×
2032
                    )
×
2033
                }
×
2034
            }
4✔
2035

408✔
2036
            if (oldColumn.isGenerated !== newColumn.isGenerated) {
408✔
2037
                // if old column was "generated", we should clear defaults
48✔
2038
                if (oldColumn.isGenerated) {
48✔
2039
                    if (oldColumn.generationStrategy === "uuid") {
20✔
2040
                        upQueries.push(
8✔
2041
                            new Query(
8✔
2042
                                `ALTER TABLE ${this.escapePath(
8✔
2043
                                    table,
8✔
2044
                                )} ALTER COLUMN "${
8✔
2045
                                    oldColumn.name
8✔
2046
                                }" DROP DEFAULT`,
8✔
2047
                            ),
8✔
2048
                        )
8✔
2049
                        downQueries.push(
8✔
2050
                            new Query(
8✔
2051
                                `ALTER TABLE ${this.escapePath(
8✔
2052
                                    table,
8✔
2053
                                )} ALTER COLUMN "${
8✔
2054
                                    oldColumn.name
8✔
2055
                                }" SET DEFAULT ${this.driver.uuidGenerator}`,
8✔
2056
                            ),
8✔
2057
                        )
8✔
2058
                    } else if (oldColumn.generationStrategy === "increment") {
20✔
2059
                        upQueries.push(
12✔
2060
                            new Query(
12✔
2061
                                `ALTER TABLE ${this.escapePath(
12✔
2062
                                    table,
12✔
2063
                                )} ALTER COLUMN "${
12✔
2064
                                    newColumn.name
12✔
2065
                                }" DROP DEFAULT`,
12✔
2066
                            ),
12✔
2067
                        )
12✔
2068
                        downQueries.push(
12✔
2069
                            new Query(
12✔
2070
                                `ALTER TABLE ${this.escapePath(
12✔
2071
                                    table,
12✔
2072
                                )} ALTER COLUMN "${
12✔
2073
                                    newColumn.name
12✔
2074
                                }" SET DEFAULT nextval('${this.escapePath(
12✔
2075
                                    this.buildSequencePath(table, newColumn),
12✔
2076
                                )}')`,
12✔
2077
                            ),
12✔
2078
                        )
12✔
2079

12✔
2080
                        upQueries.push(
12✔
2081
                            new Query(
12✔
2082
                                `DROP SEQUENCE ${this.escapePath(
12✔
2083
                                    this.buildSequencePath(table, newColumn),
12✔
2084
                                )}`,
12✔
2085
                            ),
12✔
2086
                        )
12✔
2087
                        downQueries.push(
12✔
2088
                            new Query(
12✔
2089
                                `CREATE SEQUENCE IF NOT EXISTS ${this.escapePath(
12✔
2090
                                    this.buildSequencePath(table, newColumn),
12✔
2091
                                )} OWNED BY ${this.escapePath(table)}."${
12✔
2092
                                    newColumn.name
12✔
2093
                                }"`,
12✔
2094
                            ),
12✔
2095
                        )
12✔
2096
                    }
12✔
2097
                }
20✔
2098

48✔
2099
                if (newColumn.generationStrategy === "uuid") {
48✔
2100
                    if (newColumn.isGenerated === true) {
8✔
2101
                        upQueries.push(
8✔
2102
                            new Query(
8✔
2103
                                `ALTER TABLE ${this.escapePath(
8✔
2104
                                    table,
8✔
2105
                                )} ALTER COLUMN "${
8✔
2106
                                    newColumn.name
8✔
2107
                                }" SET DEFAULT ${this.driver.uuidGenerator}`,
8✔
2108
                            ),
8✔
2109
                        )
8✔
2110
                        downQueries.push(
8✔
2111
                            new Query(
8✔
2112
                                `ALTER TABLE ${this.escapePath(
8✔
2113
                                    table,
8✔
2114
                                )} ALTER COLUMN "${
8✔
2115
                                    newColumn.name
8✔
2116
                                }" DROP DEFAULT`,
8✔
2117
                            ),
8✔
2118
                        )
8✔
2119
                    } else {
8!
2120
                        upQueries.push(
×
2121
                            new Query(
×
2122
                                `ALTER TABLE ${this.escapePath(
×
2123
                                    table,
×
2124
                                )} ALTER COLUMN "${
×
2125
                                    newColumn.name
×
2126
                                }" DROP DEFAULT`,
×
2127
                            ),
×
2128
                        )
×
2129
                        downQueries.push(
×
2130
                            new Query(
×
2131
                                `ALTER TABLE ${this.escapePath(
×
2132
                                    table,
×
2133
                                )} ALTER COLUMN "${
×
2134
                                    newColumn.name
×
2135
                                }" SET DEFAULT ${this.driver.uuidGenerator}`,
×
2136
                            ),
×
2137
                        )
×
2138
                    }
×
2139
                } else if (newColumn.generationStrategy === "increment") {
48✔
2140
                    if (newColumn.isGenerated === true) {
20✔
2141
                        upQueries.push(
20✔
2142
                            new Query(
20✔
2143
                                `CREATE SEQUENCE IF NOT EXISTS ${this.escapePath(
20✔
2144
                                    this.buildSequencePath(table, newColumn),
20✔
2145
                                )} OWNED BY ${this.escapePath(table)}."${
20✔
2146
                                    newColumn.name
20✔
2147
                                }"`,
20✔
2148
                            ),
20✔
2149
                        )
20✔
2150
                        downQueries.push(
20✔
2151
                            new Query(
20✔
2152
                                `DROP SEQUENCE ${this.escapePath(
20✔
2153
                                    this.buildSequencePath(table, newColumn),
20✔
2154
                                )}`,
20✔
2155
                            ),
20✔
2156
                        )
20✔
2157

20✔
2158
                        upQueries.push(
20✔
2159
                            new Query(
20✔
2160
                                `ALTER TABLE ${this.escapePath(
20✔
2161
                                    table,
20✔
2162
                                )} ALTER COLUMN "${
20✔
2163
                                    newColumn.name
20✔
2164
                                }" SET DEFAULT nextval('${this.escapePath(
20✔
2165
                                    this.buildSequencePath(table, newColumn),
20✔
2166
                                )}')`,
20✔
2167
                            ),
20✔
2168
                        )
20✔
2169
                        downQueries.push(
20✔
2170
                            new Query(
20✔
2171
                                `ALTER TABLE ${this.escapePath(
20✔
2172
                                    table,
20✔
2173
                                )} ALTER COLUMN "${
20✔
2174
                                    newColumn.name
20✔
2175
                                }" DROP DEFAULT`,
20✔
2176
                            ),
20✔
2177
                        )
20✔
2178
                    } else {
20!
2179
                        upQueries.push(
×
2180
                            new Query(
×
2181
                                `ALTER TABLE ${this.escapePath(
×
2182
                                    table,
×
2183
                                )} ALTER COLUMN "${
×
2184
                                    newColumn.name
×
2185
                                }" DROP DEFAULT`,
×
2186
                            ),
×
2187
                        )
×
2188
                        downQueries.push(
×
2189
                            new Query(
×
2190
                                `ALTER TABLE ${this.escapePath(
×
2191
                                    table,
×
2192
                                )} ALTER COLUMN "${
×
2193
                                    newColumn.name
×
2194
                                }" SET DEFAULT nextval('${this.escapePath(
×
2195
                                    this.buildSequencePath(table, newColumn),
×
2196
                                )}')`,
×
2197
                            ),
×
2198
                        )
×
2199

×
2200
                        upQueries.push(
×
2201
                            new Query(
×
2202
                                `DROP SEQUENCE ${this.escapePath(
×
2203
                                    this.buildSequencePath(table, newColumn),
×
2204
                                )}`,
×
2205
                            ),
×
2206
                        )
×
2207
                        downQueries.push(
×
2208
                            new Query(
×
2209
                                `CREATE SEQUENCE IF NOT EXISTS ${this.escapePath(
×
2210
                                    this.buildSequencePath(table, newColumn),
×
2211
                                )} OWNED BY ${this.escapePath(table)}."${
×
2212
                                    newColumn.name
×
2213
                                }"`,
×
2214
                            ),
×
2215
                        )
×
2216
                    }
×
2217
                }
20✔
2218
            }
48✔
2219

408✔
2220
            // the default might have changed when the enum changed
408✔
2221
            if (
408✔
2222
                newColumn.default !== oldColumn.default &&
408✔
2223
                !defaultValueChanged
32✔
2224
            ) {
408✔
2225
                if (
20✔
2226
                    newColumn.default !== null &&
20✔
2227
                    newColumn.default !== undefined
20✔
2228
                ) {
20✔
2229
                    upQueries.push(
20✔
2230
                        new Query(
20✔
2231
                            `ALTER TABLE ${this.escapePath(
20✔
2232
                                table,
20✔
2233
                            )} ALTER COLUMN "${newColumn.name}" SET DEFAULT ${
20✔
2234
                                newColumn.default
20✔
2235
                            }`,
20✔
2236
                        ),
20✔
2237
                    )
20✔
2238

20✔
2239
                    if (
20✔
2240
                        oldColumn.default !== null &&
20✔
2241
                        oldColumn.default !== undefined
20✔
2242
                    ) {
20✔
2243
                        downQueries.push(
12✔
2244
                            new Query(
12✔
2245
                                `ALTER TABLE ${this.escapePath(
12✔
2246
                                    table,
12✔
2247
                                )} ALTER COLUMN "${
12✔
2248
                                    newColumn.name
12✔
2249
                                }" SET DEFAULT ${oldColumn.default}`,
12✔
2250
                            ),
12✔
2251
                        )
12✔
2252
                    } else {
20✔
2253
                        downQueries.push(
8✔
2254
                            new Query(
8✔
2255
                                `ALTER TABLE ${this.escapePath(
8✔
2256
                                    table,
8✔
2257
                                )} ALTER COLUMN "${
8✔
2258
                                    newColumn.name
8✔
2259
                                }" DROP DEFAULT`,
8✔
2260
                            ),
8✔
2261
                        )
8✔
2262
                    }
8✔
2263
                } else if (
20!
2264
                    oldColumn.default !== null &&
×
2265
                    oldColumn.default !== undefined
×
2266
                ) {
×
2267
                    upQueries.push(
×
2268
                        new Query(
×
2269
                            `ALTER TABLE ${this.escapePath(
×
2270
                                table,
×
2271
                            )} ALTER COLUMN "${newColumn.name}" DROP DEFAULT`,
×
2272
                        ),
×
2273
                    )
×
2274
                    downQueries.push(
×
2275
                        new Query(
×
2276
                            `ALTER TABLE ${this.escapePath(
×
2277
                                table,
×
2278
                            )} ALTER COLUMN "${newColumn.name}" SET DEFAULT ${
×
2279
                                oldColumn.default
×
2280
                            }`,
×
2281
                        ),
×
2282
                    )
×
2283
                }
×
2284
            }
20✔
2285

408✔
2286
            if (
408✔
2287
                (newColumn.spatialFeatureType || "").toLowerCase() !==
408✔
2288
                    (oldColumn.spatialFeatureType || "").toLowerCase() ||
408✔
2289
                newColumn.srid !== oldColumn.srid
408✔
2290
            ) {
408!
2291
                upQueries.push(
×
2292
                    new Query(
×
2293
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
×
2294
                            newColumn.name
×
2295
                        }" TYPE ${this.driver.createFullType(newColumn)}`,
×
2296
                    ),
×
2297
                )
×
2298
                downQueries.push(
×
2299
                    new Query(
×
2300
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
×
2301
                            newColumn.name
×
2302
                        }" TYPE ${this.driver.createFullType(oldColumn)}`,
×
2303
                    ),
×
2304
                )
×
2305
            }
×
2306

408✔
2307
            // update column collation
408✔
2308
            if (newColumn.collation !== oldColumn.collation) {
408✔
2309
                upQueries.push(
8✔
2310
                    new Query(
8✔
2311
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
8✔
2312
                            newColumn.name
8✔
2313
                        }" TYPE ${newColumn.type} COLLATE "${
8✔
2314
                            newColumn.collation
8✔
2315
                        }"`,
8✔
2316
                    ),
8✔
2317
                )
8✔
2318

8✔
2319
                const oldCollation = oldColumn.collation
8✔
2320
                    ? `"${oldColumn.collation}"`
8✔
2321
                    : `pg_catalog."default"` // if there's no old collation, use default
8!
2322

8✔
2323
                downQueries.push(
8✔
2324
                    new Query(
8✔
2325
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
8✔
2326
                            newColumn.name
8✔
2327
                        }" TYPE ${newColumn.type} COLLATE ${oldCollation}`,
8✔
2328
                    ),
8✔
2329
                )
8✔
2330
            }
8✔
2331

408✔
2332
            if (newColumn.generatedType !== oldColumn.generatedType) {
408✔
2333
                // Convert generated column data to normal column
8✔
2334
                if (
8✔
2335
                    !newColumn.generatedType ||
8!
2336
                    newColumn.generatedType === "VIRTUAL"
×
2337
                ) {
8✔
2338
                    // We can copy the generated data to the new column
8✔
2339
                    const tableNameWithSchema = (
8✔
2340
                        await this.getTableNameWithSchema(table.name)
8✔
2341
                    ).split(".")
8✔
2342
                    const tableName = tableNameWithSchema[1]
8✔
2343
                    const schema = tableNameWithSchema[0]
8✔
2344

8✔
2345
                    upQueries.push(
8✔
2346
                        new Query(
8✔
2347
                            `ALTER TABLE ${this.escapePath(
8✔
2348
                                table,
8✔
2349
                            )} RENAME COLUMN "${oldColumn.name}" TO "TEMP_OLD_${
8✔
2350
                                oldColumn.name
8✔
2351
                            }"`,
8✔
2352
                        ),
8✔
2353
                    )
8✔
2354
                    upQueries.push(
8✔
2355
                        new Query(
8✔
2356
                            `ALTER TABLE ${this.escapePath(
8✔
2357
                                table,
8✔
2358
                            )} ADD ${this.buildCreateColumnSql(
8✔
2359
                                table,
8✔
2360
                                newColumn,
8✔
2361
                            )}`,
8✔
2362
                        ),
8✔
2363
                    )
8✔
2364
                    upQueries.push(
8✔
2365
                        new Query(
8✔
2366
                            `UPDATE ${this.escapePath(table)} SET "${
8✔
2367
                                newColumn.name
8✔
2368
                            }" = "TEMP_OLD_${oldColumn.name}"`,
8✔
2369
                        ),
8✔
2370
                    )
8✔
2371
                    upQueries.push(
8✔
2372
                        new Query(
8✔
2373
                            `ALTER TABLE ${this.escapePath(
8✔
2374
                                table,
8✔
2375
                            )} DROP COLUMN "TEMP_OLD_${oldColumn.name}"`,
8✔
2376
                        ),
8✔
2377
                    )
8✔
2378
                    upQueries.push(
8✔
2379
                        this.deleteTypeormMetadataSql({
8✔
2380
                            database: this.driver.database,
8✔
2381
                            schema,
8✔
2382
                            table: tableName,
8✔
2383
                            type: MetadataTableType.GENERATED_COLUMN,
8✔
2384
                            name: oldColumn.name,
8✔
2385
                        }),
8✔
2386
                    )
8✔
2387
                    // However, we can't copy it back on downgrade. It needs to regenerate.
8✔
2388
                    downQueries.push(
8✔
2389
                        this.insertTypeormMetadataSql({
8✔
2390
                            database: this.driver.database,
8✔
2391
                            schema,
8✔
2392
                            table: tableName,
8✔
2393
                            type: MetadataTableType.GENERATED_COLUMN,
8✔
2394
                            name: oldColumn.name,
8✔
2395
                            value: oldColumn.asExpression,
8✔
2396
                        }),
8✔
2397
                    )
8✔
2398
                    downQueries.push(
8✔
2399
                        new Query(
8✔
2400
                            `ALTER TABLE ${this.escapePath(
8✔
2401
                                table,
8✔
2402
                            )} ADD ${this.buildCreateColumnSql(
8✔
2403
                                table,
8✔
2404
                                oldColumn,
8✔
2405
                            )}`,
8✔
2406
                        ),
8✔
2407
                    )
8✔
2408
                    downQueries.push(
8✔
2409
                        new Query(
8✔
2410
                            `ALTER TABLE ${this.escapePath(
8✔
2411
                                table,
8✔
2412
                            )} DROP COLUMN "${newColumn.name}"`,
8✔
2413
                        ),
8✔
2414
                    )
8✔
2415
                    // downQueries.push(
8✔
2416
                    //     this.deleteTypeormMetadataSql({
8✔
2417
                    //         database: this.driver.database,
8✔
2418
                    //         schema,
8✔
2419
                    //         table: tableName,
8✔
2420
                    //         type: MetadataTableType.GENERATED_COLUMN,
8✔
2421
                    //         name: newColumn.name,
8✔
2422
                    //     }),
8✔
2423
                    // )
8✔
2424
                }
8✔
2425
            }
8✔
2426
        }
408✔
2427

508✔
2428
        await this.executeQueries(upQueries, downQueries)
508✔
2429
        this.replaceCachedTable(table, clonedTable)
508✔
2430
    }
508✔
2431

23✔
2432
    /**
23✔
2433
     * Changes a column in the table.
23✔
2434
     * @param tableOrName
23✔
2435
     * @param changedColumns
23✔
2436
     */
23✔
2437
    async changeColumns(
23✔
2438
        tableOrName: Table | string,
252✔
2439
        changedColumns: { newColumn: TableColumn; oldColumn: TableColumn }[],
252✔
2440
    ): Promise<void> {
252✔
2441
        for (const { oldColumn, newColumn } of changedColumns) {
252✔
2442
            await this.changeColumn(tableOrName, oldColumn, newColumn)
328✔
2443
        }
328✔
2444
    }
252✔
2445

23✔
2446
    /**
23✔
2447
     * Drops column in the table.
23✔
2448
     * @param tableOrName
23✔
2449
     * @param columnOrName
23✔
2450
     * @param ifExists
23✔
2451
     */
23✔
2452
    async dropColumn(
23✔
2453
        tableOrName: Table | string,
192✔
2454
        columnOrName: TableColumn | string,
192✔
2455
        ifExists?: boolean,
192✔
2456
    ): Promise<void> {
192✔
2457
        const table = InstanceChecker.isTable(tableOrName)
192✔
2458
            ? tableOrName
192✔
2459
            : await this.getCachedTable(tableOrName)
192✔
2460
        const column = InstanceChecker.isTableColumn(columnOrName)
16✔
2461
            ? columnOrName
192✔
2462
            : table.findColumnByName(columnOrName)
192✔
2463
        if (!column) {
192✔
2464
            if (ifExists) return
8✔
2465
            throw new TypeORMError(
4✔
2466
                `Column "${columnOrName}" was not found in table "${table.name}"`,
4✔
2467
            )
4✔
2468
        }
4✔
2469

184✔
2470
        const clonedTable = table.clone()
184✔
2471
        const upQueries: Query[] = []
184✔
2472
        const downQueries: Query[] = []
184✔
2473

184✔
2474
        // drop primary key constraint
184✔
2475
        if (column.isPrimary) {
192✔
2476
            const pkName = column.primaryKeyConstraintName
16✔
2477
                ? column.primaryKeyConstraintName
16!
2478
                : this.connection.namingStrategy.primaryKeyName(
16✔
2479
                      clonedTable,
16✔
2480
                      clonedTable.primaryColumns.map((column) => column.name),
16✔
2481
                  )
16✔
2482

16✔
2483
            const columnNames = clonedTable.primaryColumns
16✔
2484
                .map((primaryColumn) => `"${primaryColumn.name}"`)
16✔
2485
                .join(", ")
16✔
2486

16✔
2487
            upQueries.push(
16✔
2488
                new Query(
16✔
2489
                    `ALTER TABLE ${this.escapePath(
16✔
2490
                        clonedTable,
16✔
2491
                    )} DROP CONSTRAINT "${pkName}"`,
16✔
2492
                ),
16✔
2493
            )
16✔
2494
            downQueries.push(
16✔
2495
                new Query(
16✔
2496
                    `ALTER TABLE ${this.escapePath(
16✔
2497
                        clonedTable,
16✔
2498
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
16✔
2499
                ),
16✔
2500
            )
16✔
2501

16✔
2502
            // update column in table
16✔
2503
            const tableColumn = clonedTable.findColumnByName(column.name)
16✔
2504
            tableColumn!.isPrimary = false
16✔
2505

16✔
2506
            // if primary key have multiple columns, we must recreate it without dropped column
16✔
2507
            if (clonedTable.primaryColumns.length > 0) {
16!
2508
                const pkName = clonedTable.primaryColumns[0]
×
2509
                    .primaryKeyConstraintName
×
2510
                    ? clonedTable.primaryColumns[0].primaryKeyConstraintName
×
2511
                    : this.connection.namingStrategy.primaryKeyName(
×
2512
                          clonedTable,
×
2513
                          clonedTable.primaryColumns.map(
×
2514
                              (column) => column.name,
×
2515
                          ),
×
2516
                      )
×
2517

×
2518
                const columnNames = clonedTable.primaryColumns
×
2519
                    .map((primaryColumn) => `"${primaryColumn.name}"`)
×
2520
                    .join(", ")
×
2521

×
2522
                upQueries.push(
×
2523
                    new Query(
×
2524
                        `ALTER TABLE ${this.escapePath(
×
2525
                            clonedTable,
×
2526
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
×
2527
                    ),
×
2528
                )
×
2529
                downQueries.push(
×
2530
                    new Query(
×
2531
                        `ALTER TABLE ${this.escapePath(
×
2532
                            clonedTable,
×
2533
                        )} DROP CONSTRAINT "${pkName}"`,
×
2534
                    ),
×
2535
                )
×
2536
            }
×
2537
        }
16✔
2538

184✔
2539
        // drop column index
184✔
2540
        const columnIndex = clonedTable.indices.find(
184✔
2541
            (index) =>
184✔
2542
                index.columnNames.length === 1 &&
12✔
2543
                index.columnNames[0] === column.name,
184✔
2544
        )
184✔
2545
        if (columnIndex) {
192!
2546
            clonedTable.indices.splice(
×
2547
                clonedTable.indices.indexOf(columnIndex),
×
2548
                1,
×
2549
            )
×
2550
            upQueries.push(this.dropIndexSql(table, columnIndex))
×
2551
            downQueries.push(this.createIndexSql(table, columnIndex))
×
2552
        }
×
2553

184✔
2554
        // drop column check
184✔
2555
        const columnCheck = clonedTable.checks.find(
184✔
2556
            (check) =>
184✔
2557
                !!check.columnNames &&
56✔
2558
                check.columnNames.length === 1 &&
56✔
2559
                check.columnNames[0] === column.name,
184✔
2560
        )
184✔
2561
        if (columnCheck) {
192✔
2562
            clonedTable.checks.splice(
8✔
2563
                clonedTable.checks.indexOf(columnCheck),
8✔
2564
                1,
8✔
2565
            )
8✔
2566
            upQueries.push(this.dropCheckConstraintSql(table, columnCheck))
8✔
2567
            downQueries.push(this.createCheckConstraintSql(table, columnCheck))
8✔
2568
        }
8✔
2569

184✔
2570
        // drop column unique
184✔
2571
        const columnUnique = clonedTable.uniques.find(
184✔
2572
            (unique) =>
184✔
2573
                unique.columnNames.length === 1 &&
97✔
2574
                unique.columnNames[0] === column.name,
184✔
2575
        )
184✔
2576
        if (columnUnique) {
192✔
2577
            clonedTable.uniques.splice(
12✔
2578
                clonedTable.uniques.indexOf(columnUnique),
12✔
2579
                1,
12✔
2580
            )
12✔
2581
            upQueries.push(this.dropUniqueConstraintSql(table, columnUnique))
12✔
2582
            downQueries.push(
12✔
2583
                this.createUniqueConstraintSql(table, columnUnique),
12✔
2584
            )
12✔
2585
        }
12✔
2586

184✔
2587
        const ifExistsClause = ifExists ? "IF EXISTS " : ""
192!
2588
        upQueries.push(
192✔
2589
            new Query(
192✔
2590
                `ALTER TABLE ${this.escapePath(table)} DROP COLUMN ${ifExistsClause}"${
192✔
2591
                    column.name
192✔
2592
                }"`,
192✔
2593
            ),
192✔
2594
        )
192✔
2595
        downQueries.push(
192✔
2596
            new Query(
192✔
2597
                `ALTER TABLE ${this.escapePath(
192✔
2598
                    table,
192✔
2599
                )} ADD ${this.buildCreateColumnSql(table, column)}`,
192✔
2600
            ),
192✔
2601
        )
192✔
2602

192✔
2603
        // drop enum type
192✔
2604
        if (column.type === "enum" || column.type === "simple-enum") {
192✔
2605
            const hasEnum = await this.hasEnumType(table, column)
20✔
2606
            if (hasEnum) {
20✔
2607
                const enumType = await this.getUserDefinedTypeName(
20✔
2608
                    table,
20✔
2609
                    column,
20✔
2610
                )
20✔
2611
                const escapedEnumName = `"${enumType.schema}"."${enumType.name}"`
20✔
2612
                upQueries.push(
20✔
2613
                    this.dropEnumTypeSql(table, column, escapedEnumName),
20✔
2614
                )
20✔
2615
                downQueries.push(
20✔
2616
                    this.createEnumTypeSql(table, column, escapedEnumName),
20✔
2617
                )
20✔
2618
            }
20✔
2619
        }
20✔
2620

184✔
2621
        if (column.generatedType === "STORED") {
192✔
2622
            const tableNameWithSchema = (
12✔
2623
                await this.getTableNameWithSchema(table.name)
12✔
2624
            ).split(".")
12✔
2625
            const tableName = tableNameWithSchema[1]
12✔
2626
            const schema = tableNameWithSchema[0]
12✔
2627
            const deleteQuery = this.deleteTypeormMetadataSql({
12✔
2628
                database: this.driver.database,
12✔
2629
                schema,
12✔
2630
                table: tableName,
12✔
2631
                type: MetadataTableType.GENERATED_COLUMN,
12✔
2632
                name: column.name,
12✔
2633
            })
12✔
2634
            const insertQuery = this.insertTypeormMetadataSql({
12✔
2635
                database: this.driver.database,
12✔
2636
                schema,
12✔
2637
                table: tableName,
12✔
2638
                type: MetadataTableType.GENERATED_COLUMN,
12✔
2639
                name: column.name,
12✔
2640
                value: column.asExpression,
12✔
2641
            })
12✔
2642

12✔
2643
            upQueries.push(deleteQuery)
12✔
2644
            downQueries.push(insertQuery)
12✔
2645
        }
12✔
2646

184✔
2647
        await this.executeQueries(upQueries, downQueries)
184✔
2648

184✔
2649
        clonedTable.removeColumn(column)
184✔
2650
        this.replaceCachedTable(table, clonedTable)
184✔
2651
    }
184✔
2652

23✔
2653
    /**
23✔
2654
     * Drops the columns in the table.
23✔
2655
     * @param tableOrName
23✔
2656
     * @param columns
23✔
2657
     * @param ifExists
23✔
2658
     */
23✔
2659
    async dropColumns(
23✔
2660
        tableOrName: Table | string,
32✔
2661
        columns: TableColumn[] | string[],
32✔
2662
        ifExists?: boolean,
32✔
2663
    ): Promise<void> {
32✔
2664
        for (const column of [...columns]) {
32✔
2665
            await this.dropColumn(tableOrName, column, ifExists)
64✔
2666
        }
64✔
2667
    }
32✔
2668

23✔
2669
    /**
23✔
2670
     * Creates a new primary key.
23✔
2671
     * @param tableOrName
23✔
2672
     * @param columnNames
23✔
2673
     * @param constraintName
23✔
2674
     */
23✔
2675
    async createPrimaryKey(
23✔
2676
        tableOrName: Table | string,
8✔
2677
        columnNames: string[],
8✔
2678
        constraintName?: string,
8✔
2679
    ): Promise<void> {
8✔
2680
        const table = InstanceChecker.isTable(tableOrName)
8✔
2681
            ? tableOrName
8!
2682
            : await this.getCachedTable(tableOrName)
8✔
2683
        const clonedTable = table.clone()
8✔
2684

8✔
2685
        const up = this.createPrimaryKeySql(table, columnNames, constraintName)
8✔
2686

8✔
2687
        // mark columns as primary, because dropPrimaryKeySql build constraint name from table primary column names.
8✔
2688
        clonedTable.columns.forEach((column) => {
8✔
2689
            if (columnNames.find((columnName) => columnName === column.name))
20✔
2690
                column.isPrimary = true
20✔
2691
        })
8✔
2692
        const down = this.dropPrimaryKeySql(clonedTable)
8✔
2693

8✔
2694
        await this.executeQueries(up, down)
8✔
2695
        this.replaceCachedTable(table, clonedTable)
8✔
2696
    }
8✔
2697

23✔
2698
    /**
23✔
2699
     * Updates composite primary keys.
23✔
2700
     * @param tableOrName
23✔
2701
     * @param columns
23✔
2702
     */
23✔
2703
    async updatePrimaryKeys(
23✔
2704
        tableOrName: Table | string,
12✔
2705
        columns: TableColumn[],
12✔
2706
    ): Promise<void> {
12✔
2707
        const table = InstanceChecker.isTable(tableOrName)
12✔
2708
            ? tableOrName
12✔
2709
            : await this.getCachedTable(tableOrName)
12!
2710
        const clonedTable = table.clone()
×
2711
        const columnNames = columns.map((column) => column.name)
✔
2712
        const upQueries: Query[] = []
×
2713
        const downQueries: Query[] = []
×
2714

×
2715
        // if table already have primary columns, we must drop them.
×
2716
        const primaryColumns = clonedTable.primaryColumns
×
2717
        if (primaryColumns.length > 0) {
12✔
2718
            const pkName = primaryColumns[0].primaryKeyConstraintName
12✔
2719
                ? primaryColumns[0].primaryKeyConstraintName
12!
2720
                : this.connection.namingStrategy.primaryKeyName(
12✔
2721
                      clonedTable,
12✔
2722
                      primaryColumns.map((column) => column.name),
12✔
2723
                  )
12✔
2724

12✔
2725
            const columnNamesString = primaryColumns
12✔
2726
                .map((column) => `"${column.name}"`)
12✔
2727
                .join(", ")
12✔
2728

12✔
2729
            upQueries.push(
12✔
2730
                new Query(
12✔
2731
                    `ALTER TABLE ${this.escapePath(
12✔
2732
                        table,
12✔
2733
                    )} DROP CONSTRAINT "${pkName}"`,
12✔
2734
                ),
12✔
2735
            )
12✔
2736
            downQueries.push(
12✔
2737
                new Query(
12✔
2738
                    `ALTER TABLE ${this.escapePath(
12✔
2739
                        table,
12✔
2740
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
12✔
2741
                ),
12✔
2742
            )
12✔
2743
        }
12✔
2744

12✔
2745
        // update columns in table.
12✔
2746
        clonedTable.columns
12✔
2747
            .filter((column) => columnNames.indexOf(column.name) !== -1)
12✔
2748
            .forEach((column) => {
12✔
2749
                column.isPrimary = true
24✔
2750
            })
12✔
2751

12✔
2752
        const pkName = primaryColumns[0]?.primaryKeyConstraintName
12✔
2753
            ? primaryColumns[0].primaryKeyConstraintName
12!
2754
            : this.connection.namingStrategy.primaryKeyName(
12✔
2755
                  clonedTable,
12✔
2756
                  columnNames,
12✔
2757
              )
12✔
2758

12✔
2759
        const columnNamesString = columnNames
12✔
2760
            .map((columnName) => `"${columnName}"`)
12✔
2761
            .join(", ")
12✔
2762

12✔
2763
        upQueries.push(
12✔
2764
            new Query(
12✔
2765
                `ALTER TABLE ${this.escapePath(
12✔
2766
                    table,
12✔
2767
                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
12✔
2768
            ),
12✔
2769
        )
12✔
2770
        downQueries.push(
12✔
2771
            new Query(
12✔
2772
                `ALTER TABLE ${this.escapePath(
12✔
2773
                    table,
12✔
2774
                )} DROP CONSTRAINT "${pkName}"`,
12✔
2775
            ),
12✔
2776
        )
12✔
2777

12✔
2778
        await this.executeQueries(upQueries, downQueries)
12✔
2779
        this.replaceCachedTable(table, clonedTable)
12✔
2780
    }
12✔
2781

23✔
2782
    /**
23✔
2783
     * Drops a primary key.
23✔
2784
     * @param tableOrName
23✔
2785
     * @param constraintName
23✔
2786
     * @param ifExists
23✔
2787
     */
23✔
2788
    async dropPrimaryKey(
23✔
2789
        tableOrName: Table | string,
16✔
2790
        constraintName?: string,
16✔
2791
        ifExists?: boolean,
16✔
2792
    ): Promise<void> {
16✔
2793
        const table = InstanceChecker.isTable(tableOrName)
16✔
2794
            ? tableOrName
16✔
2795
            : await this.getCachedTable(tableOrName)
16✔
2796
        const up = this.dropPrimaryKeySql(table, ifExists)
4✔
2797
        const down = this.createPrimaryKeySql(
4✔
2798
            table,
4✔
2799
            table.primaryColumns.map((column) => column.name),
4✔
2800
            constraintName,
4✔
2801
        )
4✔
2802
        await this.executeQueries(up, down)
4✔
2803
        table.primaryColumns.forEach((column) => {
16✔
2804
            column.isPrimary = false
16✔
2805
        })
16✔
2806
    }
16✔
2807

23✔
2808
    /**
23✔
2809
     * Creates new unique constraint.
23✔
2810
     * @param tableOrName
23✔
2811
     * @param uniqueConstraint
23✔
2812
     */
23✔
2813
    async createUniqueConstraint(
23✔
2814
        tableOrName: Table | string,
40✔
2815
        uniqueConstraint: TableUnique,
40✔
2816
    ): Promise<void> {
40✔
2817
        const table = InstanceChecker.isTable(tableOrName)
40✔
2818
            ? tableOrName
40✔
2819
            : await this.getCachedTable(tableOrName)
40✔
2820

24✔
2821
        // new unique constraint may be passed without name. In this case we generate unique name manually.
24✔
2822
        if (!uniqueConstraint.name)
24✔
2823
            uniqueConstraint.name =
40✔
2824
                this.connection.namingStrategy.uniqueConstraintName(
8✔
2825
                    table,
8✔
2826
                    uniqueConstraint.columnNames,
8✔
2827
                )
8✔
2828

40✔
2829
        const up = this.createUniqueConstraintSql(table, uniqueConstraint)
40✔
2830
        const down = this.dropUniqueConstraintSql(table, uniqueConstraint)
40✔
2831
        await this.executeQueries(up, down)
40✔
2832
        table.addUniqueConstraint(uniqueConstraint)
40✔
2833
    }
40✔
2834

23✔
2835
    /**
23✔
2836
     * Creates new unique constraints.
23✔
2837
     * @param tableOrName
23✔
2838
     * @param uniqueConstraints
23✔
2839
     */
23✔
2840
    async createUniqueConstraints(
23✔
2841
        tableOrName: Table | string,
20✔
2842
        uniqueConstraints: TableUnique[],
20✔
2843
    ): Promise<void> {
20✔
2844
        for (const uniqueConstraint of uniqueConstraints) {
20✔
2845
            await this.createUniqueConstraint(tableOrName, uniqueConstraint)
32✔
2846
        }
32✔
2847
    }
20✔
2848

23✔
2849
    /**
23✔
2850
     * Drops unique constraint.
23✔
2851
     * @param tableOrName
23✔
2852
     * @param uniqueOrName
23✔
2853
     * @param ifExists
23✔
2854
     */
23✔
2855
    async dropUniqueConstraint(
23✔
2856
        tableOrName: Table | string,
44✔
2857
        uniqueOrName: TableUnique | string,
44✔
2858
        ifExists?: boolean,
44✔
2859
    ): Promise<void> {
44✔
2860
        const table = InstanceChecker.isTable(tableOrName)
44✔
2861
            ? tableOrName
44✔
2862
            : await this.getCachedTable(tableOrName)
44✔
2863
        const uniqueConstraint = InstanceChecker.isTableUnique(uniqueOrName)
20✔
2864
            ? uniqueOrName
44✔
2865
            : table.uniques.find((u) => u.name === uniqueOrName)
44✔
2866
        if (!uniqueConstraint) {
44✔
2867
            if (ifExists) return
4✔
2868
            throw new TypeORMError(
×
2869
                `Supplied unique constraint was not found in table ${table.name}`,
×
2870
            )
×
NEW
2871
        }
×
2872

40✔
2873
        const up = this.dropUniqueConstraintSql(
40✔
2874
            table,
40✔
2875
            uniqueConstraint,
40✔
2876
            ifExists,
40✔
2877
        )
40✔
2878
        const down = this.createUniqueConstraintSql(table, uniqueConstraint)
40✔
2879
        await this.executeQueries(up, down)
40✔
2880
        table.removeUniqueConstraint(uniqueConstraint)
40✔
2881
    }
40✔
2882

23✔
2883
    /**
23✔
2884
     * Drops unique constraints.
23✔
2885
     * @param tableOrName
23✔
2886
     * @param uniqueConstraints
23✔
2887
     * @param ifExists
23✔
2888
     */
23✔
2889
    async dropUniqueConstraints(
23✔
2890
        tableOrName: Table | string,
24✔
2891
        uniqueConstraints: TableUnique[],
24✔
2892
        ifExists?: boolean,
24✔
2893
    ): Promise<void> {
24✔
2894
        for (const uniqueConstraint of [...uniqueConstraints]) {
24✔
2895
            await this.dropUniqueConstraint(
36✔
2896
                tableOrName,
36✔
2897
                uniqueConstraint,
36✔
2898
                ifExists,
36✔
2899
            )
36✔
2900
        }
36✔
2901
    }
24✔
2902

23✔
2903
    /**
23✔
2904
     * Creates new check constraint.
23✔
2905
     * @param tableOrName
23✔
2906
     * @param checkConstraint
23✔
2907
     */
23✔
2908
    async createCheckConstraint(
23✔
2909
        tableOrName: Table | string,
20✔
2910
        checkConstraint: TableCheck,
20✔
2911
    ): Promise<void> {
20✔
2912
        const table = InstanceChecker.isTable(tableOrName)
20✔
2913
            ? tableOrName
20✔
2914
            : await this.getCachedTable(tableOrName)
20✔
2915

12✔
2916
        // new unique constraint may be passed without name. In this case we generate unique name manually.
12✔
2917
        if (!checkConstraint.name)
12✔
2918
            checkConstraint.name =
12✔
2919
                this.connection.namingStrategy.checkConstraintName(
12✔
2920
                    table,
12✔
2921
                    checkConstraint.expression!,
12✔
2922
                )
12✔
2923

20✔
2924
        const up = this.createCheckConstraintSql(table, checkConstraint)
20✔
2925
        const down = this.dropCheckConstraintSql(table, checkConstraint)
20✔
2926
        await this.executeQueries(up, down)
20✔
2927
        table.addCheckConstraint(checkConstraint)
20✔
2928
    }
20✔
2929

23✔
2930
    /**
23✔
2931
     * Creates new check constraints.
23✔
2932
     * @param tableOrName
23✔
2933
     * @param checkConstraints
23✔
2934
     */
23✔
2935
    async createCheckConstraints(
23✔
2936
        tableOrName: Table | string,
8✔
2937
        checkConstraints: TableCheck[],
8✔
2938
    ): Promise<void> {
8✔
2939
        const promises = checkConstraints.map((checkConstraint) =>
8✔
2940
            this.createCheckConstraint(tableOrName, checkConstraint),
8✔
2941
        )
8✔
2942
        await Promise.all(promises)
8✔
2943
    }
8✔
2944

23✔
2945
    /**
23✔
2946
     * Drops check constraint.
23✔
2947
     * @param tableOrName
23✔
2948
     * @param checkOrName
23✔
2949
     * @param ifExists
23✔
2950
     */
23✔
2951
    async dropCheckConstraint(
23✔
2952
        tableOrName: Table | string,
16✔
2953
        checkOrName: TableCheck | string,
16✔
2954
        ifExists?: boolean,
16✔
2955
    ): Promise<void> {
16✔
2956
        const table = InstanceChecker.isTable(tableOrName)
16✔
2957
            ? tableOrName
16✔
2958
            : await this.getCachedTable(tableOrName)
16✔
2959
        const checkConstraint = InstanceChecker.isTableCheck(checkOrName)
4✔
2960
            ? checkOrName
16✔
2961
            : table.checks.find((c) => c.name === checkOrName)
16✔
2962
        if (!checkConstraint) {
16✔
2963
            if (ifExists) return
4✔
2964
            throw new TypeORMError(
×
2965
                `Supplied check constraint was not found in table ${table.name}`,
×
2966
            )
×
NEW
2967
        }
×
2968

12✔
2969
        const up = this.dropCheckConstraintSql(table, checkConstraint, ifExists)
12✔
2970
        const down = this.createCheckConstraintSql(table, checkConstraint)
12✔
2971
        await this.executeQueries(up, down)
12✔
2972
        table.removeCheckConstraint(checkConstraint)
12✔
2973
    }
12✔
2974

23✔
2975
    /**
23✔
2976
     * Drops check constraints.
23✔
2977
     * @param tableOrName
23✔
2978
     * @param checkConstraints
23✔
2979
     * @param ifExists
23✔
2980
     */
23✔
2981
    async dropCheckConstraints(
23✔
2982
        tableOrName: Table | string,
8✔
2983
        checkConstraints: TableCheck[],
8✔
2984
        ifExists?: boolean,
8✔
2985
    ): Promise<void> {
8✔
2986
        const promises = checkConstraints.map((checkConstraint) =>
8✔
2987
            this.dropCheckConstraint(tableOrName, checkConstraint, ifExists),
8✔
2988
        )
8✔
2989
        await Promise.all(promises)
8✔
2990
    }
8✔
2991

23✔
2992
    /**
23✔
2993
     * Creates new exclusion constraint.
23✔
2994
     * @param tableOrName
23✔
2995
     * @param exclusionConstraint
23✔
2996
     */
23✔
2997
    async createExclusionConstraint(
23✔
2998
        tableOrName: Table | string,
16✔
2999
        exclusionConstraint: TableExclusion,
16✔
3000
    ): Promise<void> {
16✔
3001
        const table = InstanceChecker.isTable(tableOrName)
16✔
3002
            ? tableOrName
16✔
3003
            : await this.getCachedTable(tableOrName)
16✔
3004

8✔
3005
        // new unique constraint may be passed without name. In this case we generate unique name manually.
8✔
3006
        if (!exclusionConstraint.name)
8✔
3007
            exclusionConstraint.name =
8✔
3008
                this.connection.namingStrategy.exclusionConstraintName(
8✔
3009
                    table,
8✔
3010
                    exclusionConstraint.expression!,
8✔
3011
                )
8✔
3012

16✔
3013
        const up = this.createExclusionConstraintSql(table, exclusionConstraint)
16✔
3014
        const down = this.dropExclusionConstraintSql(table, exclusionConstraint)
16✔
3015
        await this.executeQueries(up, down)
16✔
3016
        table.addExclusionConstraint(exclusionConstraint)
16✔
3017
    }
16✔
3018

23✔
3019
    /**
23✔
3020
     * Creates new exclusion constraints.
23✔
3021
     * @param tableOrName
23✔
3022
     * @param exclusionConstraints
23✔
3023
     */
23✔
3024
    async createExclusionConstraints(
23✔
3025
        tableOrName: Table | string,
12✔
3026
        exclusionConstraints: TableExclusion[],
12✔
3027
    ): Promise<void> {
12✔
3028
        const promises = exclusionConstraints.map((exclusionConstraint) =>
12✔
3029
            this.createExclusionConstraint(tableOrName, exclusionConstraint),
12✔
3030
        )
12✔
3031
        await Promise.all(promises)
12✔
3032
    }
12✔
3033

23✔
3034
    /**
23✔
3035
     * Drops exclusion constraint.
23✔
3036
     * @param tableOrName
23✔
3037
     * @param exclusionOrName
23✔
3038
     * @param ifExists
23✔
3039
     */
23✔
3040
    async dropExclusionConstraint(
23✔
3041
        tableOrName: Table | string,
16✔
3042
        exclusionOrName: TableExclusion | string,
16✔
3043
        ifExists?: boolean,
16✔
3044
    ): Promise<void> {
16✔
3045
        const table = InstanceChecker.isTable(tableOrName)
16✔
3046
            ? tableOrName
16✔
3047
            : await this.getCachedTable(tableOrName)
16✔
3048
        const exclusionConstraint = InstanceChecker.isTableExclusion(
4✔
3049
            exclusionOrName,
4✔
3050
        )
4✔
3051
            ? exclusionOrName
16✔
3052
            : table.exclusions.find((c) => c.name === exclusionOrName)
16✔
3053
        if (!exclusionConstraint) {
16✔
3054
            if (ifExists) return
4✔
3055
            throw new TypeORMError(
×
3056
                `Supplied exclusion constraint was not found in table ${table.name}`,
×
3057
            )
×
NEW
3058
        }
×
3059

12✔
3060
        const up = this.dropExclusionConstraintSql(
12✔
3061
            table,
12✔
3062
            exclusionConstraint,
12✔
3063
            ifExists,
12✔
3064
        )
12✔
3065
        const down = this.createExclusionConstraintSql(
12✔
3066
            table,
12✔
3067
            exclusionConstraint,
12✔
3068
        )
12✔
3069
        await this.executeQueries(up, down)
12✔
3070
        table.removeExclusionConstraint(exclusionConstraint)
12✔
3071
    }
12✔
3072

23✔
3073
    /**
23✔
3074
     * Drops exclusion constraints.
23✔
3075
     * @param tableOrName
23✔
3076
     * @param exclusionConstraints
23✔
3077
     * @param ifExists
23✔
3078
     */
23✔
3079
    async dropExclusionConstraints(
23✔
3080
        tableOrName: Table | string,
8✔
3081
        exclusionConstraints: TableExclusion[],
8✔
3082
        ifExists?: boolean,
8✔
3083
    ): Promise<void> {
8✔
3084
        const promises = exclusionConstraints.map((exclusionConstraint) =>
8✔
3085
            this.dropExclusionConstraint(
8✔
3086
                tableOrName,
8✔
3087
                exclusionConstraint,
8✔
3088
                ifExists,
8✔
3089
            ),
8✔
3090
        )
8✔
3091
        await Promise.all(promises)
8✔
3092
    }
8✔
3093

23✔
3094
    /**
23✔
3095
     * Creates a new foreign key.
23✔
3096
     * @param tableOrName
23✔
3097
     * @param foreignKey
23✔
3098
     */
23✔
3099
    async createForeignKey(
23✔
3100
        tableOrName: Table | string,
15,696✔
3101
        foreignKey: TableForeignKey,
15,696✔
3102
    ): Promise<void> {
15,696✔
3103
        const table = InstanceChecker.isTable(tableOrName)
15,696✔
3104
            ? tableOrName
15,696✔
3105
            : await this.getCachedTable(tableOrName)
15,696✔
3106

20✔
3107
        // new FK may be passed without name. In this case we generate FK name manually.
20✔
3108
        if (!foreignKey.name)
20✔
3109
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
15,696✔
3110
                table,
4✔
3111
                foreignKey.columnNames,
4✔
3112
                this.getTablePath(foreignKey),
4✔
3113
                foreignKey.referencedColumnNames,
4✔
3114
            )
4✔
3115

15,696✔
3116
        const up = this.createForeignKeySql(table, foreignKey)
15,696✔
3117
        const down = this.dropForeignKeySql(table, foreignKey)
15,696✔
3118
        await this.executeQueries(up, down)
15,696✔
3119
        table.addForeignKey(foreignKey)
15,696✔
3120
    }
15,696✔
3121

23✔
3122
    /**
23✔
3123
     * Creates a new foreign keys.
23✔
3124
     * @param tableOrName
23✔
3125
     * @param foreignKeys
23✔
3126
     */
23✔
3127
    async createForeignKeys(
23✔
3128
        tableOrName: Table | string,
9,500✔
3129
        foreignKeys: TableForeignKey[],
9,500✔
3130
    ): Promise<void> {
9,500✔
3131
        for (const foreignKey of foreignKeys) {
9,500✔
3132
            await this.createForeignKey(tableOrName, foreignKey)
15,692✔
3133
        }
15,692✔
3134
    }
9,500✔
3135

23✔
3136
    /**
23✔
3137
     * Drops a foreign key from the table.
23✔
3138
     * @param tableOrName
23✔
3139
     * @param foreignKeyOrName
23✔
3140
     * @param ifExists
23✔
3141
     */
23✔
3142
    async dropForeignKey(
23✔
3143
        tableOrName: Table | string,
76✔
3144
        foreignKeyOrName: TableForeignKey | string,
76✔
3145
        ifExists?: boolean,
76✔
3146
    ): Promise<void> {
76✔
3147
        const table = InstanceChecker.isTable(tableOrName)
76✔
3148
            ? tableOrName
76✔
3149
            : await this.getCachedTable(tableOrName)
76✔
3150
        const foreignKey = InstanceChecker.isTableForeignKey(foreignKeyOrName)
20✔
3151
            ? foreignKeyOrName
76✔
3152
            : table.foreignKeys.find((fk) => fk.name === foreignKeyOrName)
76✔
3153
        if (!foreignKey) {
76✔
3154
            if (ifExists) return
4✔
3155
            throw new TypeORMError(
×
3156
                `Supplied foreign key was not found in table ${table.name}`,
×
3157
            )
×
NEW
3158
        }
×
3159

72✔
3160
        if (!foreignKey.name) {
76!
3161
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
×
3162
                table,
×
3163
                foreignKey.columnNames,
×
3164
                this.getTablePath(foreignKey),
×
3165
                foreignKey.referencedColumnNames,
×
3166
            )
×
3167
        }
×
3168

72✔
3169
        const up = this.dropForeignKeySql(table, foreignKey, ifExists)
72✔
3170
        const down = this.createForeignKeySql(table, foreignKey)
72✔
3171
        await this.executeQueries(up, down)
72✔
3172
        table.removeForeignKey(foreignKey)
72✔
3173
    }
72✔
3174

23✔
3175
    /**
23✔
3176
     * Drops a foreign keys from the table.
23✔
3177
     * @param tableOrName
23✔
3178
     * @param foreignKeys
23✔
3179
     * @param ifExists
23✔
3180
     */
23✔
3181
    async dropForeignKeys(
23✔
3182
        tableOrName: Table | string,
56✔
3183
        foreignKeys: TableForeignKey[],
56✔
3184
        ifExists?: boolean,
56✔
3185
    ): Promise<void> {
56✔
3186
        for (const foreignKey of [...foreignKeys]) {
56✔
3187
            await this.dropForeignKey(tableOrName, foreignKey, ifExists)
68✔
3188
        }
68✔
3189
    }
56✔
3190

23✔
3191
    /**
23✔
3192
     * Creates a new index.
23✔
3193
     * @param tableOrName
23✔
3194
     * @param index
23✔
3195
     */
23✔
3196
    async createIndex(
23✔
3197
        tableOrName: Table | string,
64✔
3198
        index: TableIndex,
64✔
3199
    ): Promise<void> {
64✔
3200
        const table = InstanceChecker.isTable(tableOrName)
64✔
3201
            ? tableOrName
64✔
3202
            : await this.getCachedTable(tableOrName)
64✔
3203

28✔
3204
        // new index may be passed without name. In this case we generate index name manually.
28✔
3205
        if (!index.name) index.name = this.generateIndexName(table, index)
64✔
3206

64✔
3207
        const up = this.createIndexSql(table, index)
64✔
3208
        const down = this.dropIndexSql(table, index)
64✔
3209
        await this.executeQueries(up, down)
64✔
3210
        table.addIndex(index)
64✔
3211
    }
64✔
3212

23✔
3213
    /**
23✔
3214
     * Create a new view index.
23✔
3215
     * @param viewOrName
23✔
3216
     * @param index
23✔
3217
     */
23✔
3218
    async createViewIndex(
23✔
3219
        viewOrName: View | string,
16✔
3220
        index: TableIndex,
16✔
3221
    ): Promise<void> {
16✔
3222
        const view = InstanceChecker.isView(viewOrName)
16✔
3223
            ? viewOrName
16✔
3224
            : await this.getCachedView(viewOrName)
16!
3225

×
3226
        // new index may be passed without name. In this case we generate index name manually.
×
3227
        if (!index.name) index.name = this.generateIndexName(view, index)
×
3228

16✔
3229
        const up = this.createViewIndexSql(view, index)
16✔
3230
        const down = this.dropIndexSql(view, index)
16✔
3231
        await this.executeQueries(up, down)
16✔
3232
        view.addIndex(index)
16✔
3233
    }
16✔
3234

23✔
3235
    /**
23✔
3236
     * Creates a new indices
23✔
3237
     * @param tableOrName
23✔
3238
     * @param indices
23✔
3239
     */
23✔
3240
    async createIndices(
23✔
3241
        tableOrName: Table | string,
36✔
3242
        indices: TableIndex[],
36✔
3243
    ): Promise<void> {
36✔
3244
        for (const index of indices) {
36✔
3245
            await this.createIndex(tableOrName, index)
48✔
3246
        }
48✔
3247
    }
36✔
3248

23✔
3249
    /**
23✔
3250
     * Creates new view indices
23✔
3251
     * @param viewOrName
23✔
3252
     * @param indices
23✔
3253
     */
23✔
3254
    async createViewIndices(
23✔
3255
        viewOrName: View | string,
16✔
3256
        indices: TableIndex[],
16✔
3257
    ): Promise<void> {
16✔
3258
        for (const index of indices) {
16✔
3259
            await this.createViewIndex(viewOrName, index)
16✔
3260
        }
16✔
3261
    }
16✔
3262

23✔
3263
    /**
23✔
3264
     * Drops an index from the table.
23✔
3265
     * @param tableOrName
23✔
3266
     * @param indexOrName
23✔
3267
     * @param ifExists
23✔
3268
     */
23✔
3269
    async dropIndex(
23✔
3270
        tableOrName: Table | string,
80✔
3271
        indexOrName: TableIndex | string,
80✔
3272
        ifExists?: boolean,
80✔
3273
    ): Promise<void> {
80✔
3274
        const table = InstanceChecker.isTable(tableOrName)
80✔
3275
            ? tableOrName
80✔
3276
            : await this.getCachedTable(tableOrName)
80✔
3277
        const index = InstanceChecker.isTableIndex(indexOrName)
24✔
3278
            ? indexOrName
80✔
3279
            : table.indices.find((i) => i.name === indexOrName)
80✔
3280
        if (!index) {
80✔
3281
            if (ifExists) return
4✔
3282
            throw new TypeORMError(
×
3283
                `Supplied index ${indexOrName} was not found in table ${table.name}`,
×
3284
            )
×
NEW
3285
        }
×
3286
        // old index may be passed without name. In this case we generate index name manually.
76✔
3287
        if (!index.name) index.name = this.generateIndexName(table, index)
80✔
3288

76✔
3289
        const up = this.dropIndexSql(table, index, ifExists)
76✔
3290
        const down = this.createIndexSql(table, index)
76✔
3291
        await this.executeQueries(up, down)
76✔
3292
        table.removeIndex(index)
76✔
3293
    }
76✔
3294

23✔
3295
    /**
23✔
3296
     * Drops an index from a view.
23✔
3297
     * @param viewOrName
23✔
3298
     * @param indexOrName
23✔
3299
     */
23✔
3300
    async dropViewIndex(
23✔
3301
        viewOrName: View | string,
12✔
3302
        indexOrName: TableIndex | string,
12✔
3303
    ): Promise<void> {
12✔
3304
        const view = InstanceChecker.isView(viewOrName)
12✔
3305
            ? viewOrName
12✔
3306
            : await this.getCachedView(viewOrName)
12!
3307
        const index = InstanceChecker.isTableIndex(indexOrName)
×
3308
            ? indexOrName
12✔
3309
            : view.indices.find((i) => i.name === indexOrName)
12!
3310
        if (!index)
12✔
3311
            throw new TypeORMError(
12!
3312
                `Supplied index ${indexOrName} was not found in view ${view.name}`,
×
3313
            )
×
3314
        // old index may be passed without name. In this case we generate index name manually.
12✔
3315
        if (!index.name) index.name = this.generateIndexName(view, index)
12!
3316

12✔
3317
        const up = this.dropIndexSql(view, index)
12✔
3318
        const down = this.createViewIndexSql(view, index)
12✔
3319
        await this.executeQueries(up, down)
12✔
3320
        view.removeIndex(index)
12✔
3321
    }
12✔
3322

23✔
3323
    /**
23✔
3324
     * Drops an indices from the table.
23✔
3325
     * @param tableOrName
23✔
3326
     * @param indices
23✔
3327
     * @param ifExists
23✔
3328
     */
23✔
3329
    async dropIndices(
23✔
3330
        tableOrName: Table | string,
8✔
3331
        indices: TableIndex[],
8✔
3332
        ifExists?: boolean,
8✔
3333
    ): Promise<void> {
8✔
3334
        for (const index of [...indices]) {
8✔
3335
            await this.dropIndex(tableOrName, index, ifExists)
24✔
3336
        }
24✔
3337
    }
8✔
3338

23✔
3339
    /**
23✔
3340
     * Clears all table contents.
23✔
3341
     * Note: this operation uses SQL's TRUNCATE query which cannot be reverted in transactions.
23✔
3342
     * @param tableName
23✔
3343
     * @param options
23✔
3344
     * @param options.cascade
23✔
3345
     */
23✔
3346
    async clearTable(
23✔
3347
        tableName: string,
36✔
3348
        options?: { cascade?: boolean },
36✔
3349
    ): Promise<void> {
36✔
3350
        const cascade = options?.cascade ? " CASCADE" : ""
36✔
3351
        await this.query(
36✔
3352
            `TRUNCATE TABLE ${this.escapePath(tableName)}${cascade}`,
36✔
3353
        )
36✔
3354
    }
32✔
3355

23✔
3356
    /**
23✔
3357
     * Removes all tables from the currently connected database.
23✔
3358
     */
23✔
3359
    async clearDatabase(): Promise<void> {
23✔
3360
        const schemas: string[] = []
7,660✔
3361
        this.connection.entityMetadatas
7,660✔
3362
            .filter((metadata) => metadata.schema)
7,660✔
3363
            .forEach((metadata) => {
7,660✔
3364
                const isSchemaExist = !!schemas.find(
192✔
3365
                    (schema) => schema === metadata.schema,
192✔
3366
                )
192✔
3367
                if (!isSchemaExist) schemas.push(metadata.schema!)
192✔
3368
            })
7,660✔
3369
        schemas.push(this.driver.options.schema || "current_schema()")
7,660✔
3370
        const schemaNamesString = schemas
7,660✔
3371
            .map((name) => {
7,660✔
3372
                return name === "current_schema()" ? name : "'" + name + "'"
7,808✔
3373
            })
7,660✔
3374
            .join(", ")
7,660✔
3375

7,660✔
3376
        const isAnotherTransactionActive = this.isTransactionActive
7,660✔
3377
        if (!isAnotherTransactionActive) await this.startTransaction()
7,660✔
3378
        try {
7,660✔
3379
            // drop views
7,660✔
3380
            const selectViewDropsQuery =
7,660✔
3381
                `SELECT 'DROP VIEW IF EXISTS ' || quote_ident(schemaname) || '.' || quote_ident(viewname) || ' CASCADE;' as "query" ` +
7,660✔
3382
                `FROM "pg_views" WHERE "schemaname" IN (${schemaNamesString}) AND "viewname" NOT IN ('geography_columns', 'geometry_columns', 'raster_columns', 'raster_overviews')`
7,660✔
3383
            const dropViewQueries: ObjectLiteral[] =
7,660✔
3384
                await this.query(selectViewDropsQuery)
7,660✔
3385
            for (const q of dropViewQueries) {
7,660✔
3386
                await this.query(q["query"])
88✔
3387
            }
88✔
3388

7,660✔
3389
            // drop materialized views
7,660✔
3390
            // Note: materialized views introduced in Postgres 9.3
7,660✔
3391
            if (DriverUtils.isReleaseVersionOrGreater(this.driver, "9.3")) {
7,660✔
3392
                const selectMatViewDropsQuery =
7,660✔
3393
                    `SELECT 'DROP MATERIALIZED VIEW IF EXISTS ' || quote_ident(schemaname) || '.' || quote_ident(matviewname) || ' CASCADE;' as "query" ` +
7,660✔
3394
                    `FROM "pg_matviews" WHERE "schemaname" IN (${schemaNamesString})`
7,660✔
3395
                const dropMatViewQueries: ObjectLiteral[] = await this.query(
7,660✔
3396
                    selectMatViewDropsQuery,
7,660✔
3397
                )
7,660✔
3398
                for (const q of dropMatViewQueries) {
7,660✔
3399
                    await this.query(q["query"])
44✔
3400
                }
44✔
3401
            }
7,660✔
3402

7,660✔
3403
            // ignore spatial_ref_sys; it's a special table supporting PostGIS
7,660✔
3404
            // TODO generalize this as this.driver.ignoreTables
7,660✔
3405

7,660✔
3406
            // drop tables
7,660✔
3407
            const selectTableDropsQuery = `SELECT 'DROP TABLE IF EXISTS ' || quote_ident(schemaname) || '.' || quote_ident(tablename) || ' CASCADE;' as "query" FROM "pg_tables" WHERE "schemaname" IN (${schemaNamesString}) AND "tablename" NOT IN ('spatial_ref_sys')`
7,660✔
3408
            const dropTableQueries: ObjectLiteral[] = await this.query(
7,660✔
3409
                selectTableDropsQuery,
7,660✔
3410
            )
7,660✔
3411
            for (const q of dropTableQueries) {
7,660✔
3412
                await this.query(q["query"])
24,116✔
3413
            }
24,116✔
3414

7,660✔
3415
            // drop enum types
7,660✔
3416
            await this.dropEnumTypes(schemaNamesString)
7,660✔
3417

7,660✔
3418
            if (!isAnotherTransactionActive) {
7,660✔
3419
                await this.commitTransaction()
7,656✔
3420
            }
7,656✔
3421
        } catch (error) {
7,660!
3422
            try {
×
3423
                // we throw original error even if rollback thrown an error
×
3424
                if (!isAnotherTransactionActive) {
×
3425
                    await this.rollbackTransaction()
×
3426
                }
×
3427
            } catch {
×
3428
                // no-op
×
3429
            }
×
3430
            throw error
×
3431
        }
×
3432
    }
7,660✔
3433

23✔
3434
    // -------------------------------------------------------------------------
23✔
3435
    // Protected Methods
23✔
3436
    // -------------------------------------------------------------------------
23✔
3437

23✔
3438
    protected async loadViews(viewNames?: string[]): Promise<View[]> {
23✔
3439
        const hasTable = await this.hasTable(this.getTypeormMetadataTableName())
8,228✔
3440

8,228✔
3441
        if (!hasTable) return []
8,228✔
3442

196✔
3443
        if (!viewNames) {
8,228!
3444
            viewNames = []
×
3445
        }
×
3446

196✔
3447
        const currentDatabase = await this.getCurrentDatabase()
196✔
3448
        const currentSchema = await this.getCurrentSchema()
196✔
3449
        const viewsCondition =
196✔
3450
            viewNames.length === 0
196✔
3451
                ? "1=1"
8,228✔
3452
                : viewNames
8,228✔
3453
                      .map((tableName) => this.driver.parseTableName(tableName))
164✔
3454
                      .map(({ schema, tableName }) => {
164✔
3455
                          if (!schema) {
236!
3456
                              schema =
×
3457
                                  this.driver.options.schema || currentSchema
×
3458
                          }
×
3459

236✔
3460
                          return `("t"."schema" = '${schema}' AND "t"."name" = '${tableName}')`
236✔
3461
                      })
164✔
3462
                      .join(" OR ")
164✔
3463

8,228✔
3464
        const constraintsCondition =
8,228✔
3465
            viewNames.length === 0
8,228✔
3466
                ? "1=1"
8,228✔
3467
                : viewNames
8,228✔
3468
                      .map((tableName) => this.driver.parseTableName(tableName))
164✔
3469
                      .map(({ schema, tableName }) => {
164✔
3470
                          if (!schema) {
236!
3471
                              schema =
×
3472
                                  this.driver.options.schema || currentSchema
×
3473
                          }
×
3474

236✔
3475
                          return `("ns"."nspname" = '${schema}' AND "t"."relname" = '${tableName}')`
236✔
3476
                      })
164✔
3477
                      .join(" OR ")
164✔
3478

8,228✔
3479
        const indicesSql =
8,228✔
3480
            `SELECT "ns"."nspname" AS "table_schema", "t"."relname" AS "table_name", "i"."relname" AS "constraint_name", "a"."attname" AS "column_name", ` +
8,228✔
3481
            `CASE "ix"."indisunique" WHEN 't' THEN 'TRUE' ELSE'FALSE' END AS "is_unique", pg_get_expr("ix"."indpred", "ix"."indrelid") AS "condition", ` +
8,228✔
3482
            `"types"."typname" AS "type_name", "am"."amname" AS "index_type" ` +
8,228✔
3483
            `FROM "pg_class" "t" ` +
8,228✔
3484
            `INNER JOIN "pg_index" "ix" ON "ix"."indrelid" = "t"."oid" ` +
8,228✔
3485
            `INNER JOIN "pg_attribute" "a" ON "a"."attrelid" = "t"."oid"  AND "a"."attnum" = ANY ("ix"."indkey") ` +
8,228✔
3486
            `INNER JOIN "pg_namespace" "ns" ON "ns"."oid" = "t"."relnamespace" ` +
8,228✔
3487
            `INNER JOIN "pg_class" "i" ON "i"."oid" = "ix"."indexrelid" ` +
8,228✔
3488
            `INNER JOIN "pg_type" "types" ON "types"."oid" = "a"."atttypid" ` +
8,228✔
3489
            `INNER JOIN "pg_am" "am" ON "i"."relam" = "am"."oid" ` +
8,228✔
3490
            `LEFT JOIN "pg_constraint" "cnst" ON "cnst"."conname" = "i"."relname" ` +
8,228✔
3491
            `WHERE "t"."relkind" IN ('m') AND "cnst"."contype" IS NULL AND (${constraintsCondition})`
8,228✔
3492

8,228✔
3493
        const query =
8,228✔
3494
            `SELECT "t".* FROM ${this.escapePath(
8,228✔
3495
                this.getTypeormMetadataTableName(),
8,228✔
3496
            )} "t" ` +
8,228✔
3497
            `INNER JOIN "pg_catalog"."pg_class" "c" ON "c"."relname" = "t"."name" ` +
8,228✔
3498
            `INNER JOIN "pg_namespace" "n" ON "n"."oid" = "c"."relnamespace" AND "n"."nspname" = "t"."schema" ` +
8,228✔
3499
            `WHERE "t"."type" IN ('${MetadataTableType.VIEW}', '${
8,228✔
3500
                MetadataTableType.MATERIALIZED_VIEW
8,228✔
3501
            }') ${viewsCondition ? `AND (${viewsCondition})` : ""}`
8,228!
3502

8,228✔
3503
        const dbViews = await this.query(query)
8,228✔
3504
        const dbIndices: ObjectLiteral[] = await this.query(indicesSql)
196✔
3505
        return dbViews.map((dbView: any) => {
196✔
3506
            // find index constraints of table, group them by constraint name and build TableIndex.
88✔
3507
            const tableIndexConstraints = OrmUtils.uniq(
88✔
3508
                dbIndices.filter((dbIndex) => {
88✔
3509
                    return (
28✔
3510
                        dbIndex["table_name"] === dbView["name"] &&
28✔
3511
                        dbIndex["table_schema"] === dbView["schema"]
28✔
3512
                    )
28✔
3513
                }),
88✔
3514
                (dbIndex) => dbIndex["constraint_name"],
88✔
3515
            )
88✔
3516
            const view = new View()
88✔
3517
            const schema =
88✔
3518
                dbView["schema"] === currentSchema &&
88✔
3519
                !this.driver.options.schema
88✔
3520
                    ? undefined
88✔
3521
                    : dbView["schema"]
88!
3522
            view.database = currentDatabase
88✔
3523
            view.schema = dbView["schema"]
88✔
3524
            view.name = this.driver.buildTableName(dbView["name"], schema)
88✔
3525
            view.expression = dbView["value"]
88✔
3526
            view.materialized =
88✔
3527
                dbView["type"] === MetadataTableType.MATERIALIZED_VIEW
88✔
3528
            view.indices = tableIndexConstraints.map((constraint) => {
88✔
3529
                const indices = dbIndices.filter((index) => {
28✔
3530
                    return (
28✔
3531
                        index["table_schema"] === constraint["table_schema"] &&
28✔
3532
                        index["table_name"] === constraint["table_name"] &&
28✔
3533
                        index["constraint_name"] ===
28✔
3534
                            constraint["constraint_name"]
28✔
3535
                    )
28✔
3536
                })
28✔
3537

28✔
3538
                return new TableIndex(<TableIndexOptions>{
28✔
3539
                    view: view,
28✔
3540
                    name: constraint["constraint_name"],
28✔
3541
                    columnNames: indices.map((i) => i["column_name"]),
28✔
3542
                    isUnique: constraint["is_unique"] === "TRUE",
28✔
3543
                    where: constraint["condition"],
28✔
3544
                    isSpatial: constraint["index_type"] === "gist",
28✔
3545
                    isFulltext: false,
28✔
3546
                    type: constraint["index_type"],
28✔
3547
                })
28✔
3548
            })
88✔
3549
            return view
88✔
3550
        })
196✔
3551
    }
196✔
3552

23✔
3553
    /**
23✔
3554
     * Loads all tables (with given names) from the database and creates a Table from them.
23✔
3555
     * @param tableNames
23✔
3556
     */
23✔
3557
    protected async loadTables(tableNames?: string[]): Promise<Table[]> {
23✔
3558
        // if no tables given then no need to proceed
9,864✔
3559
        if (tableNames && tableNames.length === 0) {
9,864✔
3560
            return []
140✔
3561
        }
140✔
3562

9,724✔
3563
        const currentSchema = await this.getCurrentSchema()
9,724✔
3564
        const currentDatabase = await this.getCurrentDatabase()
9,724✔
3565

9,724✔
3566
        const dbTables: {
9,724✔
3567
            table_schema: string
9,724✔
3568
            table_name: string
9,724✔
3569
            table_comment: string
9,724✔
3570
        }[] = []
9,724✔
3571

9,724✔
3572
        if (!tableNames) {
9,864✔
3573
            const tablesSql = `SELECT "table_schema", "table_name", obj_description((quote_ident("table_schema") || '.' || quote_ident("table_name"))::regclass, 'pg_class') AS table_comment FROM "information_schema"."tables"`
4✔
3574
            dbTables.push(...(await this.query(tablesSql)))
4✔
3575
        } else {
9,864✔
3576
            const tablesCondition = tableNames
9,720✔
3577
                .map((tableName) => this.driver.parseTableName(tableName))
9,720✔
3578
                .map(({ schema, tableName }) => {
9,720✔
3579
                    return `("table_schema" = '${
27,064✔
3580
                        schema || currentSchema
27,064!
3581
                    }' AND "table_name" = '${tableName}')`
27,064✔
3582
                })
9,720✔
3583
                .join(" OR ")
9,720✔
3584

9,720✔
3585
            const tablesSql =
9,720✔
3586
                `SELECT "table_schema", "table_name", obj_description((quote_ident("table_schema") || '.' || quote_ident("table_name"))::regclass, 'pg_class') AS table_comment FROM "information_schema"."tables" WHERE ` +
9,720✔
3587
                tablesCondition
9,720✔
3588
            dbTables.push(...(await this.query(tablesSql)))
9,720✔
3589
        }
9,720✔
3590

9,724✔
3591
        // if tables were not found in the db, no need to proceed
9,724✔
3592
        if (dbTables.length === 0) {
9,864✔
3593
            return []
7,624✔
3594
        }
7,624✔
3595

2,100✔
3596
        /**
2,100✔
3597
         * Uses standard SQL information_schema.columns table and postgres-specific
2,100✔
3598
         * pg_catalog.pg_attribute table to get column information.
2,100✔
3599
         * @see https://stackoverflow.com/a/19541865
2,100✔
3600
         */
2,100✔
3601
        const columnsCondition = dbTables
2,100✔
3602
            .map(({ table_schema, table_name }) => {
2,100✔
3603
                return `("table_schema" = '${table_schema}' AND "table_name" = '${table_name}')`
3,852✔
3604
            })
2,100✔
3605
            .join(" OR ")
2,100✔
3606
        const columnsSql =
2,100✔
3607
            `SELECT columns.*, pg_catalog.col_description((quote_ident(table_catalog) || '.' || quote_ident(table_schema) || '.' || quote_ident(table_name))::regclass::oid, ordinal_position) AS description, ` +
2,100✔
3608
            `('"' || "udt_schema" || '"."' || "udt_name" || '"')::"regtype"::text AS "regtype", pg_catalog.format_type("col_attr"."atttypid", "col_attr"."atttypmod") AS "format_type" ` +
2,100✔
3609
            `FROM "information_schema"."columns" ` +
2,100✔
3610
            `LEFT JOIN "pg_catalog"."pg_attribute" AS "col_attr" ON "col_attr"."attname" = "columns"."column_name" ` +
2,100✔
3611
            `AND "col_attr"."attrelid" = ( ` +
2,100✔
3612
            `SELECT "cls"."oid" FROM "pg_catalog"."pg_class" AS "cls" ` +
2,100✔
3613
            `LEFT JOIN "pg_catalog"."pg_namespace" AS "ns" ON "ns"."oid" = "cls"."relnamespace" ` +
2,100✔
3614
            `WHERE "cls"."relname" = "columns"."table_name" ` +
2,100✔
3615
            `AND "ns"."nspname" = "columns"."table_schema" ` +
2,100✔
3616
            `) ` +
2,100✔
3617
            `WHERE ` +
2,100✔
3618
            columnsCondition
2,100✔
3619

2,100✔
3620
        const constraintsCondition = dbTables
2,100✔
3621
            .map(({ table_schema, table_name }) => {
2,100✔
3622
                return `("ns"."nspname" = '${table_schema}' AND "t"."relname" = '${table_name}')`
3,852✔
3623
            })
2,100✔
3624
            .join(" OR ")
2,100✔
3625

2,100✔
3626
        const constraintsSql =
2,100✔
3627
            `SELECT "ns"."nspname" AS "table_schema", "t"."relname" AS "table_name", "cnst"."conname" AS "constraint_name", ` +
2,100✔
3628
            `pg_get_constraintdef("cnst"."oid") AS "expression", ` +
2,100✔
3629
            `CASE "cnst"."contype" WHEN 'p' THEN 'PRIMARY' WHEN 'u' THEN 'UNIQUE' WHEN 'c' THEN 'CHECK' WHEN 'x' THEN 'EXCLUDE' END AS "constraint_type", "a"."attname" AS "column_name" ` +
2,100✔
3630
            `FROM "pg_constraint" "cnst" ` +
2,100✔
3631
            `INNER JOIN "pg_class" "t" ON "t"."oid" = "cnst"."conrelid" ` +
2,100✔
3632
            `INNER JOIN "pg_namespace" "ns" ON "ns"."oid" = "cnst"."connamespace" ` +
2,100✔
3633
            `LEFT JOIN "pg_attribute" "a" ON "a"."attrelid" = "cnst"."conrelid" AND "a"."attnum" = ANY ("cnst"."conkey") ` +
2,100✔
3634
            `WHERE "t"."relkind" IN ('r', 'p') AND (${constraintsCondition})`
2,100✔
3635

2,100✔
3636
        const indicesSql =
2,100✔
3637
            `SELECT "ns"."nspname" AS "table_schema", "t"."relname" AS "table_name", "i"."relname" AS "constraint_name", "a"."attname" AS "column_name", ` +
2,100✔
3638
            `CASE "ix"."indisunique" WHEN 't' THEN 'TRUE' ELSE'FALSE' END AS "is_unique", pg_get_expr("ix"."indpred", "ix"."indrelid") AS "condition", ` +
2,100✔
3639
            `"types"."typname" AS "type_name", "am"."amname" AS "index_type" ` +
2,100✔
3640
            `FROM "pg_class" "t" ` +
2,100✔
3641
            `INNER JOIN "pg_index" "ix" ON "ix"."indrelid" = "t"."oid" ` +
2,100✔
3642
            `INNER JOIN "pg_attribute" "a" ON "a"."attrelid" = "t"."oid"  AND "a"."attnum" = ANY ("ix"."indkey") ` +
2,100✔
3643
            `INNER JOIN "pg_namespace" "ns" ON "ns"."oid" = "t"."relnamespace" ` +
2,100✔
3644
            `INNER JOIN "pg_class" "i" ON "i"."oid" = "ix"."indexrelid" ` +
2,100✔
3645
            `INNER JOIN "pg_type" "types" ON "types"."oid" = "a"."atttypid" ` +
2,100✔
3646
            `INNER JOIN "pg_am" "am" ON "i"."relam" = "am"."oid" ` +
2,100✔
3647
            `LEFT JOIN "pg_constraint" "cnst" ON "cnst"."conname" = "i"."relname" ` +
2,100✔
3648
            `WHERE "t"."relkind" IN ('r', 'p') AND "cnst"."contype" IS NULL AND (${constraintsCondition})`
2,100✔
3649

2,100✔
3650
        const foreignKeysCondition = dbTables
2,100✔
3651
            .map(({ table_schema, table_name }) => {
2,100✔
3652
                return `("ns"."nspname" = '${table_schema}' AND "cl"."relname" = '${table_name}')`
3,852✔
3653
            })
2,100✔
3654
            .join(" OR ")
2,100✔
3655

2,100✔
3656
        const hasRelispartitionColumn =
2,100✔
3657
            await this.hasSupportForPartitionedTables()
2,100✔
3658
        const isPartitionCondition = hasRelispartitionColumn
2,100✔
3659
            ? ` AND "cl"."relispartition" = 'f'`
2,100✔
3660
            : ""
9,864!
3661

9,864✔
3662
        const foreignKeysSql =
9,864✔
3663
            `SELECT "con"."conname" AS "constraint_name", "con"."nspname" AS "table_schema", "con"."relname" AS "table_name", "att2"."attname" AS "column_name", ` +
9,864✔
3664
            `"ns"."nspname" AS "referenced_table_schema", "cl"."relname" AS "referenced_table_name", "att"."attname" AS "referenced_column_name", "con"."confdeltype" AS "on_delete", ` +
9,864✔
3665
            `"con"."confupdtype" AS "on_update", "con"."condeferrable" AS "deferrable", "con"."condeferred" AS "deferred" ` +
9,864✔
3666
            `FROM ( ` +
9,864✔
3667
            `SELECT UNNEST ("con1"."conkey") AS "parent", UNNEST ("con1"."confkey") AS "child", "con1"."confrelid", "con1"."conrelid", "con1"."conname", "con1"."contype", "ns"."nspname", ` +
9,864✔
3668
            `"cl"."relname", "con1"."condeferrable", ` +
9,864✔
3669
            `CASE WHEN "con1"."condeferred" THEN 'INITIALLY DEFERRED' ELSE 'INITIALLY IMMEDIATE' END as condeferred, ` +
9,864✔
3670
            `CASE "con1"."confdeltype" WHEN 'a' THEN 'NO ACTION' WHEN 'r' THEN 'RESTRICT' WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' END as "confdeltype", ` +
9,864✔
3671
            `CASE "con1"."confupdtype" WHEN 'a' THEN 'NO ACTION' WHEN 'r' THEN 'RESTRICT' WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' END as "confupdtype" ` +
9,864✔
3672
            `FROM "pg_class" "cl" ` +
9,864✔
3673
            `INNER JOIN "pg_namespace" "ns" ON "cl"."relnamespace" = "ns"."oid" ` +
9,864✔
3674
            `INNER JOIN "pg_constraint" "con1" ON "con1"."conrelid" = "cl"."oid" ` +
9,864✔
3675
            `WHERE "con1"."contype" = 'f' AND (${foreignKeysCondition}) ` +
9,864✔
3676
            `) "con" ` +
9,864✔
3677
            `INNER JOIN "pg_attribute" "att" ON "att"."attrelid" = "con"."confrelid" AND "att"."attnum" = "con"."child" ` +
9,864✔
3678
            `INNER JOIN "pg_class" "cl" ON "cl"."oid" = "con"."confrelid" ${isPartitionCondition}` +
9,864✔
3679
            `INNER JOIN "pg_namespace" "ns" ON "cl"."relnamespace" = "ns"."oid" ` +
9,864✔
3680
            `INNER JOIN "pg_attribute" "att2" ON "att2"."attrelid" = "con"."conrelid" AND "att2"."attnum" = "con"."parent"`
9,864✔
3681

9,864✔
3682
        const dbColumns: ObjectLiteral[] = await this.query(columnsSql)
9,864✔
3683
        const dbConstraints: ObjectLiteral[] = await this.query(constraintsSql)
2,100✔
3684
        const dbIndices: ObjectLiteral[] = await this.query(indicesSql)
2,100✔
3685
        const dbForeignKeys: ObjectLiteral[] = await this.query(foreignKeysSql)
2,100✔
3686

2,100✔
3687
        // create tables for loaded tables
2,100✔
3688
        return Promise.all(
2,100✔
3689
            dbTables.map(async (dbTable) => {
2,100✔
3690
                const table = new Table()
3,852✔
3691

3,852✔
3692
                const getSchemaFromKey = (dbObject: any, key: string) => {
3,852✔
3693
                    return dbObject[key] === currentSchema &&
4,800✔
3694
                        (!this.driver.options.schema ||
3,584✔
3695
                            this.driver.options.schema === currentSchema)
3,584✔
3696
                        ? undefined
4,800✔
3697
                        : dbObject[key]
4,800✔
3698
                }
4,800✔
3699
                // We do not need to join schema name, when database is by default.
3,852✔
3700
                const schema = getSchemaFromKey(dbTable, "table_schema")
3,852✔
3701
                table.database = currentDatabase
3,852✔
3702
                table.schema = dbTable["table_schema"]
3,852✔
3703
                table.comment = dbTable["table_comment"]
3,852✔
3704
                table.name = this.driver.buildTableName(
3,852✔
3705
                    dbTable["table_name"],
3,852✔
3706
                    schema,
3,852✔
3707
                )
3,852✔
3708

3,852✔
3709
                // create columns from the loaded columns
3,852✔
3710
                table.columns = await Promise.all(
3,852✔
3711
                    dbColumns
3,852✔
3712
                        .filter(
3,852✔
3713
                            (dbColumn) =>
3,852✔
3714
                                dbColumn["table_name"] ===
1,946,240✔
3715
                                    dbTable["table_name"] &&
1,946,240✔
3716
                                dbColumn["table_schema"] ===
19,212✔
3717
                                    dbTable["table_schema"],
3,852✔
3718
                        )
3,852✔
3719
                        .map(async (dbColumn) => {
3,852✔
3720
                            const columnConstraints = dbConstraints.filter(
19,084✔
3721
                                (dbConstraint) => {
19,084✔
3722
                                    return (
1,842,248✔
3723
                                        dbConstraint["table_name"] ===
1,842,248✔
3724
                                            dbColumn["table_name"] &&
1,842,248✔
3725
                                        dbConstraint["table_schema"] ===
34,246✔
3726
                                            dbColumn["table_schema"] &&
1,842,248✔
3727
                                        dbConstraint["column_name"] ===
33,982✔
3728
                                            dbColumn["column_name"]
33,982✔
3729
                                    )
1,842,248✔
3730
                                },
19,084✔
3731
                            )
19,084✔
3732

19,084✔
3733
                            const tableColumn = new TableColumn()
19,084✔
3734
                            tableColumn.name = dbColumn["column_name"]
19,084✔
3735
                            tableColumn.type = dbColumn["regtype"].toLowerCase()
19,084✔
3736

19,084✔
3737
                            if (
19,084✔
3738
                                tableColumn.type === "vector" ||
19,084✔
3739
                                tableColumn.type === "halfvec"
19,068✔
3740
                            ) {
19,084✔
3741
                                const lengthMatch = dbColumn[
32✔
3742
                                    "format_type"
32✔
3743
                                ].match(/^(?:vector|halfvec)\((\d+)\)$/)
32✔
3744
                                if (lengthMatch && lengthMatch[1]) {
32✔
3745
                                    tableColumn.length = lengthMatch[1]
16✔
3746
                                }
16✔
3747
                            }
32✔
3748

19,084✔
3749
                            if (
19,084✔
3750
                                tableColumn.type === "numeric" ||
19,084✔
3751
                                tableColumn.type === "numeric[]" ||
19,084✔
3752
                                tableColumn.type === "decimal" ||
19,084✔
3753
                                tableColumn.type === "float"
19,032✔
3754
                            ) {
19,084✔
3755
                                let numericPrecision =
52✔
3756
                                    dbColumn["numeric_precision"]
52✔
3757
                                let numericScale = dbColumn["numeric_scale"]
52✔
3758
                                if (dbColumn["data_type"] === "ARRAY") {
52✔
3759
                                    const numericSize = dbColumn[
4✔
3760
                                        "format_type"
4✔
3761
                                    ].match(
4✔
3762
                                        /^numeric\(([0-9]+),([0-9]+)\)\[\]$/,
4✔
3763
                                    )
4✔
3764
                                    if (numericSize) {
4✔
3765
                                        numericPrecision = +numericSize[1]
4✔
3766
                                        numericScale = +numericSize[2]
4✔
3767
                                    }
4✔
3768
                                }
4✔
3769
                                // If one of these properties was set, and another was not, Postgres sets '0' in to unspecified property
52✔
3770
                                // we set 'undefined' in to unspecified property to avoid changing column on sync
52✔
3771
                                if (
52✔
3772
                                    numericPrecision !== null &&
52✔
3773
                                    !this.isDefaultColumnPrecision(
28✔
3774
                                        table,
28✔
3775
                                        tableColumn,
28✔
3776
                                        numericPrecision,
28✔
3777
                                    )
52✔
3778
                                ) {
52✔
3779
                                    tableColumn.precision = numericPrecision
28✔
3780
                                } else if (
52✔
3781
                                    numericScale !== null &&
24!
3782
                                    !this.isDefaultColumnScale(
×
3783
                                        table,
×
3784
                                        tableColumn,
×
3785
                                        numericScale,
×
3786
                                    )
24✔
3787
                                ) {
24!
3788
                                    tableColumn.precision = undefined
×
3789
                                }
×
3790
                                if (
52✔
3791
                                    numericScale !== null &&
52✔
3792
                                    !this.isDefaultColumnScale(
28✔
3793
                                        table,
28✔
3794
                                        tableColumn,
28✔
3795
                                        numericScale,
28✔
3796
                                    )
52✔
3797
                                ) {
52✔
3798
                                    tableColumn.scale = numericScale
28✔
3799
                                } else if (
52✔
3800
                                    numericPrecision !== null &&
24!
3801
                                    !this.isDefaultColumnPrecision(
×
3802
                                        table,
×
3803
                                        tableColumn,
×
3804
                                        numericPrecision,
×
3805
                                    )
24✔
3806
                                ) {
24!
3807
                                    tableColumn.scale = undefined
×
3808
                                }
×
3809
                            }
52✔
3810

19,084✔
3811
                            if (
19,084✔
3812
                                tableColumn.type === "interval" ||
19,084✔
3813
                                tableColumn.type === "time without time zone" ||
19,084✔
3814
                                tableColumn.type === "time with time zone" ||
19,084✔
3815
                                tableColumn.type ===
18,968✔
3816
                                    "timestamp without time zone" ||
19,084✔
3817
                                tableColumn.type === "timestamp with time zone"
18,836✔
3818
                            ) {
19,084✔
3819
                                tableColumn.precision =
476✔
3820
                                    !this.isDefaultColumnPrecision(
476✔
3821
                                        table,
476✔
3822
                                        tableColumn,
476✔
3823
                                        dbColumn["datetime_precision"],
476✔
3824
                                    )
476✔
3825
                                        ? dbColumn["datetime_precision"]
476✔
3826
                                        : undefined
476✔
3827
                            }
476✔
3828

19,084✔
3829
                            // check if column has user-defined data type.
19,084✔
3830
                            // NOTE: if ENUM type defined with "array:true" it comes with ARRAY type instead of USER-DEFINED
19,084✔
3831
                            if (
19,084✔
3832
                                dbColumn["data_type"] === "USER-DEFINED" ||
19,084✔
3833
                                dbColumn["data_type"] === "ARRAY"
18,424✔
3834
                            ) {
19,084✔
3835
                                const { name } =
1,290✔
3836
                                    await this.getUserDefinedTypeName(
1,290✔
3837
                                        table,
1,290✔
3838
                                        tableColumn,
1,290✔
3839
                                    )
1,290✔
3840

1,290✔
3841
                                // check if `enumName` is specified by user
1,290✔
3842
                                const builtEnumName = this.buildEnumName(
1,290✔
3843
                                    table,
1,290✔
3844
                                    tableColumn,
1,290✔
3845
                                    false,
1,290✔
3846
                                    true,
1,290✔
3847
                                )
1,290✔
3848
                                const enumName =
1,290✔
3849
                                    builtEnumName !== name ? name : undefined
1,290✔
3850

1,290✔
3851
                                // check if type is ENUM
1,290✔
3852
                                const sql =
1,290✔
3853
                                    `SELECT "e"."enumlabel" AS "value" FROM "pg_enum" "e" ` +
1,290✔
3854
                                    `INNER JOIN "pg_type" "t" ON "t"."oid" = "e"."enumtypid" ` +
1,290✔
3855
                                    `INNER JOIN "pg_namespace" "n" ON "n"."oid" = "t"."typnamespace" ` +
1,290✔
3856
                                    `WHERE "n"."nspname" = '${
1,290✔
3857
                                        dbTable["table_schema"]
1,290✔
3858
                                    }' AND "t"."typname" = '${
1,290✔
3859
                                        enumName || name
1,290✔
3860
                                    }'`
1,290✔
3861
                                const results: ObjectLiteral[] =
1,290✔
3862
                                    await this.query(sql)
1,290✔
3863

1,290✔
3864
                                if (results.length) {
1,290✔
3865
                                    tableColumn.type = "enum"
788✔
3866
                                    tableColumn.enum = results.map(
788✔
3867
                                        (result) => result["value"],
788✔
3868
                                    )
788✔
3869
                                    tableColumn.enumName = enumName
788✔
3870
                                }
788✔
3871

1,290✔
3872
                                if (dbColumn["data_type"] === "ARRAY") {
1,290✔
3873
                                    tableColumn.isArray = true
630✔
3874
                                    const type = tableColumn.type.replace(
630✔
3875
                                        "[]",
630✔
3876
                                        "",
630✔
3877
                                    )
630✔
3878
                                    tableColumn.type =
630✔
3879
                                        this.connection.driver.normalizeType({
630✔
3880
                                            type: type,
630✔
3881
                                        })
630✔
3882
                                }
630✔
3883
                            }
1,290✔
3884

19,084✔
3885
                            if (
19,084✔
3886
                                tableColumn.type === "geometry" ||
19,084✔
3887
                                tableColumn.type === "geography"
19,044✔
3888
                            ) {
19,084✔
3889
                                const sql =
52✔
3890
                                    `SELECT * FROM (` +
52✔
3891
                                    `SELECT "f_table_schema" "table_schema", "f_table_name" "table_name", ` +
52✔
3892
                                    `"f_${tableColumn.type}_column" "column_name", "srid", "type" ` +
52✔
3893
                                    `FROM "${tableColumn.type}_columns"` +
52✔
3894
                                    `) AS _ ` +
52✔
3895
                                    `WHERE "column_name" = '${dbColumn["column_name"]}' AND ` +
52✔
3896
                                    `"table_schema" = '${dbColumn["table_schema"]}' AND ` +
52✔
3897
                                    `"table_name" = '${dbColumn["table_name"]}'`
52✔
3898

52✔
3899
                                const results: ObjectLiteral[] =
52✔
3900
                                    await this.query(sql)
52✔
3901

52✔
3902
                                if (results.length > 0) {
52✔
3903
                                    tableColumn.spatialFeatureType =
52✔
3904
                                        results[0].type
52✔
3905
                                    tableColumn.srid = results[0].srid
52✔
3906
                                }
52✔
3907
                            }
52✔
3908

19,084✔
3909
                            // check only columns that have length property
19,084✔
3910
                            if (
19,084✔
3911
                                this.driver.withLengthColumnTypes.indexOf(
19,084✔
3912
                                    tableColumn.type as ColumnType,
19,084✔
3913
                                ) !== -1
19,084✔
3914
                            ) {
19,084✔
3915
                                let length
5,568✔
3916
                                if (tableColumn.isArray) {
5,568✔
3917
                                    const match = /\((\d+)\)/.exec(
32✔
3918
                                        dbColumn["format_type"],
32✔
3919
                                    )
32✔
3920
                                    length = match ? match[1] : undefined
32✔
3921
                                } else if (
5,568✔
3922
                                    dbColumn["character_maximum_length"]
5,536✔
3923
                                ) {
5,536✔
3924
                                    length =
778✔
3925
                                        dbColumn[
778✔
3926
                                            "character_maximum_length"
778✔
3927
                                        ].toString()
778✔
3928
                                }
778✔
3929
                                if (length) {
5,568✔
3930
                                    tableColumn.length =
782✔
3931
                                        !this.isDefaultColumnLength(
782✔
3932
                                            table,
782✔
3933
                                            tableColumn,
782✔
3934
                                            length,
782✔
3935
                                        )
782✔
3936
                                            ? length
782✔
3937
                                            : ""
782✔
3938
                                }
782✔
3939
                            }
5,568✔
3940
                            tableColumn.isNullable =
19,084✔
3941
                                dbColumn["is_nullable"] === "YES"
19,084✔
3942

19,084✔
3943
                            const primaryConstraint = columnConstraints.find(
19,084✔
3944
                                (constraint) =>
19,084✔
3945
                                    constraint["constraint_type"] === "PRIMARY",
19,084✔
3946
                            )
19,084✔
3947
                            if (primaryConstraint) {
19,084✔
3948
                                tableColumn.isPrimary = true
3,448✔
3949
                                // find another columns involved in primary key constraint
3,448✔
3950
                                const anotherPrimaryConstraints =
3,448✔
3951
                                    dbConstraints.filter(
3,448✔
3952
                                        (constraint) =>
3,448✔
3953
                                            constraint["table_name"] ===
105,174✔
3954
                                                dbColumn["table_name"] &&
105,174✔
3955
                                            constraint["table_schema"] ===
8,018✔
3956
                                                dbColumn["table_schema"] &&
105,174✔
3957
                                            constraint["column_name"] !==
7,930✔
3958
                                                dbColumn["column_name"] &&
105,174✔
3959
                                            constraint["constraint_type"] ===
4,266✔
3960
                                                "PRIMARY",
3,448✔
3961
                                    )
3,448✔
3962

3,448✔
3963
                                // collect all column names
3,448✔
3964
                                const columnNames =
3,448✔
3965
                                    anotherPrimaryConstraints.map(
3,448✔
3966
                                        (constraint) =>
3,448✔
3967
                                            constraint["column_name"],
3,448✔
3968
                                    )
3,448✔
3969
                                columnNames.push(dbColumn["column_name"])
3,448✔
3970

3,448✔
3971
                                // build default primary key constraint name
3,448✔
3972
                                const pkName =
3,448✔
3973
                                    this.connection.namingStrategy.primaryKeyName(
3,448✔
3974
                                        table,
3,448✔
3975
                                        columnNames,
3,448✔
3976
                                    )
3,448✔
3977

3,448✔
3978
                                // if primary key has user-defined constraint name, write it in table column
3,448✔
3979
                                if (
3,448✔
3980
                                    primaryConstraint["constraint_name"] !==
3,448✔
3981
                                    pkName
3,448✔
3982
                                ) {
3,448✔
3983
                                    tableColumn.primaryKeyConstraintName =
456✔
3984
                                        primaryConstraint["constraint_name"]
456✔
3985
                                }
456✔
3986
                            }
3,448✔
3987

19,084✔
3988
                            const uniqueConstraints = columnConstraints.filter(
19,084✔
3989
                                (constraint) =>
19,084✔
3990
                                    constraint["constraint_type"] === "UNIQUE",
19,084✔
3991
                            )
19,084✔
3992
                            const isConstraintComposite =
19,084✔
3993
                                uniqueConstraints.every((uniqueConstraint) => {
19,084✔
3994
                                    return dbConstraints.some(
1,974✔
3995
                                        (dbConstraint) =>
1,974✔
3996
                                            dbConstraint["constraint_type"] ===
49,872✔
3997
                                                "UNIQUE" &&
49,872✔
3998
                                            dbConstraint["constraint_name"] ===
26,114✔
3999
                                                uniqueConstraint[
26,114✔
4000
                                                    "constraint_name"
26,114✔
4001
                                                ] &&
49,872✔
4002
                                            dbConstraint["column_name"] !==
2,564✔
4003
                                                dbColumn["column_name"],
1,974✔
4004
                                    )
1,974✔
4005
                                })
19,084✔
4006
                            tableColumn.isUnique =
19,084✔
4007
                                uniqueConstraints.length > 0 &&
19,084✔
4008
                                !isConstraintComposite
1,948✔
4009

19,084✔
4010
                            if (dbColumn.is_identity === "YES") {
19,084!
4011
                                // Postgres 10+ Identity column
×
4012
                                tableColumn.isGenerated = true
×
4013
                                tableColumn.generationStrategy = "identity"
×
4014
                                tableColumn.generatedIdentity =
×
4015
                                    dbColumn.identity_generation
×
4016
                            } else if (
19,084✔
4017
                                dbColumn["column_default"] !== null &&
19,084✔
4018
                                dbColumn["column_default"] !== undefined
2,820✔
4019
                            ) {
19,084✔
4020
                                const serialDefaultName = `nextval('${this.buildSequenceName(
2,820✔
4021
                                    table,
2,820✔
4022
                                    dbColumn["column_name"],
2,820✔
4023
                                )}'::regclass)`
2,820✔
4024
                                const serialDefaultPath = `nextval('${this.buildSequencePath(
2,820✔
4025
                                    table,
2,820✔
4026
                                    dbColumn["column_name"],
2,820✔
4027
                                )}'::regclass)`
2,820✔
4028

2,820✔
4029
                                const defaultWithoutQuotes = dbColumn[
2,820✔
4030
                                    "column_default"
2,820✔
4031
                                ].replace(/"/g, "")
2,820✔
4032

2,820✔
4033
                                if (
2,820✔
4034
                                    defaultWithoutQuotes ===
2,820✔
4035
                                        serialDefaultName ||
2,820✔
4036
                                    defaultWithoutQuotes === serialDefaultPath
1,212✔
4037
                                ) {
2,820✔
4038
                                    tableColumn.isGenerated = true
1,832✔
4039
                                    tableColumn.generationStrategy = "increment"
1,832✔
4040
                                } else if (
2,820✔
4041
                                    dbColumn["column_default"] ===
988✔
4042
                                        "gen_random_uuid()" ||
988✔
4043
                                    /^uuid_generate_v\d\(\)/.test(
964✔
4044
                                        dbColumn["column_default"],
964✔
4045
                                    )
988✔
4046
                                ) {
988✔
4047
                                    if (tableColumn.type === "uuid") {
120✔
4048
                                        tableColumn.isGenerated = true
112✔
4049
                                        tableColumn.generationStrategy = "uuid"
112✔
4050
                                    } else {
120✔
4051
                                        tableColumn.default =
8✔
4052
                                            dbColumn["column_default"]
8✔
4053
                                    }
8✔
4054
                                } else if (
988✔
4055
                                    dbColumn["column_default"] === "now()" ||
868✔
4056
                                    dbColumn["column_default"].indexOf(
804✔
4057
                                        "'now'::text",
804✔
4058
                                    ) !== -1
804✔
4059
                                ) {
868✔
4060
                                    tableColumn.default =
208✔
4061
                                        dbColumn["column_default"]
208✔
4062
                                } else {
868✔
4063
                                    tableColumn.default = dbColumn[
660✔
4064
                                        "column_default"
660✔
4065
                                    ].replace(/::[\w\s.[\]\-"]+/g, "")
660✔
4066
                                    tableColumn.default =
660✔
4067
                                        tableColumn.default.replace(
660✔
4068
                                            /^(-?\d+)$/,
660✔
4069
                                            "'$1'",
660✔
4070
                                        )
660✔
4071
                                }
660✔
4072
                            }
2,820✔
4073

19,084✔
4074
                            if (
19,084✔
4075
                                dbColumn["is_generated"] === "ALWAYS" &&
19,084✔
4076
                                dbColumn["generation_expression"]
168✔
4077
                            ) {
19,084✔
4078
                                // In postgres there is no VIRTUAL generated column type
168✔
4079
                                tableColumn.generatedType = "STORED"
168✔
4080
                                // We cannot relay on information_schema.columns.generation_expression, because it is formatted different.
168✔
4081
                                const asExpressionQuery =
168✔
4082
                                    this.selectTypeormMetadataSql({
168✔
4083
                                        database: currentDatabase,
168✔
4084
                                        schema: dbTable["table_schema"],
168✔
4085
                                        table: dbTable["table_name"],
168✔
4086
                                        type: MetadataTableType.GENERATED_COLUMN,
168✔
4087
                                        name: tableColumn.name,
168✔
4088
                                    })
168✔
4089

168✔
4090
                                const results = await this.query(
168✔
4091
                                    asExpressionQuery.query,
168✔
4092
                                    asExpressionQuery.parameters,
168✔
4093
                                )
168✔
4094
                                if (results[0] && results[0].value) {
168✔
4095
                                    tableColumn.asExpression = results[0].value
168✔
4096
                                } else {
168!
4097
                                    tableColumn.asExpression = ""
×
4098
                                }
×
4099
                            }
168✔
4100

19,084✔
4101
                            tableColumn.comment = dbColumn["description"]
19,084✔
4102
                                ? dbColumn["description"]
19,084✔
4103
                                : undefined
19,084✔
4104
                            if (dbColumn["character_set_name"])
19,084✔
4105
                                tableColumn.charset =
19,084!
4106
                                    dbColumn["character_set_name"]
×
4107
                            if (dbColumn["collation_name"])
19,084✔
4108
                                tableColumn.collation =
19,084✔
4109
                                    dbColumn["collation_name"]
3,392✔
4110
                            return tableColumn
19,084✔
4111
                        }),
3,852✔
4112
                )
3,852✔
4113

3,852✔
4114
                // find unique constraints of table, group them by constraint name and build TableUnique.
3,852✔
4115
                const tableUniqueConstraints = OrmUtils.uniq(
3,852✔
4116
                    dbConstraints.filter((dbConstraint) => {
3,852✔
4117
                        return (
212,702✔
4118
                            dbConstraint["table_name"] ===
212,702✔
4119
                                dbTable["table_name"] &&
212,702✔
4120
                            dbConstraint["table_schema"] ===
7,294✔
4121
                                dbTable["table_schema"] &&
212,702✔
4122
                            dbConstraint["constraint_type"] === "UNIQUE"
7,206✔
4123
                        )
212,702✔
4124
                    }),
3,852✔
4125
                    (dbConstraint) => dbConstraint["constraint_name"],
3,852✔
4126
                )
3,852✔
4127

3,852✔
4128
                table.uniques = tableUniqueConstraints.map((constraint) => {
3,852✔
4129
                    const uniques = dbConstraints.filter(
1,318✔
4130
                        (dbC) =>
1,318✔
4131
                            dbC["constraint_name"] ===
50,438✔
4132
                            constraint["constraint_name"],
1,318✔
4133
                    )
1,318✔
4134
                    return new TableUnique({
1,318✔
4135
                        name: constraint["constraint_name"],
1,318✔
4136
                        columnNames: uniques.map((u) => u["column_name"]),
1,318✔
4137
                        deferrable: constraint["deferrable"]
1,318✔
4138
                            ? constraint["deferred"]
1,318!
4139
                            : undefined,
1,318✔
4140
                    })
1,318✔
4141
                })
3,852✔
4142

3,852✔
4143
                // find check constraints of table, group them by constraint name and build TableCheck.
3,852✔
4144
                const tableCheckConstraints = OrmUtils.uniq(
3,852✔
4145
                    dbConstraints.filter((dbConstraint) => {
3,852✔
4146
                        return (
212,702✔
4147
                            dbConstraint["table_name"] ===
212,702✔
4148
                                dbTable["table_name"] &&
212,702✔
4149
                            dbConstraint["table_schema"] ===
7,294✔
4150
                                dbTable["table_schema"] &&
212,702✔
4151
                            dbConstraint["constraint_type"] === "CHECK"
7,206✔
4152
                        )
212,702✔
4153
                    }),
3,852✔
4154
                    (dbConstraint) => dbConstraint["constraint_name"],
3,852✔
4155
                )
3,852✔
4156

3,852✔
4157
                table.checks = tableCheckConstraints.map((constraint) => {
3,852✔
4158
                    const checks = dbConstraints.filter(
448✔
4159
                        (dbC) =>
448✔
4160
                            dbC["constraint_name"] ===
4,610✔
4161
                            constraint["constraint_name"],
448✔
4162
                    )
448✔
4163
                    return new TableCheck({
448✔
4164
                        name: constraint["constraint_name"],
448✔
4165
                        columnNames: checks.map((c) => c["column_name"]),
448✔
4166
                        expression: constraint["expression"].replace(
448✔
4167
                            /^\s*CHECK\s*\((.*)\)\s*$/i,
448✔
4168
                            "$1",
448✔
4169
                        ),
448✔
4170
                    })
448✔
4171
                })
3,852✔
4172

3,852✔
4173
                // find exclusion constraints of table, group them by constraint name and build TableExclusion.
3,852✔
4174
                const tableExclusionConstraints = OrmUtils.uniq(
3,852✔
4175
                    dbConstraints.filter((dbConstraint) => {
3,852✔
4176
                        return (
212,702✔
4177
                            dbConstraint["table_name"] ===
212,702✔
4178
                                dbTable["table_name"] &&
212,702✔
4179
                            dbConstraint["table_schema"] ===
7,294✔
4180
                                dbTable["table_schema"] &&
212,702✔
4181
                            dbConstraint["constraint_type"] === "EXCLUDE"
7,206✔
4182
                        )
212,702✔
4183
                    }),
3,852✔
4184
                    (dbConstraint) => dbConstraint["constraint_name"],
3,852✔
4185
                )
3,852✔
4186

3,852✔
4187
                table.exclusions = tableExclusionConstraints.map(
3,852✔
4188
                    (constraint) => {
3,852✔
4189
                        return new TableExclusion({
352✔
4190
                            name: constraint["constraint_name"],
352✔
4191
                            expression: constraint["expression"].substring(8), // trim EXCLUDE from start of expression
352✔
4192
                        })
352✔
4193
                    },
3,852✔
4194
                )
3,852✔
4195

3,852✔
4196
                // find foreign key constraints of table, group them by constraint name and build TableForeignKey.
3,852✔
4197
                const tableForeignKeyConstraints = OrmUtils.uniq(
3,852✔
4198
                    dbForeignKeys.filter((dbForeignKey) => {
3,852✔
4199
                        return (
11,504✔
4200
                            dbForeignKey["table_name"] ===
11,504✔
4201
                                dbTable["table_name"] &&
11,504✔
4202
                            dbForeignKey["table_schema"] ===
996✔
4203
                                dbTable["table_schema"]
996✔
4204
                        )
11,504✔
4205
                    }),
3,852✔
4206
                    (dbForeignKey) => dbForeignKey["constraint_name"],
3,852✔
4207
                )
3,852✔
4208

3,852✔
4209
                table.foreignKeys = tableForeignKeyConstraints.map(
3,852✔
4210
                    (dbForeignKey) => {
3,852✔
4211
                        const foreignKeys = dbForeignKeys.filter(
948✔
4212
                            (dbFk) =>
948✔
4213
                                dbFk["constraint_name"] ===
2,660✔
4214
                                dbForeignKey["constraint_name"],
948✔
4215
                        )
948✔
4216

948✔
4217
                        // if referenced table located in currently used schema, we don't need to concat schema name to table name.
948✔
4218
                        const schema = getSchemaFromKey(
948✔
4219
                            dbForeignKey,
948✔
4220
                            "referenced_table_schema",
948✔
4221
                        )
948✔
4222
                        const referencedTableName = this.driver.buildTableName(
948✔
4223
                            dbForeignKey["referenced_table_name"],
948✔
4224
                            schema,
948✔
4225
                        )
948✔
4226

948✔
4227
                        return new TableForeignKey({
948✔
4228
                            name: dbForeignKey["constraint_name"],
948✔
4229
                            columnNames: foreignKeys.map(
948✔
4230
                                (dbFk) => dbFk["column_name"],
948✔
4231
                            ),
948✔
4232
                            referencedSchema:
948✔
4233
                                dbForeignKey["referenced_table_schema"],
948✔
4234
                            referencedTableName: referencedTableName,
948✔
4235
                            referencedColumnNames: foreignKeys.map(
948✔
4236
                                (dbFk) => dbFk["referenced_column_name"],
948✔
4237
                            ),
948✔
4238
                            onDelete: dbForeignKey["on_delete"],
948✔
4239
                            onUpdate: dbForeignKey["on_update"],
948✔
4240
                            deferrable: dbForeignKey["deferrable"]
948✔
4241
                                ? dbForeignKey["deferred"]
948✔
4242
                                : undefined,
948✔
4243
                        })
948✔
4244
                    },
3,852✔
4245
                )
3,852✔
4246

3,852✔
4247
                // find index constraints of table, group them by constraint name and build TableIndex.
3,852✔
4248
                const tableIndexConstraints = OrmUtils.uniq(
3,852✔
4249
                    dbIndices.filter((dbIndex) => {
3,852✔
4250
                        return (
31,828✔
4251
                            dbIndex["table_name"] === dbTable["table_name"] &&
31,828✔
4252
                            dbIndex["table_schema"] === dbTable["table_schema"]
876✔
4253
                        )
31,828✔
4254
                    }),
3,852✔
4255
                    (dbIndex) => dbIndex["constraint_name"],
3,852✔
4256
                )
3,852✔
4257

3,852✔
4258
                table.indices = tableIndexConstraints.map((constraint) => {
3,852✔
4259
                    const indices = dbIndices.filter((index) => {
736✔
4260
                        return (
4,176✔
4261
                            index["table_schema"] ===
4,176✔
4262
                                constraint["table_schema"] &&
4,176✔
4263
                            index["table_name"] === constraint["table_name"] &&
4,176✔
4264
                            index["constraint_name"] ===
1,628✔
4265
                                constraint["constraint_name"]
1,628✔
4266
                        )
4,176✔
4267
                    })
736✔
4268
                    return new TableIndex(<TableIndexOptions>{
736✔
4269
                        table: table,
736✔
4270
                        name: constraint["constraint_name"],
736✔
4271
                        columnNames: indices.map((i) => i["column_name"]),
736✔
4272
                        isUnique: constraint["is_unique"] === "TRUE",
736✔
4273
                        where: constraint["condition"],
736✔
4274
                        isSpatial: constraint["index_type"] === "gist",
736✔
4275
                        type: constraint["index_type"],
736✔
4276
                        isFulltext: false,
736✔
4277
                    })
736✔
4278
                })
3,852✔
4279

3,852✔
4280
                return table
3,852✔
4281
            }),
2,100✔
4282
        )
2,100✔
4283
    }
2,100✔
4284

23✔
4285
    /**
23✔
4286
     * Builds create table sql.
23✔
4287
     * @param table
23✔
4288
     * @param createForeignKeys
23✔
4289
     */
23✔
4290
    protected createTableSql(table: Table, createForeignKeys?: boolean): Query {
23✔
4291
        const columnDefinitions = table.columns
24,476✔
4292
            .map((column) => this.buildCreateColumnSql(table, column))
24,476✔
4293
            .join(", ")
24,476✔
4294
        let sql = `CREATE TABLE ${this.escapePath(table)} (${columnDefinitions}`
24,476✔
4295

24,476✔
4296
        table.columns
24,476✔
4297
            .filter((column) => column.isUnique)
24,476✔
4298
            .forEach((column) => {
24,476✔
4299
                const isUniqueExist = table.uniques.some(
3,328✔
4300
                    (unique) =>
3,328✔
4301
                        unique.columnNames.length === 1 &&
4,408✔
4302
                        unique.columnNames[0] === column.name,
3,328✔
4303
                )
3,328✔
4304
                if (!isUniqueExist)
3,328✔
4305
                    table.uniques.push(
3,328✔
4306
                        new TableUnique({
16✔
4307
                            name: this.connection.namingStrategy.uniqueConstraintName(
16✔
4308
                                table,
16✔
4309
                                [column.name],
16✔
4310
                            ),
16✔
4311
                            columnNames: [column.name],
16✔
4312
                        }),
16✔
4313
                    )
16✔
4314
            })
24,476✔
4315

24,476✔
4316
        if (table.uniques.length > 0) {
24,476✔
4317
            const uniquesSql = table.uniques
2,868✔
4318
                .map((unique) => {
2,868✔
4319
                    const uniqueName = unique.name
4,216✔
4320
                        ? unique.name
4,216✔
4321
                        : this.connection.namingStrategy.uniqueConstraintName(
4,216✔
4322
                              table,
8✔
4323
                              unique.columnNames,
8✔
4324
                          )
4,216✔
4325
                    const columnNames = unique.columnNames
4,216✔
4326
                        .map((columnName) => `"${columnName}"`)
4,216✔
4327
                        .join(", ")
4,216✔
4328
                    let constraint = `CONSTRAINT "${uniqueName}" UNIQUE (${columnNames})`
4,216✔
4329
                    if (unique.deferrable)
4,216✔
4330
                        constraint += ` DEFERRABLE ${unique.deferrable}`
4,216✔
4331
                    return constraint
4,216✔
4332
                })
2,868✔
4333
                .join(", ")
2,868✔
4334

2,868✔
4335
            sql += `, ${uniquesSql}`
2,868✔
4336
        }
2,868✔
4337

24,476✔
4338
        if (table.checks.length > 0) {
24,476✔
4339
            const checksSql = table.checks
356✔
4340
                .map((check) => {
356✔
4341
                    const checkName = check.name
360✔
4342
                        ? check.name
360✔
4343
                        : this.connection.namingStrategy.checkConstraintName(
360✔
4344
                              table,
4✔
4345
                              check.expression!,
4✔
4346
                          )
360✔
4347
                    return `CONSTRAINT "${checkName}" CHECK (${check.expression})`
360✔
4348
                })
356✔
4349
                .join(", ")
356✔
4350

356✔
4351
            sql += `, ${checksSql}`
356✔
4352
        }
356✔
4353

24,476✔
4354
        if (table.exclusions.length > 0) {
24,476✔
4355
            const exclusionsSql = table.exclusions
400✔
4356
                .map((exclusion) => {
400✔
4357
                    const exclusionName = exclusion.name
400✔
4358
                        ? exclusion.name
400✔
4359
                        : this.connection.namingStrategy.exclusionConstraintName(
400!
4360
                              table,
×
4361
                              exclusion.expression!,
×
4362
                          )
400✔
4363
                    let constraint = `CONSTRAINT "${exclusionName}" EXCLUDE ${exclusion.expression}`
400✔
4364
                    if (exclusion.deferrable)
400✔
4365
                        constraint += ` DEFERRABLE ${exclusion.deferrable}`
400✔
4366
                    return constraint
400✔
4367
                })
400✔
4368
                .join(", ")
400✔
4369

400✔
4370
            sql += `, ${exclusionsSql}`
400✔
4371
        }
400✔
4372

24,476✔
4373
        if (table.foreignKeys.length > 0 && createForeignKeys) {
24,476✔
4374
            const foreignKeysSql = table.foreignKeys
16✔
4375
                .map((fk) => {
16✔
4376
                    const columnNames = fk.columnNames
20✔
4377
                        .map((columnName) => `"${columnName}"`)
20✔
4378
                        .join(", ")
20✔
4379
                    if (!fk.name)
20✔
4380
                        fk.name = this.connection.namingStrategy.foreignKeyName(
20✔
4381
                            table,
12✔
4382
                            fk.columnNames,
12✔
4383
                            this.getTablePath(fk),
12✔
4384
                            fk.referencedColumnNames,
12✔
4385
                        )
12✔
4386

20✔
4387
                    const referencedColumnNames = fk.referencedColumnNames
20✔
4388
                        .map((columnName) => `"${columnName}"`)
20✔
4389
                        .join(", ")
20✔
4390

20✔
4391
                    let constraint = `CONSTRAINT "${
20✔
4392
                        fk.name
20✔
4393
                    }" FOREIGN KEY (${columnNames}) REFERENCES ${this.escapePath(
20✔
4394
                        this.getTablePath(fk),
20✔
4395
                    )} (${referencedColumnNames})`
20✔
4396
                    if (fk.onDelete) constraint += ` ON DELETE ${fk.onDelete}`
20✔
4397
                    if (fk.onUpdate) constraint += ` ON UPDATE ${fk.onUpdate}`
20✔
4398
                    if (fk.deferrable)
20✔
4399
                        constraint += ` DEFERRABLE ${fk.deferrable}`
20!
4400

20✔
4401
                    return constraint
20✔
4402
                })
16✔
4403
                .join(", ")
16✔
4404

16✔
4405
            sql += `, ${foreignKeysSql}`
16✔
4406
        }
16✔
4407

24,476✔
4408
        const primaryColumns = table.columns.filter(
24,476✔
4409
            (column) => column.isPrimary,
24,476✔
4410
        )
24,476✔
4411
        if (primaryColumns.length > 0) {
24,476✔
4412
            const primaryKeyName = primaryColumns[0].primaryKeyConstraintName
24,356✔
4413
                ? primaryColumns[0].primaryKeyConstraintName
24,356✔
4414
                : this.connection.namingStrategy.primaryKeyName(
24,356✔
4415
                      table,
24,292✔
4416
                      primaryColumns.map((column) => column.name),
24,292✔
4417
                  )
24,356✔
4418

24,356✔
4419
            const columnNames = primaryColumns
24,356✔
4420
                .map((column) => `"${column.name}"`)
24,356✔
4421
                .join(", ")
24,356✔
4422
            sql += `, CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNames})`
24,356✔
4423
        }
24,356✔
4424

24,476✔
4425
        sql += `)`
24,476✔
4426

24,476✔
4427
        table.columns
24,476✔
4428
            .filter((it) => it.comment)
24,476✔
4429
            .forEach((it) => {
24,476✔
4430
                sql += `; COMMENT ON COLUMN ${this.escapePath(table)}."${
248✔
4431
                    it.name
248✔
4432
                }" IS ${this.escapeComment(it.comment)}`
248✔
4433
            })
24,476✔
4434

24,476✔
4435
        return new Query(sql)
24,476✔
4436
    }
24,476✔
4437

23✔
4438
    /**
23✔
4439
     * Loads Postgres version.
23✔
4440
     */
23✔
4441
    async getVersion(): Promise<string> {
23✔
4442
        // we use `SELECT version()` instead of `SHOW server_version` or `SHOW server_version_num`
2,788✔
4443
        // to maintain compatability with Amazon Redshift.
2,788✔
4444
        //
2,788✔
4445
        // see:
2,788✔
4446
        //  - https://github.com/typeorm/typeorm/pull/9319
2,788✔
4447
        //  - https://docs.aws.amazon.com/redshift/latest/dg/c_unsupported-postgresql-functions.html
2,788✔
4448
        const result: [{ version: string }] =
2,788✔
4449
            await this.query(`SELECT version()`)
2,788✔
4450

2,788✔
4451
        // Examples:
2,788✔
4452
        // Postgres: "PostgreSQL 14.10 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-20), 64-bit"
2,788✔
4453
        // Yugabyte: "PostgreSQL 11.2-YB-2.18.1.0-b0 on x86_64-pc-linux-gnu, compiled by clang version 15.0.3 (https://github.com/yugabyte/llvm-project.git 0b8d1183745fd3998d8beffeec8cbe99c1b20529), 64-bit"
2,788✔
4454
        return result[0].version.replace(/^PostgreSQL ([\d.]+).*$/, "$1")
2,788✔
4455
    }
2,788✔
4456

23✔
4457
    /**
23✔
4458
     * Builds drop table sql.
23✔
4459
     * @param tableOrPath
23✔
4460
     */
23✔
4461
    protected dropTableSql(tableOrPath: Table | string): Query {
23✔
4462
        return new Query(`DROP TABLE ${this.escapePath(tableOrPath)}`)
24,476✔
4463
    }
24,476✔
4464

23✔
4465
    protected createViewSql(view: View): Query {
23✔
4466
        const materializedClause = view.materialized ? "MATERIALIZED " : ""
172✔
4467
        const viewName = this.escapePath(view)
172✔
4468

172✔
4469
        if (typeof view.expression === "string") {
172✔
4470
            return new Query(
124✔
4471
                `CREATE ${materializedClause}VIEW ${viewName} AS ${view.expression}`,
124✔
4472
            )
124✔
4473
        } else {
172✔
4474
            return new Query(
48✔
4475
                `CREATE ${materializedClause}VIEW ${viewName} AS ${view
48✔
4476
                    .expression(this.connection)
48✔
4477
                    .getQuery()}`,
48✔
4478
            )
48✔
4479
        }
48✔
4480
    }
172✔
4481

23✔
4482
    protected async insertViewDefinitionSql(view: View): Promise<Query> {
23✔
4483
        const currentSchema = await this.getCurrentSchema()
168✔
4484

168✔
4485
        let { schema, tableName: name } = this.driver.parseTableName(view)
168✔
4486

168✔
4487
        if (!schema) {
168!
4488
            schema = currentSchema
×
4489
        }
×
4490

168✔
4491
        const type = view.materialized
168✔
4492
            ? MetadataTableType.MATERIALIZED_VIEW
168✔
4493
            : MetadataTableType.VIEW
168✔
4494
        const expression =
168✔
4495
            typeof view.expression === "string"
168✔
4496
                ? view.expression.trim()
168✔
4497
                : view.expression(this.connection).getQuery()
168✔
4498
        return this.insertTypeormMetadataSql({
168✔
4499
            type,
168✔
4500
            schema,
168✔
4501
            name,
168✔
4502
            value: expression,
168✔
4503
        })
168✔
4504
    }
168✔
4505

23✔
4506
    /**
23✔
4507
     * Builds drop view sql.
23✔
4508
     * @param view
23✔
4509
     * @param ifExists
23✔
4510
     */
23✔
4511
    protected dropViewSql(view: View, ifExists?: boolean): Query {
23✔
4512
        const materializedClause = view.materialized ? "MATERIALIZED " : ""
172✔
4513
        const ifExistsClause = ifExists ? "IF EXISTS " : ""
172!
4514
        return new Query(
172✔
4515
            `DROP ${materializedClause}VIEW ${ifExistsClause}${this.escapePath(view)}`,
172✔
4516
        )
172✔
4517
    }
172✔
4518

23✔
4519
    /**
23✔
4520
     * Builds remove view sql.
23✔
4521
     * @param view
23✔
4522
     */
23✔
4523
    protected async deleteViewDefinitionSql(view: View): Promise<Query> {
23✔
4524
        const currentSchema = await this.getCurrentSchema()
168✔
4525

168✔
4526
        let { schema, tableName: name } = this.driver.parseTableName(view)
168✔
4527

168✔
4528
        if (!schema) {
168!
4529
            schema = currentSchema
×
4530
        }
×
4531

168✔
4532
        const type = view.materialized
168✔
4533
            ? MetadataTableType.MATERIALIZED_VIEW
168✔
4534
            : MetadataTableType.VIEW
168✔
4535
        return this.deleteTypeormMetadataSql({ type, schema, name })
168✔
4536
    }
168✔
4537

23✔
4538
    /**
23✔
4539
     * Drops ENUM type from given schemas.
23✔
4540
     * @param schemaNames
23✔
4541
     */
23✔
4542
    protected async dropEnumTypes(schemaNames: string): Promise<void> {
23✔
4543
        const selectDropsQuery =
7,660✔
4544
            `SELECT 'DROP TYPE IF EXISTS "' || n.nspname || '"."' || t.typname || '" CASCADE;' as "query" FROM "pg_type" "t" ` +
7,660✔
4545
            `INNER JOIN "pg_enum" "e" ON "e"."enumtypid" = "t"."oid" ` +
7,660✔
4546
            `INNER JOIN "pg_namespace" "n" ON "n"."oid" = "t"."typnamespace" ` +
7,660✔
4547
            `WHERE "n"."nspname" IN (${schemaNames}) GROUP BY "n"."nspname", "t"."typname"`
7,660✔
4548
        const dropQueries: ObjectLiteral[] = await this.query(selectDropsQuery)
7,660✔
4549
        for (const q of dropQueries) {
7,660✔
4550
            await this.query(q["query"])
864✔
4551
        }
864✔
4552
    }
7,660✔
4553

23✔
4554
    /**
23✔
4555
     * Checks if enum with the given name exist in the database.
23✔
4556
     * @param table
23✔
4557
     * @param column
23✔
4558
     */
23✔
4559
    protected async hasEnumType(
23✔
4560
        table: Table,
1,040✔
4561
        column: TableColumn,
1,040✔
4562
    ): Promise<boolean> {
1,040✔
4563
        let { schema } = this.driver.parseTableName(table)
1,040✔
4564

1,040✔
4565
        if (!schema) {
1,040!
4566
            schema = await this.getCurrentSchema()
×
4567
        }
×
4568

1,040✔
4569
        const enumName = this.buildEnumName(table, column, false, true)
1,040✔
4570
        const sql =
1,040✔
4571
            `SELECT "n"."nspname", "t"."typname" FROM "pg_type" "t" ` +
1,040✔
4572
            `INNER JOIN "pg_namespace" "n" ON "n"."oid" = "t"."typnamespace" ` +
1,040✔
4573
            `WHERE "n"."nspname" = '${schema}' AND "t"."typname" = '${enumName}'`
1,040✔
4574
        const result = await this.query(sql)
1,040✔
4575
        return result.length ? true : false
1,040✔
4576
    }
1,040✔
4577

23✔
4578
    /**
23✔
4579
     * Builds create ENUM type sql.
23✔
4580
     * @param table
23✔
4581
     * @param column
23✔
4582
     * @param enumName
23✔
4583
     */
23✔
4584
    protected createEnumTypeSql(
23✔
4585
        table: Table,
1,124✔
4586
        column: TableColumn,
1,124✔
4587
        enumName?: string,
1,124✔
4588
    ): Query {
1,124✔
4589
        if (!enumName) enumName = this.buildEnumName(table, column)
1,124✔
4590
        const enumValues = column
1,124✔
4591
            .enum!.map((value) => `'${value.replaceAll("'", "''")}'`)
1,124✔
4592
            .join(", ")
1,124✔
4593
        return new Query(`CREATE TYPE ${enumName} AS ENUM(${enumValues})`)
1,124✔
4594
    }
1,124✔
4595

23✔
4596
    /**
23✔
4597
     * Builds create ENUM type sql.
23✔
4598
     * @param table
23✔
4599
     * @param column
23✔
4600
     * @param enumName
23✔
4601
     */
23✔
4602
    protected dropEnumTypeSql(
23✔
4603
        table: Table,
1,124✔
4604
        column: TableColumn,
1,124✔
4605
        enumName?: string,
1,124✔
4606
    ): Query {
1,124✔
4607
        if (!enumName) enumName = this.buildEnumName(table, column)
1,124✔
4608
        return new Query(`DROP TYPE ${enumName}`)
1,124✔
4609
    }
1,124✔
4610

23✔
4611
    /**
23✔
4612
     * Builds the SQL `USING <index_type>` clause based on the index type, prioritizing `isSpatial` as `GiST`.
23✔
4613
     */
23✔
4614

23✔
4615
    private buildIndexTypeClause(index: TableIndex) {
23✔
4616
        const type = index.isSpatial ? "gist" : index.type
8,472✔
4617

8,472✔
4618
        if (typeof type !== "string") return null
8,472✔
4619

180✔
4620
        return `USING ${type}`
180✔
4621
    }
180✔
4622

23✔
4623
    /**
23✔
4624
     * Builds create index sql.
23✔
4625
     * @param table
23✔
4626
     * @param index
23✔
4627
     */
23✔
4628
    protected createIndexSql(table: Table, index: TableIndex): Query {
23✔
4629
        const indexTypeClause = this.buildIndexTypeClause(index)
8,444✔
4630

8,444✔
4631
        const columns = index.columnNames
8,444✔
4632
            .map((columnName) => `"${columnName}"`)
8,444✔
4633
            .join(", ")
8,444✔
4634
        return new Query(
8,444✔
4635
            `CREATE ${index.isUnique ? "UNIQUE " : ""}INDEX${
8,444✔
4636
                index.isConcurrent ? " CONCURRENTLY" : ""
8,444✔
4637
            } "${index.name}" ON ${this.escapePath(table)} ${
8,444✔
4638
                indexTypeClause ?? ""
8,444✔
4639
            } (${columns}) ${index.where ? "WHERE " + index.where : ""}`,
8,444✔
4640
        )
8,444✔
4641
    }
8,444✔
4642

23✔
4643
    /**
23✔
4644
     * Builds create view index sql.
23✔
4645
     * @param view
23✔
4646
     * @param index
23✔
4647
     */
23✔
4648
    protected createViewIndexSql(view: View, index: TableIndex): Query {
23✔
4649
        const indexTypeClause = this.buildIndexTypeClause(index)
28✔
4650

28✔
4651
        const columns = index.columnNames
28✔
4652
            .map((columnName) => `"${columnName}"`)
28✔
4653
            .join(", ")
28✔
4654
        return new Query(
28✔
4655
            `CREATE ${index.isUnique ? "UNIQUE " : ""}INDEX "${
28✔
4656
                index.name
28✔
4657
            }" ON ${this.escapePath(view)} ${
28✔
4658
                indexTypeClause ?? ""
28✔
4659
            } (${columns}) ${index.where ? "WHERE " + index.where : ""}`,
28!
4660
        )
28✔
4661
    }
28✔
4662

23✔
4663
    /**
23✔
4664
     * Builds drop index sql.
23✔
4665
     * @param table
23✔
4666
     * @param indexOrName
23✔
4667
     * @param ifExists
23✔
4668
     */
23✔
4669
    protected dropIndexSql(
23✔
4670
        table: Table | View,
8,472✔
4671
        indexOrName: TableIndex | string,
8,472✔
4672
        ifExists?: boolean,
8,472✔
4673
    ): Query {
8,472✔
4674
        const indexName = InstanceChecker.isTableIndex(indexOrName)
8,472✔
4675
            ? indexOrName.name
8,472✔
4676
            : indexOrName
8,472!
4677
        const concurrent = InstanceChecker.isTableIndex(indexOrName)
8,472✔
4678
            ? indexOrName.isConcurrent
8,472✔
4679
            : false
8,472!
4680
        const ifExistsClause = ifExists ? "IF EXISTS " : ""
8,472!
4681
        const { schema } = this.driver.parseTableName(table)
8,472✔
4682
        return schema
8,472✔
4683
            ? new Query(
8,472✔
4684
                  `DROP INDEX ${
8,472✔
4685
                      concurrent ? "CONCURRENTLY " : ""
8,472✔
4686
                  }${ifExistsClause}"${schema}"."${indexName}"`,
8,472✔
4687
              )
8,472✔
4688
            : new Query(
8,472!
4689
                  `DROP INDEX ${
×
4690
                      concurrent ? "CONCURRENTLY " : ""
×
NEW
4691
                  }${ifExistsClause}"${indexName}"`,
×
4692
              )
8,472✔
4693
    }
8,472✔
4694

23✔
4695
    /**
23✔
4696
     * Builds create primary key sql.
23✔
4697
     * @param table
23✔
4698
     * @param columnNames
23✔
4699
     * @param constraintName
23✔
4700
     */
23✔
4701
    protected createPrimaryKeySql(
23✔
4702
        table: Table,
24✔
4703
        columnNames: string[],
24✔
4704
        constraintName?: string,
24✔
4705
    ): Query {
24✔
4706
        const primaryKeyName = constraintName
24✔
4707
            ? constraintName
24✔
4708
            : this.connection.namingStrategy.primaryKeyName(table, columnNames)
24✔
4709

24✔
4710
        const columnNamesString = columnNames
24✔
4711
            .map((columnName) => `"${columnName}"`)
24✔
4712
            .join(", ")
24✔
4713

24✔
4714
        return new Query(
24✔
4715
            `ALTER TABLE ${this.escapePath(
24✔
4716
                table,
24✔
4717
            )} ADD CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNamesString})`,
24✔
4718
        )
24✔
4719
    }
24✔
4720

23✔
4721
    /**
23✔
4722
     * Builds drop primary key sql.
23✔
4723
     * @param table
23✔
4724
     * @param ifExists
23✔
4725
     */
23✔
4726
    protected dropPrimaryKeySql(table: Table, ifExists?: boolean): Query {
23✔
4727
        if (!table.primaryColumns.length)
24✔
4728
            throw new TypeORMError(`Table ${table} has no primary keys.`)
24!
4729

24✔
4730
        const columnNames = table.primaryColumns.map((column) => column.name)
24✔
4731
        const constraintName = table.primaryColumns[0].primaryKeyConstraintName
24✔
4732
        const primaryKeyName = constraintName
24✔
4733
            ? constraintName
24!
4734
            : this.connection.namingStrategy.primaryKeyName(table, columnNames)
24✔
4735

24✔
4736
        const ifExistsClause = ifExists ? "IF EXISTS " : ""
24✔
4737
        return new Query(
24✔
4738
            `ALTER TABLE ${this.escapePath(
24✔
4739
                table,
24✔
4740
            )} DROP CONSTRAINT ${ifExistsClause}"${primaryKeyName}"`,
24✔
4741
        )
24✔
4742
    }
24✔
4743

23✔
4744
    /**
23✔
4745
     * Builds create unique constraint sql.
23✔
4746
     * @param table
23✔
4747
     * @param uniqueConstraint
23✔
4748
     */
23✔
4749
    protected createUniqueConstraintSql(
23✔
4750
        table: Table,
92✔
4751
        uniqueConstraint: TableUnique,
92✔
4752
    ): Query {
92✔
4753
        const columnNames = uniqueConstraint.columnNames
92✔
4754
            .map((column) => `"` + column + `"`)
92✔
4755
            .join(", ")
92✔
4756
        let sql = `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
92✔
4757
            uniqueConstraint.name
92✔
4758
        }" UNIQUE (${columnNames})`
92✔
4759
        if (uniqueConstraint.deferrable)
92✔
4760
            sql += ` DEFERRABLE ${uniqueConstraint.deferrable}`
92!
4761
        return new Query(sql)
92✔
4762
    }
92✔
4763

23✔
4764
    /**
23✔
4765
     * Builds drop unique constraint sql.
23✔
4766
     * @param table
23✔
4767
     * @param uniqueOrName
23✔
4768
     * @param ifExists
23✔
4769
     */
23✔
4770
    protected dropUniqueConstraintSql(
23✔
4771
        table: Table,
92✔
4772
        uniqueOrName: TableUnique | string,
92✔
4773
        ifExists?: boolean,
92✔
4774
    ): Query {
92✔
4775
        const uniqueName = InstanceChecker.isTableUnique(uniqueOrName)
92✔
4776
            ? uniqueOrName.name
92✔
4777
            : uniqueOrName
92!
4778
        const ifExistsClause = ifExists ? "IF EXISTS " : ""
92!
4779
        return new Query(
92✔
4780
            `ALTER TABLE ${this.escapePath(
92✔
4781
                table,
92✔
4782
            )} DROP CONSTRAINT ${ifExistsClause}"${uniqueName}"`,
92✔
4783
        )
92✔
4784
    }
92✔
4785

23✔
4786
    /**
23✔
4787
     * Builds create check constraint sql.
23✔
4788
     * @param table
23✔
4789
     * @param checkConstraint
23✔
4790
     */
23✔
4791
    protected createCheckConstraintSql(
23✔
4792
        table: Table,
40✔
4793
        checkConstraint: TableCheck,
40✔
4794
    ): Query {
40✔
4795
        return new Query(
40✔
4796
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
40✔
4797
                checkConstraint.name
40✔
4798
            }" CHECK (${checkConstraint.expression})`,
40✔
4799
        )
40✔
4800
    }
40✔
4801

23✔
4802
    /**
23✔
4803
     * Builds drop check constraint sql.
23✔
4804
     * @param table
23✔
4805
     * @param checkOrName
23✔
4806
     * @param ifExists
23✔
4807
     */
23✔
4808
    protected dropCheckConstraintSql(
23✔
4809
        table: Table,
40✔
4810
        checkOrName: TableCheck | string,
40✔
4811
        ifExists?: boolean,
40✔
4812
    ): Query {
40✔
4813
        const checkName = InstanceChecker.isTableCheck(checkOrName)
40✔
4814
            ? checkOrName.name
40✔
4815
            : checkOrName
40!
4816
        const ifExistsClause = ifExists ? "IF EXISTS " : ""
40!
4817
        return new Query(
40✔
4818
            `ALTER TABLE ${this.escapePath(
40✔
4819
                table,
40✔
4820
            )} DROP CONSTRAINT ${ifExistsClause}"${checkName}"`,
40✔
4821
        )
40✔
4822
    }
40✔
4823

23✔
4824
    /**
23✔
4825
     * Builds create exclusion constraint sql.
23✔
4826
     * @param table
23✔
4827
     * @param exclusionConstraint
23✔
4828
     */
23✔
4829
    protected createExclusionConstraintSql(
23✔
4830
        table: Table,
28✔
4831
        exclusionConstraint: TableExclusion,
28✔
4832
    ): Query {
28✔
4833
        let sql = `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
28✔
4834
            exclusionConstraint.name
28✔
4835
        }" EXCLUDE ${exclusionConstraint.expression}`
28✔
4836

28✔
4837
        if (exclusionConstraint.deferrable)
28✔
4838
            sql += ` DEFERRABLE ${exclusionConstraint.deferrable}`
28!
4839

28✔
4840
        return new Query(sql)
28✔
4841
    }
28✔
4842

23✔
4843
    /**
23✔
4844
     * Builds drop exclusion constraint sql.
23✔
4845
     * @param table
23✔
4846
     * @param exclusionOrName
23✔
4847
     * @param ifExists
23✔
4848
     */
23✔
4849
    protected dropExclusionConstraintSql(
23✔
4850
        table: Table,
28✔
4851
        exclusionOrName: TableExclusion | string,
28✔
4852
        ifExists?: boolean,
28✔
4853
    ): Query {
28✔
4854
        const exclusionName = InstanceChecker.isTableExclusion(exclusionOrName)
28✔
4855
            ? exclusionOrName.name
28✔
4856
            : exclusionOrName
28!
4857
        const ifExistsClause = ifExists ? "IF EXISTS " : ""
28!
4858
        return new Query(
28✔
4859
            `ALTER TABLE ${this.escapePath(
28✔
4860
                table,
28✔
4861
            )} DROP CONSTRAINT ${ifExistsClause}"${exclusionName}"`,
28✔
4862
        )
28✔
4863
    }
28✔
4864

23✔
4865
    /**
23✔
4866
     * Builds create foreign key sql.
23✔
4867
     * @param table
23✔
4868
     * @param foreignKey
23✔
4869
     */
23✔
4870
    protected createForeignKeySql(
23✔
4871
        table: Table,
15,768✔
4872
        foreignKey: TableForeignKey,
15,768✔
4873
    ): Query {
15,768✔
4874
        const columnNames = foreignKey.columnNames
15,768✔
4875
            .map((column) => `"` + column + `"`)
15,768✔
4876
            .join(", ")
15,768✔
4877
        const referencedColumnNames = foreignKey.referencedColumnNames
15,768✔
4878
            .map((column) => `"` + column + `"`)
15,768✔
4879
            .join(",")
15,768✔
4880
        let sql =
15,768✔
4881
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
15,768✔
4882
                foreignKey.name
15,768✔
4883
            }" FOREIGN KEY (${columnNames}) ` +
15,768✔
4884
            `REFERENCES ${this.escapePath(
15,768✔
4885
                this.getTablePath(foreignKey),
15,768✔
4886
            )}(${referencedColumnNames})`
15,768✔
4887
        if (foreignKey.onDelete) sql += ` ON DELETE ${foreignKey.onDelete}`
15,768✔
4888
        if (foreignKey.onUpdate) sql += ` ON UPDATE ${foreignKey.onUpdate}`
15,768✔
4889
        if (foreignKey.deferrable) sql += ` DEFERRABLE ${foreignKey.deferrable}`
15,768✔
4890

15,768✔
4891
        return new Query(sql)
15,768✔
4892
    }
15,768✔
4893

23✔
4894
    /**
23✔
4895
     * Builds drop foreign key sql.
23✔
4896
     * @param table
23✔
4897
     * @param foreignKeyOrName
23✔
4898
     * @param ifExists
23✔
4899
     */
23✔
4900
    protected dropForeignKeySql(
23✔
4901
        table: Table,
15,788✔
4902
        foreignKeyOrName: TableForeignKey | string,
15,788✔
4903
        ifExists?: boolean,
15,788✔
4904
    ): Query {
15,788✔
4905
        const foreignKeyName = InstanceChecker.isTableForeignKey(
15,788✔
4906
            foreignKeyOrName,
15,788✔
4907
        )
15,788✔
4908
            ? foreignKeyOrName.name
15,788✔
4909
            : foreignKeyOrName
15,788!
4910
        const ifExistsClause = ifExists ? "IF EXISTS " : ""
15,788!
4911
        return new Query(
15,788✔
4912
            `ALTER TABLE ${this.escapePath(
15,788✔
4913
                table,
15,788✔
4914
            )} DROP CONSTRAINT ${ifExistsClause}"${foreignKeyName}"`,
15,788✔
4915
        )
15,788✔
4916
    }
15,788✔
4917

23✔
4918
    /**
23✔
4919
     * Builds sequence name from given table and column.
23✔
4920
     * @param table
23✔
4921
     * @param columnOrName
23✔
4922
     */
23✔
4923
    protected buildSequenceName(
23✔
4924
        table: Table,
5,944✔
4925
        columnOrName: TableColumn | string,
5,944✔
4926
    ): string {
5,944✔
4927
        const { tableName } = this.driver.parseTableName(table)
5,944✔
4928

5,944✔
4929
        const columnName = InstanceChecker.isTableColumn(columnOrName)
5,944✔
4930
            ? columnOrName.name
5,944✔
4931
            : columnOrName
5,944✔
4932

5,944✔
4933
        let seqName = `${tableName}_${columnName}_seq`
5,944✔
4934

5,944✔
4935
        if (seqName.length > this.connection.driver.maxAliasLength!) {
5,944✔
4936
            // note doesn't yet handle corner cases where .length differs from number of UTF-8 bytes
32✔
4937
            seqName = `${tableName.substring(0, 29)}_${columnName.substring(
32✔
4938
                0,
32✔
4939
                Math.max(29, 63 - table.name.length - 5),
32✔
4940
            )}_seq`
32✔
4941
        }
32✔
4942

5,944✔
4943
        return seqName
5,944✔
4944
    }
5,944✔
4945

23✔
4946
    protected buildSequencePath(
23✔
4947
        table: Table,
3,020✔
4948
        columnOrName: TableColumn | string,
3,020✔
4949
    ): string {
3,020✔
4950
        const { schema } = this.driver.parseTableName(table)
3,020✔
4951

3,020✔
4952
        return schema
3,020✔
4953
            ? `${schema}.${this.buildSequenceName(table, columnOrName)}`
3,020✔
4954
            : this.buildSequenceName(table, columnOrName)
3,020!
4955
    }
3,020✔
4956

23✔
4957
    /**
23✔
4958
     * Builds ENUM type name from given table and column.
23✔
4959
     * @param table
23✔
4960
     * @param column
23✔
4961
     * @param withSchema
23✔
4962
     * @param disableEscape
23✔
4963
     * @param toOld
23✔
4964
     */
23✔
4965
    protected buildEnumName(
23✔
4966
        table: Table,
4,818✔
4967
        column: TableColumn,
4,818✔
4968
        withSchema: boolean = true,
4,818✔
4969
        disableEscape?: boolean,
4,818✔
4970
        toOld?: boolean,
4,818✔
4971
    ): string {
4,818✔
4972
        const { schema, tableName } = this.driver.parseTableName(table)
4,818✔
4973
        let enumName = column.enumName
4,818✔
4974
            ? column.enumName
4,818✔
4975
            : `${tableName}_${column.name.toLowerCase()}_enum`
4,818✔
4976
        if (schema && withSchema) enumName = `${schema}.${enumName}`
4,818✔
4977
        if (toOld) enumName = enumName + "_old"
4,818✔
4978
        return enumName
4,818✔
4979
            .split(".")
4,818✔
4980
            .map((i) => {
4,818✔
4981
                return disableEscape ? i : `"${i}"`
7,146✔
4982
            })
4,818✔
4983
            .join(".")
4,818✔
4984
    }
4,818✔
4985

23✔
4986
    protected async getUserDefinedTypeName(table: Table, column: TableColumn) {
23✔
4987
        let { schema, tableName: name } = this.driver.parseTableName(table)
1,334✔
4988

1,334✔
4989
        if (!schema) {
1,334!
4990
            schema = await this.getCurrentSchema()
×
4991
        }
×
4992

1,334✔
4993
        const result = await this.query(
1,334✔
4994
            `SELECT "udt_schema", "udt_name" ` +
1,334✔
4995
                `FROM "information_schema"."columns" WHERE "table_schema" = '${schema}' AND "table_name" = '${name}' AND "column_name"='${column.name}'`,
1,334✔
4996
        )
1,334✔
4997

1,334✔
4998
        // docs: https://www.postgresql.org/docs/current/xtypes.html
1,334✔
4999
        // When you define a new base type, PostgreSQL automatically provides support for arrays of that type.
1,334✔
5000
        // The array type typically has the same name as the base type with the underscore character (_) prepended.
1,334✔
5001
        // ----
1,334✔
5002
        // so, we must remove this underscore character from enum type name
1,334✔
5003
        let udtName = result[0]["udt_name"]
1,334✔
5004
        if (udtName.indexOf("_") === 0) {
1,334✔
5005
            udtName = udtName.substr(1, udtName.length)
596✔
5006
        }
596✔
5007
        return {
1,334✔
5008
            schema: result[0]["udt_schema"],
1,334✔
5009
            name: udtName,
1,334✔
5010
        }
1,334✔
5011
    }
1,334✔
5012

23✔
5013
    /**
23✔
5014
     * Escapes a given comment so it's safe to include in a query.
23✔
5015
     * @param comment
23✔
5016
     */
23✔
5017
    protected escapeComment(comment?: string) {
23✔
5018
        if (!comment || comment.length === 0) {
3,144✔
5019
            return "NULL"
2,604✔
5020
        }
2,604✔
5021

540✔
5022
        comment = comment.replace(/'/g, "''").replace(/\u0000/g, "") // Null bytes aren't allowed in comments
540✔
5023

540✔
5024
        return `'${comment}'`
540✔
5025
    }
540✔
5026

23✔
5027
    /**
23✔
5028
     * Escapes given table or view path.
23✔
5029
     * @param target
23✔
5030
     */
23✔
5031
    protected escapePath(target: Table | View | string): string {
23✔
5032
        const { schema, tableName } = this.driver.parseTableName(target)
108,616✔
5033

108,616✔
5034
        if (schema && schema !== this.driver.searchSchema) {
108,616✔
5035
            return `"${schema}"."${tableName}"`
1,024✔
5036
        }
1,024✔
5037

107,592✔
5038
        return `"${tableName}"`
107,592✔
5039
    }
107,592✔
5040

23✔
5041
    /**
23✔
5042
     * Get the table name with table schema
23✔
5043
     * Note: Without ' or "
23✔
5044
     * @param target
23✔
5045
     */
23✔
5046
    protected async getTableNameWithSchema(target: Table | string) {
23✔
5047
        const tableName = InstanceChecker.isTable(target) ? target.name : target
132!
5048
        if (tableName.indexOf(".") === -1) {
132✔
5049
            const schemaResult = await this.query(`SELECT current_schema()`)
132✔
5050
            const schema = schemaResult[0]["current_schema"]
132✔
5051
            return `${schema}.${tableName}`
132✔
5052
        } else {
132!
5053
            return `${tableName.split(".")[0]}.${tableName.split(".")[1]}`
×
5054
        }
×
5055
    }
132✔
5056

23✔
5057
    /**
23✔
5058
     * Builds a query for create column.
23✔
5059
     * @param table
23✔
5060
     * @param column
23✔
5061
     */
23✔
5062
    protected buildCreateColumnSql(table: Table, column: TableColumn) {
23✔
5063
        let c = '"' + column.name + '"'
83,416✔
5064
        if (
83,416✔
5065
            column.isGenerated === true &&
83,416✔
5066
            column.generationStrategy !== "uuid"
15,252✔
5067
        ) {
83,416✔
5068
            if (column.generationStrategy === "identity") {
14,496✔
5069
                // Postgres 10+ Identity generated column
20✔
5070
                const generatedIdentityOrDefault =
20✔
5071
                    column.generatedIdentity || "BY DEFAULT"
20✔
5072
                c += ` ${column.type} GENERATED ${generatedIdentityOrDefault} AS IDENTITY`
20✔
5073
            } else {
14,496✔
5074
                // classic SERIAL primary column
14,476✔
5075
                if (
14,476✔
5076
                    column.type === "integer" ||
14,476✔
5077
                    column.type === "int" ||
14,476✔
5078
                    column.type === "int4"
40✔
5079
                )
14,476✔
5080
                    c += " SERIAL"
14,476✔
5081
                if (column.type === "smallint" || column.type === "int2")
14,476✔
5082
                    c += " SMALLSERIAL"
14,476✔
5083
                if (column.type === "bigint" || column.type === "int8")
14,476✔
5084
                    c += " BIGSERIAL"
14,476✔
5085
            }
14,476✔
5086
        }
14,496✔
5087
        if (column.type === "enum" || column.type === "simple-enum") {
83,416✔
5088
            c += " " + this.buildEnumName(table, column)
1,060✔
5089
            if (column.isArray) c += " array"
1,060✔
5090
        } else if (!column.isGenerated || column.type === "uuid") {
83,416✔
5091
            c += " " + this.connection.driver.createFullType(column)
67,860✔
5092
        }
67,860✔
5093

83,416✔
5094
        // Postgres only supports the stored generated column type
83,416✔
5095
        if (column.generatedType === "STORED" && column.asExpression) {
83,416✔
5096
            c += ` GENERATED ALWAYS AS (${column.asExpression}) STORED`
132✔
5097
        }
132✔
5098

83,416✔
5099
        if (column.charset) c += ' CHARACTER SET "' + column.charset + '"'
83,416!
5100
        if (column.collation) c += ' COLLATE "' + column.collation + '"'
83,416✔
5101
        if (column.isNullable !== true) c += " NOT NULL"
83,416✔
5102
        if (column.default !== undefined && column.default !== null)
83,416✔
5103
            c += " DEFAULT " + column.default
83,416✔
5104
        if (
83,416✔
5105
            column.isGenerated &&
83,416✔
5106
            column.generationStrategy === "uuid" &&
83,416✔
5107
            !column.default
756✔
5108
        )
83,416✔
5109
            c += ` DEFAULT ${this.driver.uuidGenerator}`
83,416✔
5110

83,416✔
5111
        return c
83,416✔
5112
    }
83,416✔
5113

23✔
5114
    /**
23✔
5115
     * Checks if the PostgreSQL server has support for partitioned tables
23✔
5116
     */
23✔
5117
    protected async hasSupportForPartitionedTables() {
23✔
5118
        const result = await this.query(
2,100✔
5119
            `SELECT TRUE FROM information_schema.columns WHERE table_name = 'pg_class' and column_name = 'relispartition'`,
2,100✔
5120
        )
2,100✔
5121
        return result.length ? true : false
2,100!
5122
    }
2,100✔
5123

23✔
5124
    /**
23✔
5125
     * Change table comment.
23✔
5126
     * @param tableOrName
23✔
5127
     * @param newComment
23✔
5128
     */
23✔
5129
    async changeTableComment(
23✔
5130
        tableOrName: Table | string,
1,312✔
5131
        newComment?: string,
1,312✔
5132
    ): Promise<void> {
1,312✔
5133
        const upQueries: Query[] = []
1,312✔
5134
        const downQueries: Query[] = []
1,312✔
5135

1,312✔
5136
        const table = InstanceChecker.isTable(tableOrName)
1,312✔
5137
            ? tableOrName
1,312✔
5138
            : await this.getCachedTable(tableOrName)
1,312!
5139

×
5140
        const escapedNewComment = this.escapeComment(newComment)
×
5141
        const escapedComment = this.escapeComment(table.comment)
×
5142

×
5143
        if (escapedNewComment === escapedComment) {
1,312✔
5144
            return
1,292✔
5145
        }
1,292✔
5146

20✔
5147
        const newTable = table.clone()
20✔
5148
        newTable.comment = newComment
20✔
5149

20✔
5150
        upQueries.push(
20✔
5151
            new Query(
20✔
5152
                `COMMENT ON TABLE ${this.escapePath(
20✔
5153
                    newTable,
20✔
5154
                )} IS ${escapedNewComment}`,
20✔
5155
            ),
20✔
5156
        )
20✔
5157

20✔
5158
        downQueries.push(
20✔
5159
            new Query(
20✔
5160
                `COMMENT ON TABLE ${this.escapePath(table)} IS ${escapedComment}`,
20✔
5161
            ),
20✔
5162
        )
20✔
5163

20✔
5164
        await this.executeQueries(upQueries, downQueries)
20✔
5165

20✔
5166
        table.comment = newTable.comment
20✔
5167
        this.replaceCachedTable(table, newTable)
20✔
5168
    }
20✔
5169
}
23✔
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