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

typeorm / typeorm / 14796576772

02 May 2025 01:52PM UTC coverage: 45.367% (-30.9%) from 76.309%
14796576772

Pull #11434

github

web-flow
Merge ec4ce2d00 into fadad1a74
Pull Request #11434: feat: release PR releases using pkg.pr.new

5216 of 12761 branches covered (40.87%)

Branch coverage included in aggregate %.

11439 of 23951 relevant lines covered (47.76%)

15712.55 hits per line

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

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

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

42
    /**
43
     * Database driver used by connection.
44
     */
45
    driver: PostgresDriver
46

47
    // -------------------------------------------------------------------------
48
    // Protected Properties
49
    // -------------------------------------------------------------------------
50

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

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

61
    // -------------------------------------------------------------------------
62
    // Constructor
63
    // -------------------------------------------------------------------------
64

65
    constructor(driver: PostgresDriver, mode: ReplicationMode) {
66
        super()
×
67
        this.driver = driver
×
68
        this.connection = driver.connection
×
69
        this.mode = mode
×
70
        this.broadcaster = new Broadcaster(this)
×
71
    }
72

73
    // -------------------------------------------------------------------------
74
    // Public Methods
75
    // -------------------------------------------------------------------------
76

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

85
        if (this.databaseConnectionPromise)
×
86
            return this.databaseConnectionPromise
×
87

88
        if (this.mode === "slave" && this.driver.isReplicated) {
×
89
            this.databaseConnectionPromise = this.driver
×
90
                .obtainSlaveConnection()
91
                .then(([connection, release]: any[]) => {
92
                    this.driver.connectedQueryRunners.push(this)
×
93
                    this.databaseConnection = connection
×
94

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

106
                    return this.databaseConnection
×
107
                })
108
        } else {
109
            // master
110
            this.databaseConnectionPromise = this.driver
×
111
                .obtainMasterConnection()
112
                .then(([connection, release]: any[]) => {
113
                    this.driver.connectedQueryRunners.push(this)
×
114
                    this.databaseConnection = connection
×
115

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

127
                    return this.databaseConnection
×
128
                })
129
        }
130

131
        return this.databaseConnectionPromise
×
132
    }
133

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

143
        this.isReleased = true
×
144
        if (this.releaseCallback) {
×
145
            this.releaseCallback(err)
×
146
            this.releaseCallback = undefined
×
147
        }
148

149
        const index = this.driver.connectedQueryRunners.indexOf(this)
×
150

151
        if (index !== -1) {
×
152
            this.driver.connectedQueryRunners.splice(index, 1)
×
153
        }
154
    }
155

156
    /**
157
     * Releases used database connection.
158
     * You cannot use query runner methods once its released.
159
     */
160
    release(): Promise<void> {
161
        return this.releasePostgresConnection()
×
162
    }
163

164
    /**
165
     * Starts transaction.
166
     */
167
    async startTransaction(isolationLevel?: IsolationLevel): Promise<void> {
168
        this.isTransactionActive = true
×
169
        try {
×
170
            await this.broadcaster.broadcast("BeforeTransactionStart")
×
171
        } catch (err) {
172
            this.isTransactionActive = false
×
173
            throw err
×
174
        }
175

176
        if (this.transactionDepth === 0) {
×
177
            await this.query("START TRANSACTION")
×
178
            if (isolationLevel) {
×
179
                await this.query(
×
180
                    "SET TRANSACTION ISOLATION LEVEL " + isolationLevel,
181
                )
182
            }
183
        } else {
184
            await this.query(`SAVEPOINT typeorm_${this.transactionDepth}`)
×
185
        }
186
        this.transactionDepth += 1
×
187

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

191
    /**
192
     * Commits transaction.
193
     * Error will be thrown if transaction was not started.
194
     */
195
    async commitTransaction(): Promise<void> {
196
        if (!this.isTransactionActive) throw new TransactionNotStartedError()
×
197

198
        await this.broadcaster.broadcast("BeforeTransactionCommit")
×
199

200
        if (this.transactionDepth > 1) {
×
201
            await this.query(
×
202
                `RELEASE SAVEPOINT typeorm_${this.transactionDepth - 1}`,
203
            )
204
        } else {
205
            await this.query("COMMIT")
×
206
            this.isTransactionActive = false
×
207
        }
208
        this.transactionDepth -= 1
×
209

210
        await this.broadcaster.broadcast("AfterTransactionCommit")
×
211
    }
212

213
    /**
214
     * Rollbacks transaction.
215
     * Error will be thrown if transaction was not started.
216
     */
217
    async rollbackTransaction(): Promise<void> {
218
        if (!this.isTransactionActive) throw new TransactionNotStartedError()
×
219

220
        await this.broadcaster.broadcast("BeforeTransactionRollback")
×
221

222
        if (this.transactionDepth > 1) {
×
223
            await this.query(
×
224
                `ROLLBACK TO SAVEPOINT typeorm_${this.transactionDepth - 1}`,
225
            )
226
        } else {
227
            await this.query("ROLLBACK")
×
228
            this.isTransactionActive = false
×
229
        }
230
        this.transactionDepth -= 1
×
231

232
        await this.broadcaster.broadcast("AfterTransactionRollback")
×
233
    }
234

235
    /**
236
     * Executes a given SQL query.
237
     */
238
    async query(
239
        query: string,
240
        parameters?: any[],
241
        useStructuredResult: boolean = false,
×
242
    ): Promise<any> {
243
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
×
244

245
        const databaseConnection = await this.connect()
×
246

247
        this.driver.connection.logger.logQuery(query, parameters, this)
×
248
        await this.broadcaster.broadcast("BeforeQuery", query, parameters)
×
249

250
        const broadcasterResult = new BroadcasterResult()
×
251

252
        try {
×
253
            const queryStartTime = Date.now()
×
254
            const raw = await databaseConnection.query(query, parameters)
×
255
            // log slow queries if maxQueryExecution time is set
256
            const maxQueryExecutionTime =
257
                this.driver.options.maxQueryExecutionTime
×
258
            const queryEndTime = Date.now()
×
259
            const queryExecutionTime = queryEndTime - queryStartTime
×
260

261
            this.broadcaster.broadcastAfterQueryEvent(
×
262
                broadcasterResult,
263
                query,
264
                parameters,
265
                true,
266
                queryExecutionTime,
267
                raw,
268
                undefined,
269
            )
270

271
            if (
×
272
                maxQueryExecutionTime &&
×
273
                queryExecutionTime > maxQueryExecutionTime
274
            )
275
                this.driver.connection.logger.logQuerySlow(
×
276
                    queryExecutionTime,
277
                    query,
278
                    parameters,
279
                    this,
280
                )
281

282
            const result = new QueryResult()
×
283
            if (raw) {
×
284
                if (raw.hasOwnProperty("rows")) {
×
285
                    result.records = raw.rows
×
286
                }
287

288
                if (raw.hasOwnProperty("rowCount")) {
×
289
                    result.affected = raw.rowCount
×
290
                }
291

292
                switch (raw.command) {
×
293
                    case "DELETE":
294
                    case "UPDATE":
295
                        // for UPDATE and DELETE query additionally return number of affected rows
296
                        result.raw = [raw.rows, raw.rowCount]
×
297
                        break
×
298
                    default:
299
                        result.raw = raw.rows
×
300
                }
301

302
                if (!useStructuredResult) {
×
303
                    return result.raw
×
304
                }
305
            }
306

307
            return result
×
308
        } catch (err) {
309
            this.driver.connection.logger.logQueryError(
×
310
                err,
311
                query,
312
                parameters,
313
                this,
314
            )
315
            this.broadcaster.broadcastAfterQueryEvent(
×
316
                broadcasterResult,
317
                query,
318
                parameters,
319
                false,
320
                undefined,
321
                undefined,
322
                err,
323
            )
324

325
            throw new QueryFailedError(query, parameters, err)
×
326
        } finally {
327
            await broadcasterResult.wait()
×
328
        }
329
    }
330

331
    /**
332
     * Returns raw data stream.
333
     */
334
    async stream(
335
        query: string,
336
        parameters?: any[],
337
        onEnd?: Function,
338
        onError?: Function,
339
    ): Promise<ReadStream> {
340
        const QueryStream = this.driver.loadStreamDependency()
×
341
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
×
342

343
        const databaseConnection = await this.connect()
×
344
        this.driver.connection.logger.logQuery(query, parameters, this)
×
345
        const stream = databaseConnection.query(
×
346
            new QueryStream(query, parameters),
347
        )
348
        if (onEnd) stream.on("end", onEnd)
×
349
        if (onError) stream.on("error", onError)
×
350

351
        return stream
×
352
    }
353

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

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

369
    /**
370
     * Checks if database with the given name exist.
371
     */
372
    async hasDatabase(database: string): Promise<boolean> {
373
        const result = await this.query(
×
374
            `SELECT * FROM pg_database WHERE datname='${database}';`,
375
        )
376
        return result.length ? true : false
×
377
    }
378

379
    /**
380
     * Loads currently using database
381
     */
382
    async getCurrentDatabase(): Promise<string> {
383
        const query = await this.query(`SELECT * FROM current_database()`)
×
384
        return query[0]["current_database"]
×
385
    }
386

387
    /**
388
     * Checks if schema with the given name exist.
389
     */
390
    async hasSchema(schema: string): Promise<boolean> {
391
        const result = await this.query(
×
392
            `SELECT * FROM "information_schema"."schemata" WHERE "schema_name" = '${schema}'`,
393
        )
394
        return result.length ? true : false
×
395
    }
396

397
    /**
398
     * Loads currently using database schema
399
     */
400
    async getCurrentSchema(): Promise<string> {
401
        const query = await this.query(`SELECT * FROM current_schema()`)
×
402
        return query[0]["current_schema"]
×
403
    }
404

405
    /**
406
     * Checks if table with the given name exist in the database.
407
     */
408
    async hasTable(tableOrName: Table | string): Promise<boolean> {
409
        const parsedTableName = this.driver.parseTableName(tableOrName)
×
410

411
        if (!parsedTableName.schema) {
×
412
            parsedTableName.schema = await this.getCurrentSchema()
×
413
        }
414

415
        const sql = `SELECT * FROM "information_schema"."tables" WHERE "table_schema" = '${parsedTableName.schema}' AND "table_name" = '${parsedTableName.tableName}'`
×
416
        const result = await this.query(sql)
×
417
        return result.length ? true : false
×
418
    }
419

420
    /**
421
     * Checks if column with the given name exist in the given table.
422
     */
423
    async hasColumn(
424
        tableOrName: Table | string,
425
        columnName: string,
426
    ): Promise<boolean> {
427
        const parsedTableName = this.driver.parseTableName(tableOrName)
×
428

429
        if (!parsedTableName.schema) {
×
430
            parsedTableName.schema = await this.getCurrentSchema()
×
431
        }
432

433
        const sql = `SELECT * FROM "information_schema"."columns" WHERE "table_schema" = '${parsedTableName.schema}' AND "table_name" = '${parsedTableName.tableName}' AND "column_name" = '${columnName}'`
×
434
        const result = await this.query(sql)
×
435
        return result.length ? true : false
×
436
    }
437

438
    /**
439
     * Creates a new database.
440
     * Note: Postgres does not support database creation inside a transaction block.
441
     */
442
    async createDatabase(
443
        database: string,
444
        ifNotExist?: boolean,
445
    ): Promise<void> {
446
        if (ifNotExist) {
×
447
            const databaseAlreadyExists = await this.hasDatabase(database)
×
448

449
            if (databaseAlreadyExists) return Promise.resolve()
×
450
        }
451

452
        const up = `CREATE DATABASE "${database}"`
×
453
        const down = `DROP DATABASE "${database}"`
×
454
        await this.executeQueries(new Query(up), new Query(down))
×
455
    }
456

457
    /**
458
     * Drops database.
459
     * Note: Postgres does not support database dropping inside a transaction block.
460
     */
461
    async dropDatabase(database: string, ifExist?: boolean): Promise<void> {
462
        const up = ifExist
×
463
            ? `DROP DATABASE IF EXISTS "${database}"`
464
            : `DROP DATABASE "${database}"`
465
        const down = `CREATE DATABASE "${database}"`
×
466
        await this.executeQueries(new Query(up), new Query(down))
×
467
    }
468

469
    /**
470
     * Creates a new table schema.
471
     */
472
    async createSchema(
473
        schemaPath: string,
474
        ifNotExist?: boolean,
475
    ): Promise<void> {
476
        const schema =
477
            schemaPath.indexOf(".") === -1
×
478
                ? schemaPath
479
                : schemaPath.split(".")[1]
480

481
        const up = ifNotExist
×
482
            ? `CREATE SCHEMA IF NOT EXISTS "${schema}"`
483
            : `CREATE SCHEMA "${schema}"`
484
        const down = `DROP SCHEMA "${schema}" CASCADE`
×
485
        await this.executeQueries(new Query(up), new Query(down))
×
486
    }
487

488
    /**
489
     * Drops table schema.
490
     */
491
    async dropSchema(
492
        schemaPath: string,
493
        ifExist?: boolean,
494
        isCascade?: boolean,
495
    ): Promise<void> {
496
        const schema =
497
            schemaPath.indexOf(".") === -1
×
498
                ? schemaPath
499
                : schemaPath.split(".")[1]
500

501
        const up = ifExist
×
502
            ? `DROP SCHEMA IF EXISTS "${schema}" ${isCascade ? "CASCADE" : ""}`
×
503
            : `DROP SCHEMA "${schema}" ${isCascade ? "CASCADE" : ""}`
×
504
        const down = `CREATE SCHEMA "${schema}"`
×
505
        await this.executeQueries(new Query(up), new Query(down))
×
506
    }
507

508
    /**
509
     * Creates a new table.
510
     */
511
    async createTable(
512
        table: Table,
513
        ifNotExist: boolean = false,
×
514
        createForeignKeys: boolean = true,
×
515
        createIndices: boolean = true,
×
516
    ): Promise<void> {
517
        if (ifNotExist) {
×
518
            const isTableExist = await this.hasTable(table)
×
519
            if (isTableExist) return Promise.resolve()
×
520
        }
521
        const upQueries: Query[] = []
×
522
        const downQueries: Query[] = []
×
523

524
        // if table have column with ENUM type, we must create this type in postgres.
525
        const enumColumns = table.columns.filter(
×
526
            (column) => column.type === "enum" || column.type === "simple-enum",
×
527
        )
528
        const createdEnumTypes: string[] = []
×
529
        for (const column of enumColumns) {
×
530
            // TODO: Should also check if values of existing type matches expected ones
531
            const hasEnum = await this.hasEnumType(table, column)
×
532
            const enumName = this.buildEnumName(table, column)
×
533

534
            // if enum with the same "enumName" is defined more then once, me must prevent double creation
535
            if (!hasEnum && createdEnumTypes.indexOf(enumName) === -1) {
×
536
                createdEnumTypes.push(enumName)
×
537
                upQueries.push(this.createEnumTypeSql(table, column, enumName))
×
538
                downQueries.push(this.dropEnumTypeSql(table, column, enumName))
×
539
            }
540
        }
541

542
        // if table have column with generated type, we must add the expression to the metadata table
543
        const generatedColumns = table.columns.filter(
×
544
            (column) =>
545
                column.generatedType === "STORED" && column.asExpression,
×
546
        )
547
        for (const column of generatedColumns) {
×
548
            const tableNameWithSchema = (
×
549
                await this.getTableNameWithSchema(table.name)
550
            ).split(".")
551
            const tableName = tableNameWithSchema[1]
×
552
            const schema = tableNameWithSchema[0]
×
553

554
            const insertQuery = this.insertTypeormMetadataSql({
×
555
                database: this.driver.database,
556
                schema,
557
                table: tableName,
558
                type: MetadataTableType.GENERATED_COLUMN,
559
                name: column.name,
560
                value: column.asExpression,
561
            })
562

563
            const deleteQuery = this.deleteTypeormMetadataSql({
×
564
                database: this.driver.database,
565
                schema,
566
                table: tableName,
567
                type: MetadataTableType.GENERATED_COLUMN,
568
                name: column.name,
569
            })
570

571
            upQueries.push(insertQuery)
×
572
            downQueries.push(deleteQuery)
×
573
        }
574

575
        upQueries.push(this.createTableSql(table, createForeignKeys))
×
576
        downQueries.push(this.dropTableSql(table))
×
577

578
        // if createForeignKeys is true, we must drop created foreign keys in down query.
579
        // createTable does not need separate method to create foreign keys, because it create fk's in the same query with table creation.
580
        if (createForeignKeys)
×
581
            table.foreignKeys.forEach((foreignKey) =>
×
582
                downQueries.push(this.dropForeignKeySql(table, foreignKey)),
×
583
            )
584

585
        if (createIndices) {
×
586
            table.indices.forEach((index) => {
×
587
                // new index may be passed without name. In this case we generate index name manually.
588
                if (!index.name)
×
589
                    index.name = this.connection.namingStrategy.indexName(
×
590
                        table,
591
                        index.columnNames,
592
                        index.where,
593
                    )
594
                upQueries.push(this.createIndexSql(table, index))
×
595
                downQueries.push(this.dropIndexSql(table, index))
×
596
            })
597
        }
598

599
        if (table.comment) {
×
600
            upQueries.push(
×
601
                new Query(
602
                    "COMMENT ON TABLE " +
603
                        this.escapePath(table) +
604
                        " IS '" +
605
                        table.comment +
606
                        "'",
607
                ),
608
            )
609
            downQueries.push(
×
610
                new Query(
611
                    "COMMENT ON TABLE " + this.escapePath(table) + " IS NULL",
612
                ),
613
            )
614
        }
615

616
        await this.executeQueries(upQueries, downQueries)
×
617
    }
618

619
    /**
620
     * Drops the table.
621
     */
622
    async dropTable(
623
        target: Table | string,
624
        ifExist?: boolean,
625
        dropForeignKeys: boolean = true,
×
626
        dropIndices: boolean = true,
×
627
    ): Promise<void> {
628
        // It needs because if table does not exist and dropForeignKeys or dropIndices is true, we don't need
629
        // to perform drop queries for foreign keys and indices.
630
        if (ifExist) {
×
631
            const isTableExist = await this.hasTable(target)
×
632
            if (!isTableExist) return Promise.resolve()
×
633
        }
634

635
        // if dropTable called with dropForeignKeys = true, we must create foreign keys in down query.
636
        const createForeignKeys: boolean = dropForeignKeys
×
637
        const tablePath = this.getTablePath(target)
×
638
        const table = await this.getCachedTable(tablePath)
×
639
        const upQueries: Query[] = []
×
640
        const downQueries: Query[] = []
×
641

642
        if (dropIndices) {
×
643
            table.indices.forEach((index) => {
×
644
                upQueries.push(this.dropIndexSql(table, index))
×
645
                downQueries.push(this.createIndexSql(table, index))
×
646
            })
647
        }
648

649
        if (dropForeignKeys)
×
650
            table.foreignKeys.forEach((foreignKey) =>
×
651
                upQueries.push(this.dropForeignKeySql(table, foreignKey)),
×
652
            )
653

654
        upQueries.push(this.dropTableSql(table))
×
655
        downQueries.push(this.createTableSql(table, createForeignKeys))
×
656

657
        // if table had columns with generated type, we must remove the expression from the metadata table
658
        const generatedColumns = table.columns.filter(
×
659
            (column) => column.generatedType && column.asExpression,
×
660
        )
661
        for (const column of generatedColumns) {
×
662
            const tableNameWithSchema = (
×
663
                await this.getTableNameWithSchema(table.name)
664
            ).split(".")
665
            const tableName = tableNameWithSchema[1]
×
666
            const schema = tableNameWithSchema[0]
×
667

668
            const deleteQuery = this.deleteTypeormMetadataSql({
×
669
                database: this.driver.database,
670
                schema,
671
                table: tableName,
672
                type: MetadataTableType.GENERATED_COLUMN,
673
                name: column.name,
674
            })
675

676
            const insertQuery = this.insertTypeormMetadataSql({
×
677
                database: this.driver.database,
678
                schema,
679
                table: tableName,
680
                type: MetadataTableType.GENERATED_COLUMN,
681
                name: column.name,
682
                value: column.asExpression,
683
            })
684

685
            upQueries.push(deleteQuery)
×
686
            downQueries.push(insertQuery)
×
687
        }
688

689
        await this.executeQueries(upQueries, downQueries)
×
690
    }
691

692
    /**
693
     * Creates a new view.
694
     */
695
    async createView(
696
        view: View,
697
        syncWithMetadata: boolean = false,
×
698
    ): Promise<void> {
699
        const upQueries: Query[] = []
×
700
        const downQueries: Query[] = []
×
701
        upQueries.push(this.createViewSql(view))
×
702
        if (syncWithMetadata)
×
703
            upQueries.push(await this.insertViewDefinitionSql(view))
×
704
        downQueries.push(this.dropViewSql(view))
×
705
        if (syncWithMetadata)
×
706
            downQueries.push(await this.deleteViewDefinitionSql(view))
×
707
        await this.executeQueries(upQueries, downQueries)
×
708
    }
709

710
    /**
711
     * Drops the view.
712
     */
713
    async dropView(target: View | string): Promise<void> {
714
        const viewName = InstanceChecker.isView(target) ? target.name : target
×
715
        const view = await this.getCachedView(viewName)
×
716

717
        const upQueries: Query[] = []
×
718
        const downQueries: Query[] = []
×
719
        upQueries.push(await this.deleteViewDefinitionSql(view))
×
720
        upQueries.push(this.dropViewSql(view))
×
721
        downQueries.push(await this.insertViewDefinitionSql(view))
×
722
        downQueries.push(this.createViewSql(view))
×
723
        await this.executeQueries(upQueries, downQueries)
×
724
    }
725

726
    /**
727
     * Renames the given table.
728
     */
729
    async renameTable(
730
        oldTableOrName: Table | string,
731
        newTableName: string,
732
    ): Promise<void> {
733
        const upQueries: Query[] = []
×
734
        const downQueries: Query[] = []
×
735
        const oldTable = InstanceChecker.isTable(oldTableOrName)
×
736
            ? oldTableOrName
737
            : await this.getCachedTable(oldTableOrName)
738
        const newTable = oldTable.clone()
×
739

740
        const { schema: schemaName, tableName: oldTableName } =
741
            this.driver.parseTableName(oldTable)
×
742

743
        newTable.name = schemaName
×
744
            ? `${schemaName}.${newTableName}`
745
            : newTableName
746

747
        upQueries.push(
×
748
            new Query(
749
                `ALTER TABLE ${this.escapePath(
750
                    oldTable,
751
                )} RENAME TO "${newTableName}"`,
752
            ),
753
        )
754
        downQueries.push(
×
755
            new Query(
756
                `ALTER TABLE ${this.escapePath(
757
                    newTable,
758
                )} RENAME TO "${oldTableName}"`,
759
            ),
760
        )
761

762
        // rename column primary key constraint if it has default constraint name
763
        if (
×
764
            newTable.primaryColumns.length > 0 &&
×
765
            !newTable.primaryColumns[0].primaryKeyConstraintName
766
        ) {
767
            const columnNames = newTable.primaryColumns.map(
×
768
                (column) => column.name,
×
769
            )
770

771
            const oldPkName = this.connection.namingStrategy.primaryKeyName(
×
772
                oldTable,
773
                columnNames,
774
            )
775

776
            const newPkName = this.connection.namingStrategy.primaryKeyName(
×
777
                newTable,
778
                columnNames,
779
            )
780

781
            upQueries.push(
×
782
                new Query(
783
                    `ALTER TABLE ${this.escapePath(
784
                        newTable,
785
                    )} RENAME CONSTRAINT "${oldPkName}" TO "${newPkName}"`,
786
                ),
787
            )
788
            downQueries.push(
×
789
                new Query(
790
                    `ALTER TABLE ${this.escapePath(
791
                        newTable,
792
                    )} RENAME CONSTRAINT "${newPkName}" TO "${oldPkName}"`,
793
                ),
794
            )
795
        }
796

797
        // rename sequences
798
        newTable.columns.map((col) => {
×
799
            if (col.isGenerated && col.generationStrategy === "increment") {
×
800
                const sequencePath = this.buildSequencePath(oldTable, col.name)
×
801
                const sequenceName = this.buildSequenceName(oldTable, col.name)
×
802

803
                const newSequencePath = this.buildSequencePath(
×
804
                    newTable,
805
                    col.name,
806
                )
807
                const newSequenceName = this.buildSequenceName(
×
808
                    newTable,
809
                    col.name,
810
                )
811

812
                const up = `ALTER SEQUENCE ${this.escapePath(
×
813
                    sequencePath,
814
                )} RENAME TO "${newSequenceName}"`
815
                const down = `ALTER SEQUENCE ${this.escapePath(
×
816
                    newSequencePath,
817
                )} RENAME TO "${sequenceName}"`
818

819
                upQueries.push(new Query(up))
×
820
                downQueries.push(new Query(down))
×
821
            }
822
        })
823

824
        // rename unique constraints
825
        newTable.uniques.forEach((unique) => {
×
826
            const oldUniqueName =
827
                this.connection.namingStrategy.uniqueConstraintName(
×
828
                    oldTable,
829
                    unique.columnNames,
830
                )
831

832
            // Skip renaming if Unique has user defined constraint name
833
            if (unique.name !== oldUniqueName) return
×
834

835
            // build new constraint name
836
            const newUniqueName =
837
                this.connection.namingStrategy.uniqueConstraintName(
×
838
                    newTable,
839
                    unique.columnNames,
840
                )
841

842
            // build queries
843
            upQueries.push(
×
844
                new Query(
845
                    `ALTER TABLE ${this.escapePath(
846
                        newTable,
847
                    )} RENAME CONSTRAINT "${
848
                        unique.name
849
                    }" TO "${newUniqueName}"`,
850
                ),
851
            )
852
            downQueries.push(
×
853
                new Query(
854
                    `ALTER TABLE ${this.escapePath(
855
                        newTable,
856
                    )} RENAME CONSTRAINT "${newUniqueName}" TO "${
857
                        unique.name
858
                    }"`,
859
                ),
860
            )
861

862
            // replace constraint name
863
            unique.name = newUniqueName
×
864
        })
865

866
        // rename index constraints
867
        newTable.indices.forEach((index) => {
×
868
            const oldIndexName = this.connection.namingStrategy.indexName(
×
869
                oldTable,
870
                index.columnNames,
871
                index.where,
872
            )
873

874
            // Skip renaming if Index has user defined constraint name
875
            if (index.name !== oldIndexName) return
×
876

877
            // build new constraint name
878
            const { schema } = this.driver.parseTableName(newTable)
×
879
            const newIndexName = this.connection.namingStrategy.indexName(
×
880
                newTable,
881
                index.columnNames,
882
                index.where,
883
            )
884

885
            // build queries
886
            const up = schema
×
887
                ? `ALTER INDEX "${schema}"."${index.name}" RENAME TO "${newIndexName}"`
888
                : `ALTER INDEX "${index.name}" RENAME TO "${newIndexName}"`
889
            const down = schema
×
890
                ? `ALTER INDEX "${schema}"."${newIndexName}" RENAME TO "${index.name}"`
891
                : `ALTER INDEX "${newIndexName}" RENAME TO "${index.name}"`
892
            upQueries.push(new Query(up))
×
893
            downQueries.push(new Query(down))
×
894

895
            // replace constraint name
896
            index.name = newIndexName
×
897
        })
898

899
        // rename foreign key constraints
900
        newTable.foreignKeys.forEach((foreignKey) => {
×
901
            const oldForeignKeyName =
902
                this.connection.namingStrategy.foreignKeyName(
×
903
                    oldTable,
904
                    foreignKey.columnNames,
905
                    this.getTablePath(foreignKey),
906
                    foreignKey.referencedColumnNames,
907
                )
908

909
            // Skip renaming if foreign key has user defined constraint name
910
            if (foreignKey.name !== oldForeignKeyName) return
×
911

912
            // build new constraint name
913
            const newForeignKeyName =
914
                this.connection.namingStrategy.foreignKeyName(
×
915
                    newTable,
916
                    foreignKey.columnNames,
917
                    this.getTablePath(foreignKey),
918
                    foreignKey.referencedColumnNames,
919
                )
920

921
            // build queries
922
            upQueries.push(
×
923
                new Query(
924
                    `ALTER TABLE ${this.escapePath(
925
                        newTable,
926
                    )} RENAME CONSTRAINT "${
927
                        foreignKey.name
928
                    }" TO "${newForeignKeyName}"`,
929
                ),
930
            )
931
            downQueries.push(
×
932
                new Query(
933
                    `ALTER TABLE ${this.escapePath(
934
                        newTable,
935
                    )} RENAME CONSTRAINT "${newForeignKeyName}" TO "${
936
                        foreignKey.name
937
                    }"`,
938
                ),
939
            )
940

941
            // replace constraint name
942
            foreignKey.name = newForeignKeyName
×
943
        })
944

945
        // rename ENUM types
946
        const enumColumns = newTable.columns.filter(
×
947
            (column) => column.type === "enum" || column.type === "simple-enum",
×
948
        )
949
        for (const column of enumColumns) {
×
950
            // skip renaming for user-defined enum name
951
            if (column.enumName) continue
×
952

953
            const oldEnumType = await this.getUserDefinedTypeName(
×
954
                oldTable,
955
                column,
956
            )
957
            upQueries.push(
×
958
                new Query(
959
                    `ALTER TYPE "${oldEnumType.schema}"."${
960
                        oldEnumType.name
961
                    }" RENAME TO ${this.buildEnumName(
962
                        newTable,
963
                        column,
964
                        false,
965
                    )}`,
966
                ),
967
            )
968
            downQueries.push(
×
969
                new Query(
970
                    `ALTER TYPE ${this.buildEnumName(
971
                        newTable,
972
                        column,
973
                    )} RENAME TO "${oldEnumType.name}"`,
974
                ),
975
            )
976
        }
977
        await this.executeQueries(upQueries, downQueries)
×
978
    }
979

980
    /**
981
     * Creates a new column from the column in the table.
982
     */
983
    async addColumn(
984
        tableOrName: Table | string,
985
        column: TableColumn,
986
    ): Promise<void> {
987
        const table = InstanceChecker.isTable(tableOrName)
×
988
            ? tableOrName
989
            : await this.getCachedTable(tableOrName)
990
        const clonedTable = table.clone()
×
991
        const upQueries: Query[] = []
×
992
        const downQueries: Query[] = []
×
993

994
        if (column.type === "enum" || column.type === "simple-enum") {
×
995
            const hasEnum = await this.hasEnumType(table, column)
×
996
            if (!hasEnum) {
×
997
                upQueries.push(this.createEnumTypeSql(table, column))
×
998
                downQueries.push(this.dropEnumTypeSql(table, column))
×
999
            }
1000
        }
1001

1002
        upQueries.push(
×
1003
            new Query(
1004
                `ALTER TABLE ${this.escapePath(
1005
                    table,
1006
                )} ADD ${this.buildCreateColumnSql(table, column)}`,
1007
            ),
1008
        )
1009
        downQueries.push(
×
1010
            new Query(
1011
                `ALTER TABLE ${this.escapePath(table)} DROP COLUMN "${
1012
                    column.name
1013
                }"`,
1014
            ),
1015
        )
1016

1017
        // create or update primary key constraint
1018
        if (column.isPrimary) {
×
1019
            const primaryColumns = clonedTable.primaryColumns
×
1020
            // if table already have primary key, me must drop it and recreate again
1021
            if (primaryColumns.length > 0) {
×
1022
                const pkName = primaryColumns[0].primaryKeyConstraintName
×
1023
                    ? primaryColumns[0].primaryKeyConstraintName
1024
                    : this.connection.namingStrategy.primaryKeyName(
1025
                          clonedTable,
1026
                          primaryColumns.map((column) => column.name),
×
1027
                      )
1028

1029
                const columnNames = primaryColumns
×
1030
                    .map((column) => `"${column.name}"`)
×
1031
                    .join(", ")
1032

1033
                upQueries.push(
×
1034
                    new Query(
1035
                        `ALTER TABLE ${this.escapePath(
1036
                            table,
1037
                        )} DROP CONSTRAINT "${pkName}"`,
1038
                    ),
1039
                )
1040
                downQueries.push(
×
1041
                    new Query(
1042
                        `ALTER TABLE ${this.escapePath(
1043
                            table,
1044
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1045
                    ),
1046
                )
1047
            }
1048

1049
            primaryColumns.push(column)
×
1050
            const pkName = primaryColumns[0].primaryKeyConstraintName
×
1051
                ? primaryColumns[0].primaryKeyConstraintName
1052
                : this.connection.namingStrategy.primaryKeyName(
1053
                      clonedTable,
1054
                      primaryColumns.map((column) => column.name),
×
1055
                  )
1056

1057
            const columnNames = primaryColumns
×
1058
                .map((column) => `"${column.name}"`)
×
1059
                .join(", ")
1060

1061
            upQueries.push(
×
1062
                new Query(
1063
                    `ALTER TABLE ${this.escapePath(
1064
                        table,
1065
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1066
                ),
1067
            )
1068
            downQueries.push(
×
1069
                new Query(
1070
                    `ALTER TABLE ${this.escapePath(
1071
                        table,
1072
                    )} DROP CONSTRAINT "${pkName}"`,
1073
                ),
1074
            )
1075
        }
1076

1077
        // create column index
1078
        const columnIndex = clonedTable.indices.find(
×
1079
            (index) =>
1080
                index.columnNames.length === 1 &&
×
1081
                index.columnNames[0] === column.name,
1082
        )
1083
        if (columnIndex) {
×
1084
            upQueries.push(this.createIndexSql(table, columnIndex))
×
1085
            downQueries.push(this.dropIndexSql(table, columnIndex))
×
1086
        }
1087

1088
        // create unique constraint
1089
        if (column.isUnique) {
×
1090
            const uniqueConstraint = new TableUnique({
×
1091
                name: this.connection.namingStrategy.uniqueConstraintName(
1092
                    table,
1093
                    [column.name],
1094
                ),
1095
                columnNames: [column.name],
1096
            })
1097
            clonedTable.uniques.push(uniqueConstraint)
×
1098
            upQueries.push(
×
1099
                new Query(
1100
                    `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
1101
                        uniqueConstraint.name
1102
                    }" UNIQUE ("${column.name}")`,
1103
                ),
1104
            )
1105
            downQueries.push(
×
1106
                new Query(
1107
                    `ALTER TABLE ${this.escapePath(table)} DROP CONSTRAINT "${
1108
                        uniqueConstraint.name
1109
                    }"`,
1110
                ),
1111
            )
1112
        }
1113

1114
        if (column.generatedType === "STORED" && column.asExpression) {
×
1115
            const tableNameWithSchema = (
×
1116
                await this.getTableNameWithSchema(table.name)
1117
            ).split(".")
1118
            const tableName = tableNameWithSchema[1]
×
1119
            const schema = tableNameWithSchema[0]
×
1120

1121
            const insertQuery = this.insertTypeormMetadataSql({
×
1122
                database: this.driver.database,
1123
                schema,
1124
                table: tableName,
1125
                type: MetadataTableType.GENERATED_COLUMN,
1126
                name: column.name,
1127
                value: column.asExpression,
1128
            })
1129

1130
            const deleteQuery = this.deleteTypeormMetadataSql({
×
1131
                database: this.driver.database,
1132
                schema,
1133
                table: tableName,
1134
                type: MetadataTableType.GENERATED_COLUMN,
1135
                name: column.name,
1136
            })
1137

1138
            upQueries.push(insertQuery)
×
1139
            downQueries.push(deleteQuery)
×
1140
        }
1141

1142
        // create column's comment
1143
        if (column.comment) {
×
1144
            upQueries.push(
×
1145
                new Query(
1146
                    `COMMENT ON COLUMN ${this.escapePath(table)}."${
1147
                        column.name
1148
                    }" IS ${this.escapeComment(column.comment)}`,
1149
                ),
1150
            )
1151
            downQueries.push(
×
1152
                new Query(
1153
                    `COMMENT ON COLUMN ${this.escapePath(table)}."${
1154
                        column.name
1155
                    }" IS ${this.escapeComment(column.comment)}`,
1156
                ),
1157
            )
1158
        }
1159

1160
        await this.executeQueries(upQueries, downQueries)
×
1161

1162
        clonedTable.addColumn(column)
×
1163
        this.replaceCachedTable(table, clonedTable)
×
1164
    }
1165

1166
    /**
1167
     * Creates a new columns from the column in the table.
1168
     */
1169
    async addColumns(
1170
        tableOrName: Table | string,
1171
        columns: TableColumn[],
1172
    ): Promise<void> {
1173
        for (const column of columns) {
×
1174
            await this.addColumn(tableOrName, column)
×
1175
        }
1176
    }
1177

1178
    /**
1179
     * Renames column in the given table.
1180
     */
1181
    async renameColumn(
1182
        tableOrName: Table | string,
1183
        oldTableColumnOrName: TableColumn | string,
1184
        newTableColumnOrName: TableColumn | string,
1185
    ): Promise<void> {
1186
        const table = InstanceChecker.isTable(tableOrName)
×
1187
            ? tableOrName
1188
            : await this.getCachedTable(tableOrName)
1189
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
×
1190
            ? oldTableColumnOrName
1191
            : table.columns.find((c) => c.name === oldTableColumnOrName)
×
1192
        if (!oldColumn)
×
1193
            throw new TypeORMError(
×
1194
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
1195
            )
1196

1197
        let newColumn
1198
        if (InstanceChecker.isTableColumn(newTableColumnOrName)) {
×
1199
            newColumn = newTableColumnOrName
×
1200
        } else {
1201
            newColumn = oldColumn.clone()
×
1202
            newColumn.name = newTableColumnOrName
×
1203
        }
1204

1205
        return this.changeColumn(table, oldColumn, newColumn)
×
1206
    }
1207

1208
    /**
1209
     * Changes a column in the table.
1210
     */
1211
    async changeColumn(
1212
        tableOrName: Table | string,
1213
        oldTableColumnOrName: TableColumn | string,
1214
        newColumn: TableColumn,
1215
    ): Promise<void> {
1216
        const table = InstanceChecker.isTable(tableOrName)
×
1217
            ? tableOrName
1218
            : await this.getCachedTable(tableOrName)
1219
        let clonedTable = table.clone()
×
1220
        const upQueries: Query[] = []
×
1221
        const downQueries: Query[] = []
×
1222
        let defaultValueChanged = false
×
1223

1224
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
×
1225
            ? oldTableColumnOrName
1226
            : table.columns.find(
1227
                  (column) => column.name === oldTableColumnOrName,
×
1228
              )
1229
        if (!oldColumn)
×
1230
            throw new TypeORMError(
×
1231
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
1232
            )
1233

1234
        if (
×
1235
            oldColumn.type !== newColumn.type ||
×
1236
            oldColumn.length !== newColumn.length ||
1237
            newColumn.isArray !== oldColumn.isArray ||
1238
            (!oldColumn.generatedType &&
1239
                newColumn.generatedType === "STORED") ||
1240
            (oldColumn.asExpression !== newColumn.asExpression &&
1241
                newColumn.generatedType === "STORED")
1242
        ) {
1243
            // To avoid data conversion, we just recreate column
1244
            await this.dropColumn(table, oldColumn)
×
1245
            await this.addColumn(table, newColumn)
×
1246

1247
            // update cloned table
1248
            clonedTable = table.clone()
×
1249
        } else {
1250
            if (oldColumn.name !== newColumn.name) {
×
1251
                // rename column
1252
                upQueries.push(
×
1253
                    new Query(
1254
                        `ALTER TABLE ${this.escapePath(table)} RENAME COLUMN "${
1255
                            oldColumn.name
1256
                        }" TO "${newColumn.name}"`,
1257
                    ),
1258
                )
1259
                downQueries.push(
×
1260
                    new Query(
1261
                        `ALTER TABLE ${this.escapePath(table)} RENAME COLUMN "${
1262
                            newColumn.name
1263
                        }" TO "${oldColumn.name}"`,
1264
                    ),
1265
                )
1266

1267
                // rename ENUM type
1268
                if (
×
1269
                    oldColumn.type === "enum" ||
×
1270
                    oldColumn.type === "simple-enum"
1271
                ) {
1272
                    const oldEnumType = await this.getUserDefinedTypeName(
×
1273
                        table,
1274
                        oldColumn,
1275
                    )
1276
                    upQueries.push(
×
1277
                        new Query(
1278
                            `ALTER TYPE "${oldEnumType.schema}"."${
1279
                                oldEnumType.name
1280
                            }" RENAME TO ${this.buildEnumName(
1281
                                table,
1282
                                newColumn,
1283
                                false,
1284
                            )}`,
1285
                        ),
1286
                    )
1287
                    downQueries.push(
×
1288
                        new Query(
1289
                            `ALTER TYPE ${this.buildEnumName(
1290
                                table,
1291
                                newColumn,
1292
                            )} RENAME TO "${oldEnumType.name}"`,
1293
                        ),
1294
                    )
1295
                }
1296

1297
                // rename column primary key constraint
1298
                if (
×
1299
                    oldColumn.isPrimary === true &&
×
1300
                    !oldColumn.primaryKeyConstraintName
1301
                ) {
1302
                    const primaryColumns = clonedTable.primaryColumns
×
1303

1304
                    // build old primary constraint name
1305
                    const columnNames = primaryColumns.map(
×
1306
                        (column) => column.name,
×
1307
                    )
1308
                    const oldPkName =
1309
                        this.connection.namingStrategy.primaryKeyName(
×
1310
                            clonedTable,
1311
                            columnNames,
1312
                        )
1313

1314
                    // replace old column name with new column name
1315
                    columnNames.splice(columnNames.indexOf(oldColumn.name), 1)
×
1316
                    columnNames.push(newColumn.name)
×
1317

1318
                    // build new primary constraint name
1319
                    const newPkName =
1320
                        this.connection.namingStrategy.primaryKeyName(
×
1321
                            clonedTable,
1322
                            columnNames,
1323
                        )
1324

1325
                    upQueries.push(
×
1326
                        new Query(
1327
                            `ALTER TABLE ${this.escapePath(
1328
                                table,
1329
                            )} RENAME CONSTRAINT "${oldPkName}" TO "${newPkName}"`,
1330
                        ),
1331
                    )
1332
                    downQueries.push(
×
1333
                        new Query(
1334
                            `ALTER TABLE ${this.escapePath(
1335
                                table,
1336
                            )} RENAME CONSTRAINT "${newPkName}" TO "${oldPkName}"`,
1337
                        ),
1338
                    )
1339
                }
1340

1341
                // rename column sequence
1342
                if (
×
1343
                    oldColumn.isGenerated === true &&
×
1344
                    newColumn.generationStrategy === "increment"
1345
                ) {
1346
                    const sequencePath = this.buildSequencePath(
×
1347
                        table,
1348
                        oldColumn.name,
1349
                    )
1350
                    const sequenceName = this.buildSequenceName(
×
1351
                        table,
1352
                        oldColumn.name,
1353
                    )
1354

1355
                    const newSequencePath = this.buildSequencePath(
×
1356
                        table,
1357
                        newColumn.name,
1358
                    )
1359
                    const newSequenceName = this.buildSequenceName(
×
1360
                        table,
1361
                        newColumn.name,
1362
                    )
1363

1364
                    const up = `ALTER SEQUENCE ${this.escapePath(
×
1365
                        sequencePath,
1366
                    )} RENAME TO "${newSequenceName}"`
1367
                    const down = `ALTER SEQUENCE ${this.escapePath(
×
1368
                        newSequencePath,
1369
                    )} RENAME TO "${sequenceName}"`
1370

1371
                    upQueries.push(new Query(up))
×
1372
                    downQueries.push(new Query(down))
×
1373
                }
1374

1375
                // rename unique constraints
1376
                clonedTable.findColumnUniques(oldColumn).forEach((unique) => {
×
1377
                    const oldUniqueName =
1378
                        this.connection.namingStrategy.uniqueConstraintName(
×
1379
                            clonedTable,
1380
                            unique.columnNames,
1381
                        )
1382

1383
                    // Skip renaming if Unique has user defined constraint name
1384
                    if (unique.name !== oldUniqueName) return
×
1385

1386
                    // build new constraint name
1387
                    unique.columnNames.splice(
×
1388
                        unique.columnNames.indexOf(oldColumn.name),
1389
                        1,
1390
                    )
1391
                    unique.columnNames.push(newColumn.name)
×
1392
                    const newUniqueName =
1393
                        this.connection.namingStrategy.uniqueConstraintName(
×
1394
                            clonedTable,
1395
                            unique.columnNames,
1396
                        )
1397

1398
                    // build queries
1399
                    upQueries.push(
×
1400
                        new Query(
1401
                            `ALTER TABLE ${this.escapePath(
1402
                                table,
1403
                            )} RENAME CONSTRAINT "${
1404
                                unique.name
1405
                            }" TO "${newUniqueName}"`,
1406
                        ),
1407
                    )
1408
                    downQueries.push(
×
1409
                        new Query(
1410
                            `ALTER TABLE ${this.escapePath(
1411
                                table,
1412
                            )} RENAME CONSTRAINT "${newUniqueName}" TO "${
1413
                                unique.name
1414
                            }"`,
1415
                        ),
1416
                    )
1417

1418
                    // replace constraint name
1419
                    unique.name = newUniqueName
×
1420
                })
1421

1422
                // rename index constraints
1423
                clonedTable.findColumnIndices(oldColumn).forEach((index) => {
×
1424
                    const oldIndexName =
1425
                        this.connection.namingStrategy.indexName(
×
1426
                            clonedTable,
1427
                            index.columnNames,
1428
                            index.where,
1429
                        )
1430

1431
                    // Skip renaming if Index has user defined constraint name
1432
                    if (index.name !== oldIndexName) return
×
1433

1434
                    // build new constraint name
1435
                    index.columnNames.splice(
×
1436
                        index.columnNames.indexOf(oldColumn.name),
1437
                        1,
1438
                    )
1439
                    index.columnNames.push(newColumn.name)
×
1440
                    const { schema } = this.driver.parseTableName(table)
×
1441
                    const newIndexName =
1442
                        this.connection.namingStrategy.indexName(
×
1443
                            clonedTable,
1444
                            index.columnNames,
1445
                            index.where,
1446
                        )
1447

1448
                    // build queries
1449
                    const up = schema
×
1450
                        ? `ALTER INDEX "${schema}"."${index.name}" RENAME TO "${newIndexName}"`
1451
                        : `ALTER INDEX "${index.name}" RENAME TO "${newIndexName}"`
1452
                    const down = schema
×
1453
                        ? `ALTER INDEX "${schema}"."${newIndexName}" RENAME TO "${index.name}"`
1454
                        : `ALTER INDEX "${newIndexName}" RENAME TO "${index.name}"`
1455

1456
                    upQueries.push(new Query(up))
×
1457
                    downQueries.push(new Query(down))
×
1458

1459
                    // replace constraint name
1460
                    index.name = newIndexName
×
1461
                })
1462

1463
                // rename foreign key constraints
1464
                clonedTable
×
1465
                    .findColumnForeignKeys(oldColumn)
1466
                    .forEach((foreignKey) => {
1467
                        const foreignKeyName =
1468
                            this.connection.namingStrategy.foreignKeyName(
×
1469
                                clonedTable,
1470
                                foreignKey.columnNames,
1471
                                this.getTablePath(foreignKey),
1472
                                foreignKey.referencedColumnNames,
1473
                            )
1474

1475
                        // Skip renaming if foreign key has user defined constraint name
1476
                        if (foreignKey.name !== foreignKeyName) return
×
1477

1478
                        // build new constraint name
1479
                        foreignKey.columnNames.splice(
×
1480
                            foreignKey.columnNames.indexOf(oldColumn.name),
1481
                            1,
1482
                        )
1483
                        foreignKey.columnNames.push(newColumn.name)
×
1484
                        const newForeignKeyName =
1485
                            this.connection.namingStrategy.foreignKeyName(
×
1486
                                clonedTable,
1487
                                foreignKey.columnNames,
1488
                                this.getTablePath(foreignKey),
1489
                                foreignKey.referencedColumnNames,
1490
                            )
1491

1492
                        // build queries
1493
                        upQueries.push(
×
1494
                            new Query(
1495
                                `ALTER TABLE ${this.escapePath(
1496
                                    table,
1497
                                )} RENAME CONSTRAINT "${
1498
                                    foreignKey.name
1499
                                }" TO "${newForeignKeyName}"`,
1500
                            ),
1501
                        )
1502
                        downQueries.push(
×
1503
                            new Query(
1504
                                `ALTER TABLE ${this.escapePath(
1505
                                    table,
1506
                                )} RENAME CONSTRAINT "${newForeignKeyName}" TO "${
1507
                                    foreignKey.name
1508
                                }"`,
1509
                            ),
1510
                        )
1511

1512
                        // replace constraint name
1513
                        foreignKey.name = newForeignKeyName
×
1514
                    })
1515

1516
                // rename old column in the Table object
1517
                const oldTableColumn = clonedTable.columns.find(
×
1518
                    (column) => column.name === oldColumn.name,
×
1519
                )
1520
                clonedTable.columns[
×
1521
                    clonedTable.columns.indexOf(oldTableColumn!)
1522
                ].name = newColumn.name
1523
                oldColumn.name = newColumn.name
×
1524
            }
1525

1526
            if (
×
1527
                newColumn.precision !== oldColumn.precision ||
×
1528
                newColumn.scale !== oldColumn.scale
1529
            ) {
1530
                upQueries.push(
×
1531
                    new Query(
1532
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
1533
                            newColumn.name
1534
                        }" TYPE ${this.driver.createFullType(newColumn)}`,
1535
                    ),
1536
                )
1537
                downQueries.push(
×
1538
                    new Query(
1539
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
1540
                            newColumn.name
1541
                        }" TYPE ${this.driver.createFullType(oldColumn)}`,
1542
                    ),
1543
                )
1544
            }
1545

1546
            if (
×
1547
                (newColumn.type === "enum" ||
×
1548
                    newColumn.type === "simple-enum") &&
1549
                (oldColumn.type === "enum" ||
1550
                    oldColumn.type === "simple-enum") &&
1551
                (!OrmUtils.isArraysEqual(newColumn.enum!, oldColumn.enum!) ||
1552
                    newColumn.enumName !== oldColumn.enumName)
1553
            ) {
1554
                const arraySuffix = newColumn.isArray ? "[]" : ""
×
1555

1556
                // "public"."new_enum"
1557
                const newEnumName = this.buildEnumName(table, newColumn)
×
1558

1559
                // "public"."old_enum"
1560
                const oldEnumName = this.buildEnumName(table, oldColumn)
×
1561

1562
                // "old_enum"
1563
                const oldEnumNameWithoutSchema = this.buildEnumName(
×
1564
                    table,
1565
                    oldColumn,
1566
                    false,
1567
                )
1568

1569
                //"public"."old_enum_old"
1570
                const oldEnumNameWithSchema_old = this.buildEnumName(
×
1571
                    table,
1572
                    oldColumn,
1573
                    true,
1574
                    false,
1575
                    true,
1576
                )
1577

1578
                //"old_enum_old"
1579
                const oldEnumNameWithoutSchema_old = this.buildEnumName(
×
1580
                    table,
1581
                    oldColumn,
1582
                    false,
1583
                    false,
1584
                    true,
1585
                )
1586

1587
                // rename old ENUM
1588
                upQueries.push(
×
1589
                    new Query(
1590
                        `ALTER TYPE ${oldEnumName} RENAME TO ${oldEnumNameWithoutSchema_old}`,
1591
                    ),
1592
                )
1593
                downQueries.push(
×
1594
                    new Query(
1595
                        `ALTER TYPE ${oldEnumNameWithSchema_old} RENAME TO ${oldEnumNameWithoutSchema}`,
1596
                    ),
1597
                )
1598

1599
                // create new ENUM
1600
                upQueries.push(
×
1601
                    this.createEnumTypeSql(table, newColumn, newEnumName),
1602
                )
1603
                downQueries.push(
×
1604
                    this.dropEnumTypeSql(table, newColumn, newEnumName),
1605
                )
1606

1607
                // if column have default value, we must drop it to avoid issues with type casting
1608
                if (
×
1609
                    oldColumn.default !== null &&
×
1610
                    oldColumn.default !== undefined
1611
                ) {
1612
                    // mark default as changed to prevent double update
1613
                    defaultValueChanged = true
×
1614
                    upQueries.push(
×
1615
                        new Query(
1616
                            `ALTER TABLE ${this.escapePath(
1617
                                table,
1618
                            )} ALTER COLUMN "${oldColumn.name}" DROP DEFAULT`,
1619
                        ),
1620
                    )
1621
                    downQueries.push(
×
1622
                        new Query(
1623
                            `ALTER TABLE ${this.escapePath(
1624
                                table,
1625
                            )} ALTER COLUMN "${oldColumn.name}" SET DEFAULT ${
1626
                                oldColumn.default
1627
                            }`,
1628
                        ),
1629
                    )
1630
                }
1631

1632
                // build column types
1633
                const upType = `${newEnumName}${arraySuffix} USING "${newColumn.name}"::"text"::${newEnumName}${arraySuffix}`
×
1634
                const downType = `${oldEnumNameWithSchema_old}${arraySuffix} USING "${newColumn.name}"::"text"::${oldEnumNameWithSchema_old}${arraySuffix}`
×
1635

1636
                // update column to use new type
1637
                upQueries.push(
×
1638
                    new Query(
1639
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
1640
                            newColumn.name
1641
                        }" TYPE ${upType}`,
1642
                    ),
1643
                )
1644
                downQueries.push(
×
1645
                    new Query(
1646
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
1647
                            newColumn.name
1648
                        }" TYPE ${downType}`,
1649
                    ),
1650
                )
1651

1652
                // restore column default or create new one
1653
                if (
×
1654
                    newColumn.default !== null &&
×
1655
                    newColumn.default !== undefined
1656
                ) {
1657
                    upQueries.push(
×
1658
                        new Query(
1659
                            `ALTER TABLE ${this.escapePath(
1660
                                table,
1661
                            )} ALTER COLUMN "${newColumn.name}" SET DEFAULT ${
1662
                                newColumn.default
1663
                            }`,
1664
                        ),
1665
                    )
1666
                    downQueries.push(
×
1667
                        new Query(
1668
                            `ALTER TABLE ${this.escapePath(
1669
                                table,
1670
                            )} ALTER COLUMN "${newColumn.name}" DROP DEFAULT`,
1671
                        ),
1672
                    )
1673
                }
1674

1675
                // remove old ENUM
1676
                upQueries.push(
×
1677
                    this.dropEnumTypeSql(
1678
                        table,
1679
                        oldColumn,
1680
                        oldEnumNameWithSchema_old,
1681
                    ),
1682
                )
1683
                downQueries.push(
×
1684
                    this.createEnumTypeSql(
1685
                        table,
1686
                        oldColumn,
1687
                        oldEnumNameWithSchema_old,
1688
                    ),
1689
                )
1690
            }
1691

1692
            if (oldColumn.isNullable !== newColumn.isNullable) {
×
1693
                if (newColumn.isNullable) {
×
1694
                    upQueries.push(
×
1695
                        new Query(
1696
                            `ALTER TABLE ${this.escapePath(
1697
                                table,
1698
                            )} ALTER COLUMN "${oldColumn.name}" DROP NOT NULL`,
1699
                        ),
1700
                    )
1701
                    downQueries.push(
×
1702
                        new Query(
1703
                            `ALTER TABLE ${this.escapePath(
1704
                                table,
1705
                            )} ALTER COLUMN "${oldColumn.name}" SET NOT NULL`,
1706
                        ),
1707
                    )
1708
                } else {
1709
                    upQueries.push(
×
1710
                        new Query(
1711
                            `ALTER TABLE ${this.escapePath(
1712
                                table,
1713
                            )} ALTER COLUMN "${oldColumn.name}" SET NOT NULL`,
1714
                        ),
1715
                    )
1716
                    downQueries.push(
×
1717
                        new Query(
1718
                            `ALTER TABLE ${this.escapePath(
1719
                                table,
1720
                            )} ALTER COLUMN "${oldColumn.name}" DROP NOT NULL`,
1721
                        ),
1722
                    )
1723
                }
1724
            }
1725

1726
            if (oldColumn.comment !== newColumn.comment) {
×
1727
                upQueries.push(
×
1728
                    new Query(
1729
                        `COMMENT ON COLUMN ${this.escapePath(table)}."${
1730
                            oldColumn.name
1731
                        }" IS ${this.escapeComment(newColumn.comment)}`,
1732
                    ),
1733
                )
1734
                downQueries.push(
×
1735
                    new Query(
1736
                        `COMMENT ON COLUMN ${this.escapePath(table)}."${
1737
                            newColumn.name
1738
                        }" IS ${this.escapeComment(oldColumn.comment)}`,
1739
                    ),
1740
                )
1741
            }
1742

1743
            if (newColumn.isPrimary !== oldColumn.isPrimary) {
×
1744
                const primaryColumns = clonedTable.primaryColumns
×
1745

1746
                // if primary column state changed, we must always drop existed constraint.
1747
                if (primaryColumns.length > 0) {
×
1748
                    const pkName = primaryColumns[0].primaryKeyConstraintName
×
1749
                        ? primaryColumns[0].primaryKeyConstraintName
1750
                        : this.connection.namingStrategy.primaryKeyName(
1751
                              clonedTable,
1752
                              primaryColumns.map((column) => column.name),
×
1753
                          )
1754

1755
                    const columnNames = primaryColumns
×
1756
                        .map((column) => `"${column.name}"`)
×
1757
                        .join(", ")
1758

1759
                    upQueries.push(
×
1760
                        new Query(
1761
                            `ALTER TABLE ${this.escapePath(
1762
                                table,
1763
                            )} DROP CONSTRAINT "${pkName}"`,
1764
                        ),
1765
                    )
1766
                    downQueries.push(
×
1767
                        new Query(
1768
                            `ALTER TABLE ${this.escapePath(
1769
                                table,
1770
                            )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1771
                        ),
1772
                    )
1773
                }
1774

1775
                if (newColumn.isPrimary === true) {
×
1776
                    primaryColumns.push(newColumn)
×
1777
                    // update column in table
1778
                    const column = clonedTable.columns.find(
×
1779
                        (column) => column.name === newColumn.name,
×
1780
                    )
1781
                    column!.isPrimary = true
×
1782
                    const pkName = primaryColumns[0].primaryKeyConstraintName
×
1783
                        ? primaryColumns[0].primaryKeyConstraintName
1784
                        : this.connection.namingStrategy.primaryKeyName(
1785
                              clonedTable,
1786
                              primaryColumns.map((column) => column.name),
×
1787
                          )
1788

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

1793
                    upQueries.push(
×
1794
                        new Query(
1795
                            `ALTER TABLE ${this.escapePath(
1796
                                table,
1797
                            )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1798
                        ),
1799
                    )
1800
                    downQueries.push(
×
1801
                        new Query(
1802
                            `ALTER TABLE ${this.escapePath(
1803
                                table,
1804
                            )} DROP CONSTRAINT "${pkName}"`,
1805
                        ),
1806
                    )
1807
                } else {
1808
                    const primaryColumn = primaryColumns.find(
×
1809
                        (c) => c.name === newColumn.name,
×
1810
                    )
1811
                    primaryColumns.splice(
×
1812
                        primaryColumns.indexOf(primaryColumn!),
1813
                        1,
1814
                    )
1815

1816
                    // update column in table
1817
                    const column = clonedTable.columns.find(
×
1818
                        (column) => column.name === newColumn.name,
×
1819
                    )
1820
                    column!.isPrimary = false
×
1821

1822
                    // if we have another primary keys, we must recreate constraint.
1823
                    if (primaryColumns.length > 0) {
×
1824
                        const pkName = primaryColumns[0]
×
1825
                            .primaryKeyConstraintName
1826
                            ? primaryColumns[0].primaryKeyConstraintName
1827
                            : this.connection.namingStrategy.primaryKeyName(
1828
                                  clonedTable,
1829
                                  primaryColumns.map((column) => column.name),
×
1830
                              )
1831

1832
                        const columnNames = primaryColumns
×
1833
                            .map((column) => `"${column.name}"`)
×
1834
                            .join(", ")
1835

1836
                        upQueries.push(
×
1837
                            new Query(
1838
                                `ALTER TABLE ${this.escapePath(
1839
                                    table,
1840
                                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
1841
                            ),
1842
                        )
1843
                        downQueries.push(
×
1844
                            new Query(
1845
                                `ALTER TABLE ${this.escapePath(
1846
                                    table,
1847
                                )} DROP CONSTRAINT "${pkName}"`,
1848
                            ),
1849
                        )
1850
                    }
1851
                }
1852
            }
1853

1854
            if (newColumn.isUnique !== oldColumn.isUnique) {
×
1855
                if (newColumn.isUnique === true) {
×
1856
                    const uniqueConstraint = new TableUnique({
×
1857
                        name: this.connection.namingStrategy.uniqueConstraintName(
1858
                            table,
1859
                            [newColumn.name],
1860
                        ),
1861
                        columnNames: [newColumn.name],
1862
                    })
1863
                    clonedTable.uniques.push(uniqueConstraint)
×
1864
                    upQueries.push(
×
1865
                        new Query(
1866
                            `ALTER TABLE ${this.escapePath(
1867
                                table,
1868
                            )} ADD CONSTRAINT "${
1869
                                uniqueConstraint.name
1870
                            }" UNIQUE ("${newColumn.name}")`,
1871
                        ),
1872
                    )
1873
                    downQueries.push(
×
1874
                        new Query(
1875
                            `ALTER TABLE ${this.escapePath(
1876
                                table,
1877
                            )} DROP CONSTRAINT "${uniqueConstraint.name}"`,
1878
                        ),
1879
                    )
1880
                } else {
1881
                    const uniqueConstraint = clonedTable.uniques.find(
×
1882
                        (unique) => {
1883
                            return (
×
1884
                                unique.columnNames.length === 1 &&
×
1885
                                !!unique.columnNames.find(
1886
                                    (columnName) =>
1887
                                        columnName === newColumn.name,
×
1888
                                )
1889
                            )
1890
                        },
1891
                    )
1892
                    clonedTable.uniques.splice(
×
1893
                        clonedTable.uniques.indexOf(uniqueConstraint!),
1894
                        1,
1895
                    )
1896
                    upQueries.push(
×
1897
                        new Query(
1898
                            `ALTER TABLE ${this.escapePath(
1899
                                table,
1900
                            )} DROP CONSTRAINT "${uniqueConstraint!.name}"`,
1901
                        ),
1902
                    )
1903
                    downQueries.push(
×
1904
                        new Query(
1905
                            `ALTER TABLE ${this.escapePath(
1906
                                table,
1907
                            )} ADD CONSTRAINT "${
1908
                                uniqueConstraint!.name
1909
                            }" UNIQUE ("${newColumn.name}")`,
1910
                        ),
1911
                    )
1912
                }
1913
            }
1914

1915
            if (oldColumn.isGenerated !== newColumn.isGenerated) {
×
1916
                // if old column was "generated", we should clear defaults
1917
                if (oldColumn.isGenerated) {
×
1918
                    if (oldColumn.generationStrategy === "uuid") {
×
1919
                        upQueries.push(
×
1920
                            new Query(
1921
                                `ALTER TABLE ${this.escapePath(
1922
                                    table,
1923
                                )} ALTER COLUMN "${
1924
                                    oldColumn.name
1925
                                }" DROP DEFAULT`,
1926
                            ),
1927
                        )
1928
                        downQueries.push(
×
1929
                            new Query(
1930
                                `ALTER TABLE ${this.escapePath(
1931
                                    table,
1932
                                )} ALTER COLUMN "${
1933
                                    oldColumn.name
1934
                                }" SET DEFAULT ${this.driver.uuidGenerator}`,
1935
                            ),
1936
                        )
1937
                    } else if (oldColumn.generationStrategy === "increment") {
×
1938
                        upQueries.push(
×
1939
                            new Query(
1940
                                `ALTER TABLE ${this.escapePath(
1941
                                    table,
1942
                                )} ALTER COLUMN "${
1943
                                    newColumn.name
1944
                                }" DROP DEFAULT`,
1945
                            ),
1946
                        )
1947
                        downQueries.push(
×
1948
                            new Query(
1949
                                `ALTER TABLE ${this.escapePath(
1950
                                    table,
1951
                                )} ALTER COLUMN "${
1952
                                    newColumn.name
1953
                                }" SET DEFAULT nextval('${this.escapePath(
1954
                                    this.buildSequencePath(table, newColumn),
1955
                                )}')`,
1956
                            ),
1957
                        )
1958

1959
                        upQueries.push(
×
1960
                            new Query(
1961
                                `DROP SEQUENCE ${this.escapePath(
1962
                                    this.buildSequencePath(table, newColumn),
1963
                                )}`,
1964
                            ),
1965
                        )
1966
                        downQueries.push(
×
1967
                            new Query(
1968
                                `CREATE SEQUENCE IF NOT EXISTS ${this.escapePath(
1969
                                    this.buildSequencePath(table, newColumn),
1970
                                )} OWNED BY ${this.escapePath(table)}."${
1971
                                    newColumn.name
1972
                                }"`,
1973
                            ),
1974
                        )
1975
                    }
1976
                }
1977

1978
                if (newColumn.generationStrategy === "uuid") {
×
1979
                    if (newColumn.isGenerated === true) {
×
1980
                        upQueries.push(
×
1981
                            new Query(
1982
                                `ALTER TABLE ${this.escapePath(
1983
                                    table,
1984
                                )} ALTER COLUMN "${
1985
                                    newColumn.name
1986
                                }" SET DEFAULT ${this.driver.uuidGenerator}`,
1987
                            ),
1988
                        )
1989
                        downQueries.push(
×
1990
                            new Query(
1991
                                `ALTER TABLE ${this.escapePath(
1992
                                    table,
1993
                                )} ALTER COLUMN "${
1994
                                    newColumn.name
1995
                                }" DROP DEFAULT`,
1996
                            ),
1997
                        )
1998
                    } else {
1999
                        upQueries.push(
×
2000
                            new Query(
2001
                                `ALTER TABLE ${this.escapePath(
2002
                                    table,
2003
                                )} ALTER COLUMN "${
2004
                                    newColumn.name
2005
                                }" DROP DEFAULT`,
2006
                            ),
2007
                        )
2008
                        downQueries.push(
×
2009
                            new Query(
2010
                                `ALTER TABLE ${this.escapePath(
2011
                                    table,
2012
                                )} ALTER COLUMN "${
2013
                                    newColumn.name
2014
                                }" SET DEFAULT ${this.driver.uuidGenerator}`,
2015
                            ),
2016
                        )
2017
                    }
2018
                } else if (newColumn.generationStrategy === "increment") {
×
2019
                    if (newColumn.isGenerated === true) {
×
2020
                        upQueries.push(
×
2021
                            new Query(
2022
                                `CREATE SEQUENCE IF NOT EXISTS ${this.escapePath(
2023
                                    this.buildSequencePath(table, newColumn),
2024
                                )} OWNED BY ${this.escapePath(table)}."${
2025
                                    newColumn.name
2026
                                }"`,
2027
                            ),
2028
                        )
2029
                        downQueries.push(
×
2030
                            new Query(
2031
                                `DROP SEQUENCE ${this.escapePath(
2032
                                    this.buildSequencePath(table, newColumn),
2033
                                )}`,
2034
                            ),
2035
                        )
2036

2037
                        upQueries.push(
×
2038
                            new Query(
2039
                                `ALTER TABLE ${this.escapePath(
2040
                                    table,
2041
                                )} ALTER COLUMN "${
2042
                                    newColumn.name
2043
                                }" SET DEFAULT nextval('${this.escapePath(
2044
                                    this.buildSequencePath(table, newColumn),
2045
                                )}')`,
2046
                            ),
2047
                        )
2048
                        downQueries.push(
×
2049
                            new Query(
2050
                                `ALTER TABLE ${this.escapePath(
2051
                                    table,
2052
                                )} ALTER COLUMN "${
2053
                                    newColumn.name
2054
                                }" DROP DEFAULT`,
2055
                            ),
2056
                        )
2057
                    } else {
2058
                        upQueries.push(
×
2059
                            new Query(
2060
                                `ALTER TABLE ${this.escapePath(
2061
                                    table,
2062
                                )} ALTER COLUMN "${
2063
                                    newColumn.name
2064
                                }" DROP DEFAULT`,
2065
                            ),
2066
                        )
2067
                        downQueries.push(
×
2068
                            new Query(
2069
                                `ALTER TABLE ${this.escapePath(
2070
                                    table,
2071
                                )} ALTER COLUMN "${
2072
                                    newColumn.name
2073
                                }" SET DEFAULT nextval('${this.escapePath(
2074
                                    this.buildSequencePath(table, newColumn),
2075
                                )}')`,
2076
                            ),
2077
                        )
2078

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

2099
            // the default might have changed when the enum changed
2100
            if (
×
2101
                newColumn.default !== oldColumn.default &&
×
2102
                !defaultValueChanged
2103
            ) {
2104
                if (
×
2105
                    newColumn.default !== null &&
×
2106
                    newColumn.default !== undefined
2107
                ) {
2108
                    upQueries.push(
×
2109
                        new Query(
2110
                            `ALTER TABLE ${this.escapePath(
2111
                                table,
2112
                            )} ALTER COLUMN "${newColumn.name}" SET DEFAULT ${
2113
                                newColumn.default
2114
                            }`,
2115
                        ),
2116
                    )
2117

2118
                    if (
×
2119
                        oldColumn.default !== null &&
×
2120
                        oldColumn.default !== undefined
2121
                    ) {
2122
                        downQueries.push(
×
2123
                            new Query(
2124
                                `ALTER TABLE ${this.escapePath(
2125
                                    table,
2126
                                )} ALTER COLUMN "${
2127
                                    newColumn.name
2128
                                }" SET DEFAULT ${oldColumn.default}`,
2129
                            ),
2130
                        )
2131
                    } else {
2132
                        downQueries.push(
×
2133
                            new Query(
2134
                                `ALTER TABLE ${this.escapePath(
2135
                                    table,
2136
                                )} ALTER COLUMN "${
2137
                                    newColumn.name
2138
                                }" DROP DEFAULT`,
2139
                            ),
2140
                        )
2141
                    }
2142
                } else if (
×
2143
                    oldColumn.default !== null &&
×
2144
                    oldColumn.default !== undefined
2145
                ) {
2146
                    upQueries.push(
×
2147
                        new Query(
2148
                            `ALTER TABLE ${this.escapePath(
2149
                                table,
2150
                            )} ALTER COLUMN "${newColumn.name}" DROP DEFAULT`,
2151
                        ),
2152
                    )
2153
                    downQueries.push(
×
2154
                        new Query(
2155
                            `ALTER TABLE ${this.escapePath(
2156
                                table,
2157
                            )} ALTER COLUMN "${newColumn.name}" SET DEFAULT ${
2158
                                oldColumn.default
2159
                            }`,
2160
                        ),
2161
                    )
2162
                }
2163
            }
2164

2165
            if (
×
2166
                (newColumn.spatialFeatureType || "").toLowerCase() !==
×
2167
                    (oldColumn.spatialFeatureType || "").toLowerCase() ||
×
2168
                newColumn.srid !== oldColumn.srid
2169
            ) {
2170
                upQueries.push(
×
2171
                    new Query(
2172
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
2173
                            newColumn.name
2174
                        }" TYPE ${this.driver.createFullType(newColumn)}`,
2175
                    ),
2176
                )
2177
                downQueries.push(
×
2178
                    new Query(
2179
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
2180
                            newColumn.name
2181
                        }" TYPE ${this.driver.createFullType(oldColumn)}`,
2182
                    ),
2183
                )
2184
            }
2185

2186
            if (newColumn.generatedType !== oldColumn.generatedType) {
×
2187
                // Convert generated column data to normal column
2188
                if (
×
2189
                    !newColumn.generatedType ||
×
2190
                    newColumn.generatedType === "VIRTUAL"
2191
                ) {
2192
                    // We can copy the generated data to the new column
2193
                    const tableNameWithSchema = (
×
2194
                        await this.getTableNameWithSchema(table.name)
2195
                    ).split(".")
2196
                    const tableName = tableNameWithSchema[1]
×
2197
                    const schema = tableNameWithSchema[0]
×
2198

2199
                    upQueries.push(
×
2200
                        new Query(
2201
                            `ALTER TABLE ${this.escapePath(
2202
                                table,
2203
                            )} RENAME COLUMN "${oldColumn.name}" TO "TEMP_OLD_${
2204
                                oldColumn.name
2205
                            }"`,
2206
                        ),
2207
                    )
2208
                    upQueries.push(
×
2209
                        new Query(
2210
                            `ALTER TABLE ${this.escapePath(
2211
                                table,
2212
                            )} ADD ${this.buildCreateColumnSql(
2213
                                table,
2214
                                newColumn,
2215
                            )}`,
2216
                        ),
2217
                    )
2218
                    upQueries.push(
×
2219
                        new Query(
2220
                            `UPDATE ${this.escapePath(table)} SET "${
2221
                                newColumn.name
2222
                            }" = "TEMP_OLD_${oldColumn.name}"`,
2223
                        ),
2224
                    )
2225
                    upQueries.push(
×
2226
                        new Query(
2227
                            `ALTER TABLE ${this.escapePath(
2228
                                table,
2229
                            )} DROP COLUMN "TEMP_OLD_${oldColumn.name}"`,
2230
                        ),
2231
                    )
2232
                    upQueries.push(
×
2233
                        this.deleteTypeormMetadataSql({
2234
                            database: this.driver.database,
2235
                            schema,
2236
                            table: tableName,
2237
                            type: MetadataTableType.GENERATED_COLUMN,
2238
                            name: oldColumn.name,
2239
                        }),
2240
                    )
2241
                    // However, we can't copy it back on downgrade. It needs to regenerate.
2242
                    downQueries.push(
×
2243
                        this.insertTypeormMetadataSql({
2244
                            database: this.driver.database,
2245
                            schema,
2246
                            table: tableName,
2247
                            type: MetadataTableType.GENERATED_COLUMN,
2248
                            name: oldColumn.name,
2249
                            value: oldColumn.asExpression,
2250
                        }),
2251
                    )
2252
                    downQueries.push(
×
2253
                        new Query(
2254
                            `ALTER TABLE ${this.escapePath(
2255
                                table,
2256
                            )} ADD ${this.buildCreateColumnSql(
2257
                                table,
2258
                                oldColumn,
2259
                            )}`,
2260
                        ),
2261
                    )
2262
                    downQueries.push(
×
2263
                        new Query(
2264
                            `ALTER TABLE ${this.escapePath(
2265
                                table,
2266
                            )} DROP COLUMN "${newColumn.name}"`,
2267
                        ),
2268
                    )
2269
                    // downQueries.push(
2270
                    //     this.deleteTypeormMetadataSql({
2271
                    //         database: this.driver.database,
2272
                    //         schema,
2273
                    //         table: tableName,
2274
                    //         type: MetadataTableType.GENERATED_COLUMN,
2275
                    //         name: newColumn.name,
2276
                    //     }),
2277
                    // )
2278
                }
2279
            }
2280
        }
2281

2282
        await this.executeQueries(upQueries, downQueries)
×
2283
        this.replaceCachedTable(table, clonedTable)
×
2284
    }
2285

2286
    /**
2287
     * Changes a column in the table.
2288
     */
2289
    async changeColumns(
2290
        tableOrName: Table | string,
2291
        changedColumns: { newColumn: TableColumn; oldColumn: TableColumn }[],
2292
    ): Promise<void> {
2293
        for (const { oldColumn, newColumn } of changedColumns) {
×
2294
            await this.changeColumn(tableOrName, oldColumn, newColumn)
×
2295
        }
2296
    }
2297

2298
    /**
2299
     * Drops column in the table.
2300
     */
2301
    async dropColumn(
2302
        tableOrName: Table | string,
2303
        columnOrName: TableColumn | string,
2304
    ): Promise<void> {
2305
        const table = InstanceChecker.isTable(tableOrName)
×
2306
            ? tableOrName
2307
            : await this.getCachedTable(tableOrName)
2308
        const column = InstanceChecker.isTableColumn(columnOrName)
×
2309
            ? columnOrName
2310
            : table.findColumnByName(columnOrName)
2311
        if (!column)
×
2312
            throw new TypeORMError(
×
2313
                `Column "${columnOrName}" was not found in table "${table.name}"`,
2314
            )
2315

2316
        const clonedTable = table.clone()
×
2317
        const upQueries: Query[] = []
×
2318
        const downQueries: Query[] = []
×
2319

2320
        // drop primary key constraint
2321
        if (column.isPrimary) {
×
2322
            const pkName = column.primaryKeyConstraintName
×
2323
                ? column.primaryKeyConstraintName
2324
                : this.connection.namingStrategy.primaryKeyName(
2325
                      clonedTable,
2326
                      clonedTable.primaryColumns.map((column) => column.name),
×
2327
                  )
2328

2329
            const columnNames = clonedTable.primaryColumns
×
2330
                .map((primaryColumn) => `"${primaryColumn.name}"`)
×
2331
                .join(", ")
2332

2333
            upQueries.push(
×
2334
                new Query(
2335
                    `ALTER TABLE ${this.escapePath(
2336
                        clonedTable,
2337
                    )} DROP CONSTRAINT "${pkName}"`,
2338
                ),
2339
            )
2340
            downQueries.push(
×
2341
                new Query(
2342
                    `ALTER TABLE ${this.escapePath(
2343
                        clonedTable,
2344
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
2345
                ),
2346
            )
2347

2348
            // update column in table
2349
            const tableColumn = clonedTable.findColumnByName(column.name)
×
2350
            tableColumn!.isPrimary = false
×
2351

2352
            // if primary key have multiple columns, we must recreate it without dropped column
2353
            if (clonedTable.primaryColumns.length > 0) {
×
2354
                const pkName = clonedTable.primaryColumns[0]
×
2355
                    .primaryKeyConstraintName
2356
                    ? clonedTable.primaryColumns[0].primaryKeyConstraintName
2357
                    : this.connection.namingStrategy.primaryKeyName(
2358
                          clonedTable,
2359
                          clonedTable.primaryColumns.map(
2360
                              (column) => column.name,
×
2361
                          ),
2362
                      )
2363

2364
                const columnNames = clonedTable.primaryColumns
×
2365
                    .map((primaryColumn) => `"${primaryColumn.name}"`)
×
2366
                    .join(", ")
2367

2368
                upQueries.push(
×
2369
                    new Query(
2370
                        `ALTER TABLE ${this.escapePath(
2371
                            clonedTable,
2372
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
2373
                    ),
2374
                )
2375
                downQueries.push(
×
2376
                    new Query(
2377
                        `ALTER TABLE ${this.escapePath(
2378
                            clonedTable,
2379
                        )} DROP CONSTRAINT "${pkName}"`,
2380
                    ),
2381
                )
2382
            }
2383
        }
2384

2385
        // drop column index
2386
        const columnIndex = clonedTable.indices.find(
×
2387
            (index) =>
2388
                index.columnNames.length === 1 &&
×
2389
                index.columnNames[0] === column.name,
2390
        )
2391
        if (columnIndex) {
×
2392
            clonedTable.indices.splice(
×
2393
                clonedTable.indices.indexOf(columnIndex),
2394
                1,
2395
            )
2396
            upQueries.push(this.dropIndexSql(table, columnIndex))
×
2397
            downQueries.push(this.createIndexSql(table, columnIndex))
×
2398
        }
2399

2400
        // drop column check
2401
        const columnCheck = clonedTable.checks.find(
×
2402
            (check) =>
2403
                !!check.columnNames &&
×
2404
                check.columnNames.length === 1 &&
2405
                check.columnNames[0] === column.name,
2406
        )
2407
        if (columnCheck) {
×
2408
            clonedTable.checks.splice(
×
2409
                clonedTable.checks.indexOf(columnCheck),
2410
                1,
2411
            )
2412
            upQueries.push(this.dropCheckConstraintSql(table, columnCheck))
×
2413
            downQueries.push(this.createCheckConstraintSql(table, columnCheck))
×
2414
        }
2415

2416
        // drop column unique
2417
        const columnUnique = clonedTable.uniques.find(
×
2418
            (unique) =>
2419
                unique.columnNames.length === 1 &&
×
2420
                unique.columnNames[0] === column.name,
2421
        )
2422
        if (columnUnique) {
×
2423
            clonedTable.uniques.splice(
×
2424
                clonedTable.uniques.indexOf(columnUnique),
2425
                1,
2426
            )
2427
            upQueries.push(this.dropUniqueConstraintSql(table, columnUnique))
×
2428
            downQueries.push(
×
2429
                this.createUniqueConstraintSql(table, columnUnique),
2430
            )
2431
        }
2432

2433
        upQueries.push(
×
2434
            new Query(
2435
                `ALTER TABLE ${this.escapePath(table)} DROP COLUMN "${
2436
                    column.name
2437
                }"`,
2438
            ),
2439
        )
2440
        downQueries.push(
×
2441
            new Query(
2442
                `ALTER TABLE ${this.escapePath(
2443
                    table,
2444
                )} ADD ${this.buildCreateColumnSql(table, column)}`,
2445
            ),
2446
        )
2447

2448
        // drop enum type
2449
        if (column.type === "enum" || column.type === "simple-enum") {
×
2450
            const hasEnum = await this.hasEnumType(table, column)
×
2451
            if (hasEnum) {
×
2452
                const enumType = await this.getUserDefinedTypeName(
×
2453
                    table,
2454
                    column,
2455
                )
2456
                const escapedEnumName = `"${enumType.schema}"."${enumType.name}"`
×
2457
                upQueries.push(
×
2458
                    this.dropEnumTypeSql(table, column, escapedEnumName),
2459
                )
2460
                downQueries.push(
×
2461
                    this.createEnumTypeSql(table, column, escapedEnumName),
2462
                )
2463
            }
2464
        }
2465

2466
        if (column.generatedType === "STORED") {
×
2467
            const tableNameWithSchema = (
×
2468
                await this.getTableNameWithSchema(table.name)
2469
            ).split(".")
2470
            const tableName = tableNameWithSchema[1]
×
2471
            const schema = tableNameWithSchema[0]
×
2472
            const deleteQuery = this.deleteTypeormMetadataSql({
×
2473
                database: this.driver.database,
2474
                schema,
2475
                table: tableName,
2476
                type: MetadataTableType.GENERATED_COLUMN,
2477
                name: column.name,
2478
            })
2479
            const insertQuery = this.insertTypeormMetadataSql({
×
2480
                database: this.driver.database,
2481
                schema,
2482
                table: tableName,
2483
                type: MetadataTableType.GENERATED_COLUMN,
2484
                name: column.name,
2485
                value: column.asExpression,
2486
            })
2487

2488
            upQueries.push(deleteQuery)
×
2489
            downQueries.push(insertQuery)
×
2490
        }
2491

2492
        await this.executeQueries(upQueries, downQueries)
×
2493

2494
        clonedTable.removeColumn(column)
×
2495
        this.replaceCachedTable(table, clonedTable)
×
2496
    }
2497

2498
    /**
2499
     * Drops the columns in the table.
2500
     */
2501
    async dropColumns(
2502
        tableOrName: Table | string,
2503
        columns: TableColumn[] | string[],
2504
    ): Promise<void> {
2505
        for (const column of columns) {
×
2506
            await this.dropColumn(tableOrName, column)
×
2507
        }
2508
    }
2509

2510
    /**
2511
     * Creates a new primary key.
2512
     */
2513
    async createPrimaryKey(
2514
        tableOrName: Table | string,
2515
        columnNames: string[],
2516
        constraintName?: string,
2517
    ): Promise<void> {
2518
        const table = InstanceChecker.isTable(tableOrName)
×
2519
            ? tableOrName
2520
            : await this.getCachedTable(tableOrName)
2521
        const clonedTable = table.clone()
×
2522

2523
        const up = this.createPrimaryKeySql(table, columnNames, constraintName)
×
2524

2525
        // mark columns as primary, because dropPrimaryKeySql build constraint name from table primary column names.
2526
        clonedTable.columns.forEach((column) => {
×
2527
            if (columnNames.find((columnName) => columnName === column.name))
×
2528
                column.isPrimary = true
×
2529
        })
2530
        const down = this.dropPrimaryKeySql(clonedTable)
×
2531

2532
        await this.executeQueries(up, down)
×
2533
        this.replaceCachedTable(table, clonedTable)
×
2534
    }
2535

2536
    /**
2537
     * Updates composite primary keys.
2538
     */
2539
    async updatePrimaryKeys(
2540
        tableOrName: Table | string,
2541
        columns: TableColumn[],
2542
    ): Promise<void> {
2543
        const table = InstanceChecker.isTable(tableOrName)
×
2544
            ? tableOrName
2545
            : await this.getCachedTable(tableOrName)
2546
        const clonedTable = table.clone()
×
2547
        const columnNames = columns.map((column) => column.name)
×
2548
        const upQueries: Query[] = []
×
2549
        const downQueries: Query[] = []
×
2550

2551
        // if table already have primary columns, we must drop them.
2552
        const primaryColumns = clonedTable.primaryColumns
×
2553
        if (primaryColumns.length > 0) {
×
2554
            const pkName = primaryColumns[0].primaryKeyConstraintName
×
2555
                ? primaryColumns[0].primaryKeyConstraintName
2556
                : this.connection.namingStrategy.primaryKeyName(
2557
                      clonedTable,
2558
                      primaryColumns.map((column) => column.name),
×
2559
                  )
2560

2561
            const columnNamesString = primaryColumns
×
2562
                .map((column) => `"${column.name}"`)
×
2563
                .join(", ")
2564

2565
            upQueries.push(
×
2566
                new Query(
2567
                    `ALTER TABLE ${this.escapePath(
2568
                        table,
2569
                    )} DROP CONSTRAINT "${pkName}"`,
2570
                ),
2571
            )
2572
            downQueries.push(
×
2573
                new Query(
2574
                    `ALTER TABLE ${this.escapePath(
2575
                        table,
2576
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
2577
                ),
2578
            )
2579
        }
2580

2581
        // update columns in table.
2582
        clonedTable.columns
×
2583
            .filter((column) => columnNames.indexOf(column.name) !== -1)
×
2584
            .forEach((column) => (column.isPrimary = true))
×
2585

2586
        const pkName = primaryColumns[0]?.primaryKeyConstraintName
×
2587
            ? primaryColumns[0].primaryKeyConstraintName
2588
            : this.connection.namingStrategy.primaryKeyName(
2589
                  clonedTable,
2590
                  columnNames,
2591
              )
2592

2593
        const columnNamesString = columnNames
×
2594
            .map((columnName) => `"${columnName}"`)
×
2595
            .join(", ")
2596

2597
        upQueries.push(
×
2598
            new Query(
2599
                `ALTER TABLE ${this.escapePath(
2600
                    table,
2601
                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
2602
            ),
2603
        )
2604
        downQueries.push(
×
2605
            new Query(
2606
                `ALTER TABLE ${this.escapePath(
2607
                    table,
2608
                )} DROP CONSTRAINT "${pkName}"`,
2609
            ),
2610
        )
2611

2612
        await this.executeQueries(upQueries, downQueries)
×
2613
        this.replaceCachedTable(table, clonedTable)
×
2614
    }
2615

2616
    /**
2617
     * Drops a primary key.
2618
     */
2619
    async dropPrimaryKey(
2620
        tableOrName: Table | string,
2621
        constraintName?: string,
2622
    ): Promise<void> {
2623
        const table = InstanceChecker.isTable(tableOrName)
×
2624
            ? tableOrName
2625
            : await this.getCachedTable(tableOrName)
2626
        const up = this.dropPrimaryKeySql(table)
×
2627
        const down = this.createPrimaryKeySql(
×
2628
            table,
2629
            table.primaryColumns.map((column) => column.name),
×
2630
            constraintName,
2631
        )
2632
        await this.executeQueries(up, down)
×
2633
        table.primaryColumns.forEach((column) => {
×
2634
            column.isPrimary = false
×
2635
        })
2636
    }
2637

2638
    /**
2639
     * Creates new unique constraint.
2640
     */
2641
    async createUniqueConstraint(
2642
        tableOrName: Table | string,
2643
        uniqueConstraint: TableUnique,
2644
    ): Promise<void> {
2645
        const table = InstanceChecker.isTable(tableOrName)
×
2646
            ? tableOrName
2647
            : await this.getCachedTable(tableOrName)
2648

2649
        // new unique constraint may be passed without name. In this case we generate unique name manually.
2650
        if (!uniqueConstraint.name)
×
2651
            uniqueConstraint.name =
×
2652
                this.connection.namingStrategy.uniqueConstraintName(
2653
                    table,
2654
                    uniqueConstraint.columnNames,
2655
                )
2656

2657
        const up = this.createUniqueConstraintSql(table, uniqueConstraint)
×
2658
        const down = this.dropUniqueConstraintSql(table, uniqueConstraint)
×
2659
        await this.executeQueries(up, down)
×
2660
        table.addUniqueConstraint(uniqueConstraint)
×
2661
    }
2662

2663
    /**
2664
     * Creates new unique constraints.
2665
     */
2666
    async createUniqueConstraints(
2667
        tableOrName: Table | string,
2668
        uniqueConstraints: TableUnique[],
2669
    ): Promise<void> {
2670
        for (const uniqueConstraint of uniqueConstraints) {
×
2671
            await this.createUniqueConstraint(tableOrName, uniqueConstraint)
×
2672
        }
2673
    }
2674

2675
    /**
2676
     * Drops unique constraint.
2677
     */
2678
    async dropUniqueConstraint(
2679
        tableOrName: Table | string,
2680
        uniqueOrName: TableUnique | string,
2681
    ): Promise<void> {
2682
        const table = InstanceChecker.isTable(tableOrName)
×
2683
            ? tableOrName
2684
            : await this.getCachedTable(tableOrName)
2685
        const uniqueConstraint = InstanceChecker.isTableUnique(uniqueOrName)
×
2686
            ? uniqueOrName
2687
            : table.uniques.find((u) => u.name === uniqueOrName)
×
2688
        if (!uniqueConstraint)
×
2689
            throw new TypeORMError(
×
2690
                `Supplied unique constraint was not found in table ${table.name}`,
2691
            )
2692

2693
        const up = this.dropUniqueConstraintSql(table, uniqueConstraint)
×
2694
        const down = this.createUniqueConstraintSql(table, uniqueConstraint)
×
2695
        await this.executeQueries(up, down)
×
2696
        table.removeUniqueConstraint(uniqueConstraint)
×
2697
    }
2698

2699
    /**
2700
     * Drops unique constraints.
2701
     */
2702
    async dropUniqueConstraints(
2703
        tableOrName: Table | string,
2704
        uniqueConstraints: TableUnique[],
2705
    ): Promise<void> {
2706
        for (const uniqueConstraint of uniqueConstraints) {
×
2707
            await this.dropUniqueConstraint(tableOrName, uniqueConstraint)
×
2708
        }
2709
    }
2710

2711
    /**
2712
     * Creates new check constraint.
2713
     */
2714
    async createCheckConstraint(
2715
        tableOrName: Table | string,
2716
        checkConstraint: TableCheck,
2717
    ): Promise<void> {
2718
        const table = InstanceChecker.isTable(tableOrName)
×
2719
            ? tableOrName
2720
            : await this.getCachedTable(tableOrName)
2721

2722
        // new unique constraint may be passed without name. In this case we generate unique name manually.
2723
        if (!checkConstraint.name)
×
2724
            checkConstraint.name =
×
2725
                this.connection.namingStrategy.checkConstraintName(
2726
                    table,
2727
                    checkConstraint.expression!,
2728
                )
2729

2730
        const up = this.createCheckConstraintSql(table, checkConstraint)
×
2731
        const down = this.dropCheckConstraintSql(table, checkConstraint)
×
2732
        await this.executeQueries(up, down)
×
2733
        table.addCheckConstraint(checkConstraint)
×
2734
    }
2735

2736
    /**
2737
     * Creates new check constraints.
2738
     */
2739
    async createCheckConstraints(
2740
        tableOrName: Table | string,
2741
        checkConstraints: TableCheck[],
2742
    ): Promise<void> {
2743
        const promises = checkConstraints.map((checkConstraint) =>
×
2744
            this.createCheckConstraint(tableOrName, checkConstraint),
×
2745
        )
2746
        await Promise.all(promises)
×
2747
    }
2748

2749
    /**
2750
     * Drops check constraint.
2751
     */
2752
    async dropCheckConstraint(
2753
        tableOrName: Table | string,
2754
        checkOrName: TableCheck | string,
2755
    ): Promise<void> {
2756
        const table = InstanceChecker.isTable(tableOrName)
×
2757
            ? tableOrName
2758
            : await this.getCachedTable(tableOrName)
2759
        const checkConstraint = InstanceChecker.isTableCheck(checkOrName)
×
2760
            ? checkOrName
2761
            : table.checks.find((c) => c.name === checkOrName)
×
2762
        if (!checkConstraint)
×
2763
            throw new TypeORMError(
×
2764
                `Supplied check constraint was not found in table ${table.name}`,
2765
            )
2766

2767
        const up = this.dropCheckConstraintSql(table, checkConstraint)
×
2768
        const down = this.createCheckConstraintSql(table, checkConstraint)
×
2769
        await this.executeQueries(up, down)
×
2770
        table.removeCheckConstraint(checkConstraint)
×
2771
    }
2772

2773
    /**
2774
     * Drops check constraints.
2775
     */
2776
    async dropCheckConstraints(
2777
        tableOrName: Table | string,
2778
        checkConstraints: TableCheck[],
2779
    ): Promise<void> {
2780
        const promises = checkConstraints.map((checkConstraint) =>
×
2781
            this.dropCheckConstraint(tableOrName, checkConstraint),
×
2782
        )
2783
        await Promise.all(promises)
×
2784
    }
2785

2786
    /**
2787
     * Creates new exclusion constraint.
2788
     */
2789
    async createExclusionConstraint(
2790
        tableOrName: Table | string,
2791
        exclusionConstraint: TableExclusion,
2792
    ): Promise<void> {
2793
        const table = InstanceChecker.isTable(tableOrName)
×
2794
            ? tableOrName
2795
            : await this.getCachedTable(tableOrName)
2796

2797
        // new unique constraint may be passed without name. In this case we generate unique name manually.
2798
        if (!exclusionConstraint.name)
×
2799
            exclusionConstraint.name =
×
2800
                this.connection.namingStrategy.exclusionConstraintName(
2801
                    table,
2802
                    exclusionConstraint.expression!,
2803
                )
2804

2805
        const up = this.createExclusionConstraintSql(table, exclusionConstraint)
×
2806
        const down = this.dropExclusionConstraintSql(table, exclusionConstraint)
×
2807
        await this.executeQueries(up, down)
×
2808
        table.addExclusionConstraint(exclusionConstraint)
×
2809
    }
2810

2811
    /**
2812
     * Creates new exclusion constraints.
2813
     */
2814
    async createExclusionConstraints(
2815
        tableOrName: Table | string,
2816
        exclusionConstraints: TableExclusion[],
2817
    ): Promise<void> {
2818
        const promises = exclusionConstraints.map((exclusionConstraint) =>
×
2819
            this.createExclusionConstraint(tableOrName, exclusionConstraint),
×
2820
        )
2821
        await Promise.all(promises)
×
2822
    }
2823

2824
    /**
2825
     * Drops exclusion constraint.
2826
     */
2827
    async dropExclusionConstraint(
2828
        tableOrName: Table | string,
2829
        exclusionOrName: TableExclusion | string,
2830
    ): Promise<void> {
2831
        const table = InstanceChecker.isTable(tableOrName)
×
2832
            ? tableOrName
2833
            : await this.getCachedTable(tableOrName)
2834
        const exclusionConstraint = InstanceChecker.isTableExclusion(
×
2835
            exclusionOrName,
2836
        )
2837
            ? exclusionOrName
2838
            : table.exclusions.find((c) => c.name === exclusionOrName)
×
2839
        if (!exclusionConstraint)
×
2840
            throw new TypeORMError(
×
2841
                `Supplied exclusion constraint was not found in table ${table.name}`,
2842
            )
2843

2844
        const up = this.dropExclusionConstraintSql(table, exclusionConstraint)
×
2845
        const down = this.createExclusionConstraintSql(
×
2846
            table,
2847
            exclusionConstraint,
2848
        )
2849
        await this.executeQueries(up, down)
×
2850
        table.removeExclusionConstraint(exclusionConstraint)
×
2851
    }
2852

2853
    /**
2854
     * Drops exclusion constraints.
2855
     */
2856
    async dropExclusionConstraints(
2857
        tableOrName: Table | string,
2858
        exclusionConstraints: TableExclusion[],
2859
    ): Promise<void> {
2860
        const promises = exclusionConstraints.map((exclusionConstraint) =>
×
2861
            this.dropExclusionConstraint(tableOrName, exclusionConstraint),
×
2862
        )
2863
        await Promise.all(promises)
×
2864
    }
2865

2866
    /**
2867
     * Creates a new foreign key.
2868
     */
2869
    async createForeignKey(
2870
        tableOrName: Table | string,
2871
        foreignKey: TableForeignKey,
2872
    ): Promise<void> {
2873
        const table = InstanceChecker.isTable(tableOrName)
×
2874
            ? tableOrName
2875
            : await this.getCachedTable(tableOrName)
2876

2877
        // new FK may be passed without name. In this case we generate FK name manually.
2878
        if (!foreignKey.name)
×
2879
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
×
2880
                table,
2881
                foreignKey.columnNames,
2882
                this.getTablePath(foreignKey),
2883
                foreignKey.referencedColumnNames,
2884
            )
2885

2886
        const up = this.createForeignKeySql(table, foreignKey)
×
2887
        const down = this.dropForeignKeySql(table, foreignKey)
×
2888
        await this.executeQueries(up, down)
×
2889
        table.addForeignKey(foreignKey)
×
2890
    }
2891

2892
    /**
2893
     * Creates a new foreign keys.
2894
     */
2895
    async createForeignKeys(
2896
        tableOrName: Table | string,
2897
        foreignKeys: TableForeignKey[],
2898
    ): Promise<void> {
2899
        for (const foreignKey of foreignKeys) {
×
2900
            await this.createForeignKey(tableOrName, foreignKey)
×
2901
        }
2902
    }
2903

2904
    /**
2905
     * Drops a foreign key from the table.
2906
     */
2907
    async dropForeignKey(
2908
        tableOrName: Table | string,
2909
        foreignKeyOrName: TableForeignKey | string,
2910
    ): Promise<void> {
2911
        const table = InstanceChecker.isTable(tableOrName)
×
2912
            ? tableOrName
2913
            : await this.getCachedTable(tableOrName)
2914
        const foreignKey = InstanceChecker.isTableForeignKey(foreignKeyOrName)
×
2915
            ? foreignKeyOrName
2916
            : table.foreignKeys.find((fk) => fk.name === foreignKeyOrName)
×
2917
        if (!foreignKey)
×
2918
            throw new TypeORMError(
×
2919
                `Supplied foreign key was not found in table ${table.name}`,
2920
            )
2921

2922
        if (!foreignKey.name) {
×
2923
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
×
2924
                table,
2925
                foreignKey.columnNames,
2926
                this.getTablePath(foreignKey),
2927
                foreignKey.referencedColumnNames,
2928
            )
2929
        }
2930

2931
        const up = this.dropForeignKeySql(table, foreignKey)
×
2932
        const down = this.createForeignKeySql(table, foreignKey)
×
2933
        await this.executeQueries(up, down)
×
2934
        table.removeForeignKey(foreignKey)
×
2935
    }
2936

2937
    /**
2938
     * Drops a foreign keys from the table.
2939
     */
2940
    async dropForeignKeys(
2941
        tableOrName: Table | string,
2942
        foreignKeys: TableForeignKey[],
2943
    ): Promise<void> {
2944
        for (const foreignKey of foreignKeys) {
×
2945
            await this.dropForeignKey(tableOrName, foreignKey)
×
2946
        }
2947
    }
2948

2949
    /**
2950
     * Creates a new index.
2951
     */
2952
    async createIndex(
2953
        tableOrName: Table | string,
2954
        index: TableIndex,
2955
    ): Promise<void> {
2956
        const table = InstanceChecker.isTable(tableOrName)
×
2957
            ? tableOrName
2958
            : await this.getCachedTable(tableOrName)
2959

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

2963
        const up = this.createIndexSql(table, index)
×
2964
        const down = this.dropIndexSql(table, index)
×
2965
        await this.executeQueries(up, down)
×
2966
        table.addIndex(index)
×
2967
    }
2968

2969
    /**
2970
     * Create a new view index.
2971
     */
2972
    async createViewIndex(
2973
        viewOrName: View | string,
2974
        index: TableIndex,
2975
    ): Promise<void> {
2976
        const view = InstanceChecker.isView(viewOrName)
×
2977
            ? viewOrName
2978
            : await this.getCachedView(viewOrName)
2979

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

2983
        const up = this.createViewIndexSql(view, index)
×
2984
        const down = this.dropIndexSql(view, index)
×
2985
        await this.executeQueries(up, down)
×
2986
        view.addIndex(index)
×
2987
    }
2988

2989
    /**
2990
     * Creates a new indices
2991
     */
2992
    async createIndices(
2993
        tableOrName: Table | string,
2994
        indices: TableIndex[],
2995
    ): Promise<void> {
2996
        for (const index of indices) {
×
2997
            await this.createIndex(tableOrName, index)
×
2998
        }
2999
    }
3000

3001
    /**
3002
     * Creates new view indices
3003
     */
3004
    async createViewIndices(
3005
        viewOrName: View | string,
3006
        indices: TableIndex[],
3007
    ): Promise<void> {
3008
        for (const index of indices) {
×
3009
            await this.createViewIndex(viewOrName, index)
×
3010
        }
3011
    }
3012

3013
    /**
3014
     * Drops an index from the table.
3015
     */
3016
    async dropIndex(
3017
        tableOrName: Table | string,
3018
        indexOrName: TableIndex | string,
3019
    ): Promise<void> {
3020
        const table = InstanceChecker.isTable(tableOrName)
×
3021
            ? tableOrName
3022
            : await this.getCachedTable(tableOrName)
3023
        const index = InstanceChecker.isTableIndex(indexOrName)
×
3024
            ? indexOrName
3025
            : table.indices.find((i) => i.name === indexOrName)
×
3026
        if (!index)
×
3027
            throw new TypeORMError(
×
3028
                `Supplied index ${indexOrName} was not found in table ${table.name}`,
3029
            )
3030
        // old index may be passed without name. In this case we generate index name manually.
3031
        if (!index.name) index.name = this.generateIndexName(table, index)
×
3032

3033
        const up = this.dropIndexSql(table, index)
×
3034
        const down = this.createIndexSql(table, index)
×
3035
        await this.executeQueries(up, down)
×
3036
        table.removeIndex(index)
×
3037
    }
3038

3039
    /**
3040
     * Drops an index from a view.
3041
     */
3042
    async dropViewIndex(
3043
        viewOrName: View | string,
3044
        indexOrName: TableIndex | string,
3045
    ): Promise<void> {
3046
        const view = InstanceChecker.isView(viewOrName)
×
3047
            ? viewOrName
3048
            : await this.getCachedView(viewOrName)
3049
        const index = InstanceChecker.isTableIndex(indexOrName)
×
3050
            ? indexOrName
3051
            : view.indices.find((i) => i.name === indexOrName)
×
3052
        if (!index)
×
3053
            throw new TypeORMError(
×
3054
                `Supplied index ${indexOrName} was not found in view ${view.name}`,
3055
            )
3056
        // old index may be passed without name. In this case we generate index name manually.
3057
        if (!index.name) index.name = this.generateIndexName(view, index)
×
3058

3059
        const up = this.dropIndexSql(view, index)
×
3060
        const down = this.createViewIndexSql(view, index)
×
3061
        await this.executeQueries(up, down)
×
3062
        view.removeIndex(index)
×
3063
    }
3064

3065
    /**
3066
     * Drops an indices from the table.
3067
     */
3068
    async dropIndices(
3069
        tableOrName: Table | string,
3070
        indices: TableIndex[],
3071
    ): Promise<void> {
3072
        for (const index of indices) {
×
3073
            await this.dropIndex(tableOrName, index)
×
3074
        }
3075
    }
3076

3077
    /**
3078
     * Clears all table contents.
3079
     * Note: this operation uses SQL's TRUNCATE query which cannot be reverted in transactions.
3080
     */
3081
    async clearTable(tableName: string): Promise<void> {
3082
        await this.query(`TRUNCATE TABLE ${this.escapePath(tableName)}`)
×
3083
    }
3084

3085
    /**
3086
     * Removes all tables from the currently connected database.
3087
     */
3088
    async clearDatabase(): Promise<void> {
3089
        const schemas: string[] = []
×
3090
        this.connection.entityMetadatas
×
3091
            .filter((metadata) => metadata.schema)
×
3092
            .forEach((metadata) => {
3093
                const isSchemaExist = !!schemas.find(
×
3094
                    (schema) => schema === metadata.schema,
×
3095
                )
3096
                if (!isSchemaExist) schemas.push(metadata.schema!)
×
3097
            })
3098
        schemas.push(this.driver.options.schema || "current_schema()")
×
3099
        const schemaNamesString = schemas
×
3100
            .map((name) => {
3101
                return name === "current_schema()" ? name : "'" + name + "'"
×
3102
            })
3103
            .join(", ")
3104

3105
        const isAnotherTransactionActive = this.isTransactionActive
×
3106
        if (!isAnotherTransactionActive) await this.startTransaction()
×
3107
        try {
×
3108
            // drop views
3109
            const selectViewDropsQuery =
3110
                `SELECT 'DROP VIEW IF EXISTS "' || schemaname || '"."' || viewname || '" CASCADE;' as "query" ` +
×
3111
                `FROM "pg_views" WHERE "schemaname" IN (${schemaNamesString}) AND "viewname" NOT IN ('geography_columns', 'geometry_columns', 'raster_columns', 'raster_overviews')`
3112
            const dropViewQueries: ObjectLiteral[] = await this.query(
×
3113
                selectViewDropsQuery,
3114
            )
3115
            await Promise.all(
×
3116
                dropViewQueries.map((q) => this.query(q["query"])),
×
3117
            )
3118

3119
            // drop materialized views
3120
            // Note: materialized views introduced in Postgres 9.3
3121
            if (DriverUtils.isReleaseVersionOrGreater(this.driver, "9.3")) {
×
3122
                const selectMatViewDropsQuery =
3123
                    `SELECT 'DROP MATERIALIZED VIEW IF EXISTS "' || schemaname || '"."' || matviewname || '" CASCADE;' as "query" ` +
×
3124
                    `FROM "pg_matviews" WHERE "schemaname" IN (${schemaNamesString})`
3125
                const dropMatViewQueries: ObjectLiteral[] = await this.query(
×
3126
                    selectMatViewDropsQuery,
3127
                )
3128
                await Promise.all(
×
3129
                    dropMatViewQueries.map((q) => this.query(q["query"])),
×
3130
                )
3131
            }
3132

3133
            // ignore spatial_ref_sys; it's a special table supporting PostGIS
3134
            // TODO generalize this as this.driver.ignoreTables
3135

3136
            // drop tables
3137
            const selectTableDropsQuery = `SELECT 'DROP TABLE IF EXISTS "' || schemaname || '"."' || tablename || '" CASCADE;' as "query" FROM "pg_tables" WHERE "schemaname" IN (${schemaNamesString}) AND "tablename" NOT IN ('spatial_ref_sys')`
×
3138
            const dropTableQueries: ObjectLiteral[] = await this.query(
×
3139
                selectTableDropsQuery,
3140
            )
3141
            await Promise.all(
×
3142
                dropTableQueries.map((q) => this.query(q["query"])),
×
3143
            )
3144

3145
            // drop enum types
3146
            await this.dropEnumTypes(schemaNamesString)
×
3147

3148
            if (!isAnotherTransactionActive) {
×
3149
                await this.commitTransaction()
×
3150
            }
3151
        } catch (error) {
3152
            try {
×
3153
                // we throw original error even if rollback thrown an error
3154
                if (!isAnotherTransactionActive) {
×
3155
                    await this.rollbackTransaction()
×
3156
                }
3157
            } catch {
3158
                // no-op
3159
            }
3160
            throw error
×
3161
        }
3162
    }
3163

3164
    // -------------------------------------------------------------------------
3165
    // Protected Methods
3166
    // -------------------------------------------------------------------------
3167

3168
    protected async loadViews(viewNames?: string[]): Promise<View[]> {
3169
        const hasTable = await this.hasTable(this.getTypeormMetadataTableName())
×
3170

3171
        if (!hasTable) return []
×
3172

3173
        if (!viewNames) {
×
3174
            viewNames = []
×
3175
        }
3176

3177
        const currentDatabase = await this.getCurrentDatabase()
×
3178
        const currentSchema = await this.getCurrentSchema()
×
3179
        const viewsCondition =
3180
            viewNames.length === 0
×
3181
                ? "1=1"
3182
                : viewNames
3183
                      .map((tableName) => this.driver.parseTableName(tableName))
×
3184
                      .map(({ schema, tableName }) => {
3185
                          if (!schema) {
×
3186
                              schema =
×
3187
                                  this.driver.options.schema || currentSchema
×
3188
                          }
3189

3190
                          return `("t"."schema" = '${schema}' AND "t"."name" = '${tableName}')`
×
3191
                      })
3192
                      .join(" OR ")
3193

3194
        const constraintsCondition =
3195
            viewNames.length === 0
×
3196
                ? "1=1"
3197
                : viewNames
3198
                      .map((tableName) => this.driver.parseTableName(tableName))
×
3199
                      .map(({ schema, tableName }) => {
3200
                          if (!schema) {
×
3201
                              schema =
×
3202
                                  this.driver.options.schema || currentSchema
×
3203
                          }
3204

3205
                          return `("ns"."nspname" = '${schema}' AND "t"."relname" = '${tableName}')`
×
3206
                      })
3207
                      .join(" OR ")
3208

3209
        const indicesSql =
3210
            `SELECT "ns"."nspname" AS "table_schema", "t"."relname" AS "table_name", "i"."relname" AS "constraint_name", "a"."attname" AS "column_name", ` +
×
3211
            `CASE "ix"."indisunique" WHEN 't' THEN 'TRUE' ELSE'FALSE' END AS "is_unique", pg_get_expr("ix"."indpred", "ix"."indrelid") AS "condition", ` +
3212
            `"types"."typname" AS "type_name" ` +
3213
            `FROM "pg_class" "t" ` +
3214
            `INNER JOIN "pg_index" "ix" ON "ix"."indrelid" = "t"."oid" ` +
3215
            `INNER JOIN "pg_attribute" "a" ON "a"."attrelid" = "t"."oid"  AND "a"."attnum" = ANY ("ix"."indkey") ` +
3216
            `INNER JOIN "pg_namespace" "ns" ON "ns"."oid" = "t"."relnamespace" ` +
3217
            `INNER JOIN "pg_class" "i" ON "i"."oid" = "ix"."indexrelid" ` +
3218
            `INNER JOIN "pg_type" "types" ON "types"."oid" = "a"."atttypid" ` +
3219
            `LEFT JOIN "pg_constraint" "cnst" ON "cnst"."conname" = "i"."relname" ` +
3220
            `WHERE "t"."relkind" IN ('m') AND "cnst"."contype" IS NULL AND (${constraintsCondition})`
3221

3222
        const query =
3223
            `SELECT "t".* FROM ${this.escapePath(
×
3224
                this.getTypeormMetadataTableName(),
3225
            )} "t" ` +
3226
            `INNER JOIN "pg_catalog"."pg_class" "c" ON "c"."relname" = "t"."name" ` +
3227
            `INNER JOIN "pg_namespace" "n" ON "n"."oid" = "c"."relnamespace" AND "n"."nspname" = "t"."schema" ` +
3228
            `WHERE "t"."type" IN ('${MetadataTableType.VIEW}', '${
3229
                MetadataTableType.MATERIALIZED_VIEW
3230
            }') ${viewsCondition ? `AND (${viewsCondition})` : ""}`
×
3231

3232
        const dbViews = await this.query(query)
×
3233
        const dbIndices: ObjectLiteral[] = await this.query(indicesSql)
×
3234
        return dbViews.map((dbView: any) => {
×
3235
            // find index constraints of table, group them by constraint name and build TableIndex.
3236
            const tableIndexConstraints = OrmUtils.uniq(
×
3237
                dbIndices.filter((dbIndex) => {
3238
                    return (
×
3239
                        dbIndex["table_name"] === dbView["name"] &&
×
3240
                        dbIndex["table_schema"] === dbView["schema"]
3241
                    )
3242
                }),
3243
                (dbIndex) => dbIndex["constraint_name"],
×
3244
            )
3245
            const view = new View()
×
3246
            const schema =
3247
                dbView["schema"] === currentSchema &&
×
3248
                !this.driver.options.schema
3249
                    ? undefined
3250
                    : dbView["schema"]
3251
            view.database = currentDatabase
×
3252
            view.schema = dbView["schema"]
×
3253
            view.name = this.driver.buildTableName(dbView["name"], schema)
×
3254
            view.expression = dbView["value"]
×
3255
            view.materialized =
×
3256
                dbView["type"] === MetadataTableType.MATERIALIZED_VIEW
3257
            view.indices = tableIndexConstraints.map((constraint) => {
×
3258
                const indices = dbIndices.filter((index) => {
×
3259
                    return (
×
3260
                        index["table_schema"] === constraint["table_schema"] &&
×
3261
                        index["table_name"] === constraint["table_name"] &&
3262
                        index["constraint_name"] ===
3263
                            constraint["constraint_name"]
3264
                    )
3265
                })
3266
                return new TableIndex(<TableIndexOptions>{
×
3267
                    view: view,
3268
                    name: constraint["constraint_name"],
3269
                    columnNames: indices.map((i) => i["column_name"]),
×
3270
                    isUnique: constraint["is_unique"] === "TRUE",
3271
                    where: constraint["condition"],
3272
                    isFulltext: false,
3273
                })
3274
            })
3275
            return view
×
3276
        })
3277
    }
3278

3279
    /**
3280
     * Loads all tables (with given names) from the database and creates a Table from them.
3281
     */
3282
    protected async loadTables(tableNames?: string[]): Promise<Table[]> {
3283
        // if no tables given then no need to proceed
3284
        if (tableNames && tableNames.length === 0) {
×
3285
            return []
×
3286
        }
3287

3288
        const currentSchema = await this.getCurrentSchema()
×
3289
        const currentDatabase = await this.getCurrentDatabase()
×
3290

3291
        const dbTables: {
3292
            table_schema: string
3293
            table_name: string
3294
            table_comment: string
3295
        }[] = []
×
3296

3297
        if (!tableNames) {
×
3298
            const tablesSql = `SELECT "table_schema", "table_name", obj_description(('"' || "table_schema" || '"."' || "table_name" || '"')::regclass, 'pg_class') AS table_comment FROM "information_schema"."tables"`
×
3299
            dbTables.push(...(await this.query(tablesSql)))
×
3300
        } else {
3301
            const tablesCondition = tableNames
×
3302
                .map((tableName) => this.driver.parseTableName(tableName))
×
3303
                .map(({ schema, tableName }) => {
3304
                    return `("table_schema" = '${
×
3305
                        schema || currentSchema
×
3306
                    }' AND "table_name" = '${tableName}')`
3307
                })
3308
                .join(" OR ")
3309

3310
            const tablesSql =
3311
                `SELECT "table_schema", "table_name", obj_description(('"' || "table_schema" || '"."' || "table_name" || '"')::regclass, 'pg_class') AS table_comment FROM "information_schema"."tables" WHERE ` +
×
3312
                tablesCondition
3313
            dbTables.push(...(await this.query(tablesSql)))
×
3314
        }
3315

3316
        // if tables were not found in the db, no need to proceed
3317
        if (dbTables.length === 0) {
×
3318
            return []
×
3319
        }
3320

3321
        /**
3322
         * Uses standard SQL information_schema.columns table and postgres-specific
3323
         * pg_catalog.pg_attribute table to get column information.
3324
         * @see https://stackoverflow.com/a/19541865
3325
         */
3326
        const columnsCondition = dbTables
×
3327
            .map(({ table_schema, table_name }) => {
3328
                return `("table_schema" = '${table_schema}' AND "table_name" = '${table_name}')`
×
3329
            })
3330
            .join(" OR ")
3331
        const columnsSql =
3332
            `SELECT columns.*, pg_catalog.col_description(('"' || table_catalog || '"."' || table_schema || '"."' || table_name || '"')::regclass::oid, ordinal_position) AS description, ` +
×
3333
            `('"' || "udt_schema" || '"."' || "udt_name" || '"')::"regtype" AS "regtype", pg_catalog.format_type("col_attr"."atttypid", "col_attr"."atttypmod") AS "format_type" ` +
3334
            `FROM "information_schema"."columns" ` +
3335
            `LEFT JOIN "pg_catalog"."pg_attribute" AS "col_attr" ON "col_attr"."attname" = "columns"."column_name" ` +
3336
            `AND "col_attr"."attrelid" = ( ` +
3337
            `SELECT "cls"."oid" FROM "pg_catalog"."pg_class" AS "cls" ` +
3338
            `LEFT JOIN "pg_catalog"."pg_namespace" AS "ns" ON "ns"."oid" = "cls"."relnamespace" ` +
3339
            `WHERE "cls"."relname" = "columns"."table_name" ` +
3340
            `AND "ns"."nspname" = "columns"."table_schema" ` +
3341
            `) ` +
3342
            `WHERE ` +
3343
            columnsCondition
3344

3345
        const constraintsCondition = dbTables
×
3346
            .map(({ table_schema, table_name }) => {
3347
                return `("ns"."nspname" = '${table_schema}' AND "t"."relname" = '${table_name}')`
×
3348
            })
3349
            .join(" OR ")
3350

3351
        const constraintsSql =
3352
            `SELECT "ns"."nspname" AS "table_schema", "t"."relname" AS "table_name", "cnst"."conname" AS "constraint_name", ` +
×
3353
            `pg_get_constraintdef("cnst"."oid") AS "expression", ` +
3354
            `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" ` +
3355
            `FROM "pg_constraint" "cnst" ` +
3356
            `INNER JOIN "pg_class" "t" ON "t"."oid" = "cnst"."conrelid" ` +
3357
            `INNER JOIN "pg_namespace" "ns" ON "ns"."oid" = "cnst"."connamespace" ` +
3358
            `LEFT JOIN "pg_attribute" "a" ON "a"."attrelid" = "cnst"."conrelid" AND "a"."attnum" = ANY ("cnst"."conkey") ` +
3359
            `WHERE "t"."relkind" IN ('r', 'p') AND (${constraintsCondition})`
3360

3361
        const indicesSql =
3362
            `SELECT "ns"."nspname" AS "table_schema", "t"."relname" AS "table_name", "i"."relname" AS "constraint_name", "a"."attname" AS "column_name", ` +
×
3363
            `CASE "ix"."indisunique" WHEN 't' THEN 'TRUE' ELSE'FALSE' END AS "is_unique", pg_get_expr("ix"."indpred", "ix"."indrelid") AS "condition", ` +
3364
            `"types"."typname" AS "type_name", "am"."amname" AS "index_type" ` +
3365
            `FROM "pg_class" "t" ` +
3366
            `INNER JOIN "pg_index" "ix" ON "ix"."indrelid" = "t"."oid" ` +
3367
            `INNER JOIN "pg_attribute" "a" ON "a"."attrelid" = "t"."oid"  AND "a"."attnum" = ANY ("ix"."indkey") ` +
3368
            `INNER JOIN "pg_namespace" "ns" ON "ns"."oid" = "t"."relnamespace" ` +
3369
            `INNER JOIN "pg_class" "i" ON "i"."oid" = "ix"."indexrelid" ` +
3370
            `INNER JOIN "pg_type" "types" ON "types"."oid" = "a"."atttypid" ` +
3371
            `INNER JOIN "pg_am" "am" ON "i"."relam" = "am"."oid" ` +
3372
            `LEFT JOIN "pg_constraint" "cnst" ON "cnst"."conname" = "i"."relname" ` +
3373
            `WHERE "t"."relkind" IN ('r', 'p') AND "cnst"."contype" IS NULL AND (${constraintsCondition})`
3374

3375
        const foreignKeysCondition = dbTables
×
3376
            .map(({ table_schema, table_name }) => {
3377
                return `("ns"."nspname" = '${table_schema}' AND "cl"."relname" = '${table_name}')`
×
3378
            })
3379
            .join(" OR ")
3380

3381
        const hasRelispartitionColumn =
3382
            await this.hasSupportForPartitionedTables()
×
3383
        const isPartitionCondition = hasRelispartitionColumn
×
3384
            ? ` AND "cl"."relispartition" = 'f'`
3385
            : ""
3386

3387
        const foreignKeysSql =
3388
            `SELECT "con"."conname" AS "constraint_name", "con"."nspname" AS "table_schema", "con"."relname" AS "table_name", "att2"."attname" AS "column_name", ` +
×
3389
            `"ns"."nspname" AS "referenced_table_schema", "cl"."relname" AS "referenced_table_name", "att"."attname" AS "referenced_column_name", "con"."confdeltype" AS "on_delete", ` +
3390
            `"con"."confupdtype" AS "on_update", "con"."condeferrable" AS "deferrable", "con"."condeferred" AS "deferred" ` +
3391
            `FROM ( ` +
3392
            `SELECT UNNEST ("con1"."conkey") AS "parent", UNNEST ("con1"."confkey") AS "child", "con1"."confrelid", "con1"."conrelid", "con1"."conname", "con1"."contype", "ns"."nspname", ` +
3393
            `"cl"."relname", "con1"."condeferrable", ` +
3394
            `CASE WHEN "con1"."condeferred" THEN 'INITIALLY DEFERRED' ELSE 'INITIALLY IMMEDIATE' END as condeferred, ` +
3395
            `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", ` +
3396
            `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" ` +
3397
            `FROM "pg_class" "cl" ` +
3398
            `INNER JOIN "pg_namespace" "ns" ON "cl"."relnamespace" = "ns"."oid" ` +
3399
            `INNER JOIN "pg_constraint" "con1" ON "con1"."conrelid" = "cl"."oid" ` +
3400
            `WHERE "con1"."contype" = 'f' AND (${foreignKeysCondition}) ` +
3401
            `) "con" ` +
3402
            `INNER JOIN "pg_attribute" "att" ON "att"."attrelid" = "con"."confrelid" AND "att"."attnum" = "con"."child" ` +
3403
            `INNER JOIN "pg_class" "cl" ON "cl"."oid" = "con"."confrelid" ${isPartitionCondition}` +
3404
            `INNER JOIN "pg_namespace" "ns" ON "cl"."relnamespace" = "ns"."oid" ` +
3405
            `INNER JOIN "pg_attribute" "att2" ON "att2"."attrelid" = "con"."conrelid" AND "att2"."attnum" = "con"."parent"`
3406

3407
        const [
3408
            dbColumns,
3409
            dbConstraints,
3410
            dbIndices,
3411
            dbForeignKeys,
3412
        ]: ObjectLiteral[][] = await Promise.all([
×
3413
            this.query(columnsSql),
3414
            this.query(constraintsSql),
3415
            this.query(indicesSql),
3416
            this.query(foreignKeysSql),
3417
        ])
3418

3419
        // create tables for loaded tables
3420
        return Promise.all(
×
3421
            dbTables.map(async (dbTable) => {
3422
                const table = new Table()
×
3423

3424
                const getSchemaFromKey = (dbObject: any, key: string) => {
×
3425
                    return dbObject[key] === currentSchema &&
×
3426
                        (!this.driver.options.schema ||
3427
                            this.driver.options.schema === currentSchema)
3428
                        ? undefined
3429
                        : dbObject[key]
3430
                }
3431
                // We do not need to join schema name, when database is by default.
3432
                const schema = getSchemaFromKey(dbTable, "table_schema")
×
3433
                table.database = currentDatabase
×
3434
                table.schema = dbTable["table_schema"]
×
3435
                table.comment = dbTable["table_comment"]
×
3436
                table.name = this.driver.buildTableName(
×
3437
                    dbTable["table_name"],
3438
                    schema,
3439
                )
3440

3441
                // create columns from the loaded columns
3442
                table.columns = await Promise.all(
×
3443
                    dbColumns
3444
                        .filter(
3445
                            (dbColumn) =>
3446
                                dbColumn["table_name"] ===
×
3447
                                    dbTable["table_name"] &&
3448
                                dbColumn["table_schema"] ===
3449
                                    dbTable["table_schema"],
3450
                        )
3451
                        .map(async (dbColumn) => {
3452
                            const columnConstraints = dbConstraints.filter(
×
3453
                                (dbConstraint) => {
3454
                                    return (
×
3455
                                        dbConstraint["table_name"] ===
×
3456
                                            dbColumn["table_name"] &&
3457
                                        dbConstraint["table_schema"] ===
3458
                                            dbColumn["table_schema"] &&
3459
                                        dbConstraint["column_name"] ===
3460
                                            dbColumn["column_name"]
3461
                                    )
3462
                                },
3463
                            )
3464

3465
                            const tableColumn = new TableColumn()
×
3466
                            tableColumn.name = dbColumn["column_name"]
×
3467
                            tableColumn.type = dbColumn["regtype"].toLowerCase()
×
3468

3469
                            if (
×
3470
                                tableColumn.type === "numeric" ||
×
3471
                                tableColumn.type === "numeric[]" ||
3472
                                tableColumn.type === "decimal" ||
3473
                                tableColumn.type === "float"
3474
                            ) {
3475
                                let numericPrecision =
3476
                                    dbColumn["numeric_precision"]
×
3477
                                let numericScale = dbColumn["numeric_scale"]
×
3478
                                if (dbColumn["data_type"] === "ARRAY") {
×
3479
                                    const numericSize = dbColumn[
×
3480
                                        "format_type"
3481
                                    ].match(
3482
                                        /^numeric\(([0-9]+),([0-9]+)\)\[\]$/,
3483
                                    )
3484
                                    if (numericSize) {
×
3485
                                        numericPrecision = +numericSize[1]
×
3486
                                        numericScale = +numericSize[2]
×
3487
                                    }
3488
                                }
3489
                                // If one of these properties was set, and another was not, Postgres sets '0' in to unspecified property
3490
                                // we set 'undefined' in to unspecified property to avoid changing column on sync
3491
                                if (
×
3492
                                    numericPrecision !== null &&
×
3493
                                    !this.isDefaultColumnPrecision(
3494
                                        table,
3495
                                        tableColumn,
3496
                                        numericPrecision,
3497
                                    )
3498
                                ) {
3499
                                    tableColumn.precision = numericPrecision
×
3500
                                } else if (
×
3501
                                    numericScale !== null &&
×
3502
                                    !this.isDefaultColumnScale(
3503
                                        table,
3504
                                        tableColumn,
3505
                                        numericScale,
3506
                                    )
3507
                                ) {
3508
                                    tableColumn.precision = undefined
×
3509
                                }
3510
                                if (
×
3511
                                    numericScale !== null &&
×
3512
                                    !this.isDefaultColumnScale(
3513
                                        table,
3514
                                        tableColumn,
3515
                                        numericScale,
3516
                                    )
3517
                                ) {
3518
                                    tableColumn.scale = numericScale
×
3519
                                } else if (
×
3520
                                    numericPrecision !== null &&
×
3521
                                    !this.isDefaultColumnPrecision(
3522
                                        table,
3523
                                        tableColumn,
3524
                                        numericPrecision,
3525
                                    )
3526
                                ) {
3527
                                    tableColumn.scale = undefined
×
3528
                                }
3529
                            }
3530

3531
                            if (
×
3532
                                tableColumn.type === "interval" ||
×
3533
                                tableColumn.type === "time without time zone" ||
3534
                                tableColumn.type === "time with time zone" ||
3535
                                tableColumn.type ===
3536
                                    "timestamp without time zone" ||
3537
                                tableColumn.type === "timestamp with time zone"
3538
                            ) {
3539
                                tableColumn.precision =
×
3540
                                    !this.isDefaultColumnPrecision(
×
3541
                                        table,
3542
                                        tableColumn,
3543
                                        dbColumn["datetime_precision"],
3544
                                    )
3545
                                        ? dbColumn["datetime_precision"]
3546
                                        : undefined
3547
                            }
3548

3549
                            // check if column has user-defined data type.
3550
                            // NOTE: if ENUM type defined with "array:true" it comes with ARRAY type instead of USER-DEFINED
3551
                            if (
×
3552
                                dbColumn["data_type"] === "USER-DEFINED" ||
×
3553
                                dbColumn["data_type"] === "ARRAY"
3554
                            ) {
3555
                                const { name } =
3556
                                    await this.getUserDefinedTypeName(
×
3557
                                        table,
3558
                                        tableColumn,
3559
                                    )
3560

3561
                                // check if `enumName` is specified by user
3562
                                const builtEnumName = this.buildEnumName(
×
3563
                                    table,
3564
                                    tableColumn,
3565
                                    false,
3566
                                    true,
3567
                                )
3568
                                const enumName =
3569
                                    builtEnumName !== name ? name : undefined
×
3570

3571
                                // check if type is ENUM
3572
                                const sql =
3573
                                    `SELECT "e"."enumlabel" AS "value" FROM "pg_enum" "e" ` +
×
3574
                                    `INNER JOIN "pg_type" "t" ON "t"."oid" = "e"."enumtypid" ` +
3575
                                    `INNER JOIN "pg_namespace" "n" ON "n"."oid" = "t"."typnamespace" ` +
3576
                                    `WHERE "n"."nspname" = '${
3577
                                        dbTable["table_schema"]
3578
                                    }' AND "t"."typname" = '${
3579
                                        enumName || name
×
3580
                                    }'`
3581
                                const results: ObjectLiteral[] =
3582
                                    await this.query(sql)
×
3583

3584
                                if (results.length) {
×
3585
                                    tableColumn.type = "enum"
×
3586
                                    tableColumn.enum = results.map(
×
3587
                                        (result) => result["value"],
×
3588
                                    )
3589
                                    tableColumn.enumName = enumName
×
3590
                                }
3591

3592
                                if (dbColumn["data_type"] === "ARRAY") {
×
3593
                                    tableColumn.isArray = true
×
3594
                                    const type = tableColumn.type.replace(
×
3595
                                        "[]",
3596
                                        "",
3597
                                    )
3598
                                    tableColumn.type =
×
3599
                                        this.connection.driver.normalizeType({
3600
                                            type: type,
3601
                                        })
3602
                                }
3603
                            }
3604

3605
                            if (
×
3606
                                tableColumn.type === "geometry" ||
×
3607
                                tableColumn.type === "geography"
3608
                            ) {
3609
                                const sql =
3610
                                    `SELECT * FROM (` +
×
3611
                                    `SELECT "f_table_schema" "table_schema", "f_table_name" "table_name", ` +
3612
                                    `"f_${tableColumn.type}_column" "column_name", "srid", "type" ` +
3613
                                    `FROM "${tableColumn.type}_columns"` +
3614
                                    `) AS _ ` +
3615
                                    `WHERE "column_name" = '${dbColumn["column_name"]}' AND ` +
3616
                                    `"table_schema" = '${dbColumn["table_schema"]}' AND ` +
3617
                                    `"table_name" = '${dbColumn["table_name"]}'`
3618

3619
                                const results: ObjectLiteral[] =
3620
                                    await this.query(sql)
×
3621

3622
                                if (results.length > 0) {
×
3623
                                    tableColumn.spatialFeatureType =
×
3624
                                        results[0].type
3625
                                    tableColumn.srid = results[0].srid
×
3626
                                }
3627
                            }
3628

3629
                            // check only columns that have length property
3630
                            if (
×
3631
                                this.driver.withLengthColumnTypes.indexOf(
3632
                                    tableColumn.type as ColumnType,
3633
                                ) !== -1
3634
                            ) {
3635
                                let length
3636
                                if (tableColumn.isArray) {
×
3637
                                    const match = /\((\d+)\)/.exec(
×
3638
                                        dbColumn["format_type"],
3639
                                    )
3640
                                    length = match ? match[1] : undefined
×
3641
                                } else if (
×
3642
                                    dbColumn["character_maximum_length"]
3643
                                ) {
3644
                                    length =
×
3645
                                        dbColumn[
3646
                                            "character_maximum_length"
3647
                                        ].toString()
3648
                                }
3649
                                if (length) {
×
3650
                                    tableColumn.length =
×
3651
                                        !this.isDefaultColumnLength(
×
3652
                                            table,
3653
                                            tableColumn,
3654
                                            length,
3655
                                        )
3656
                                            ? length
3657
                                            : ""
3658
                                }
3659
                            }
3660
                            tableColumn.isNullable =
×
3661
                                dbColumn["is_nullable"] === "YES"
3662

3663
                            const primaryConstraint = columnConstraints.find(
×
3664
                                (constraint) =>
3665
                                    constraint["constraint_type"] === "PRIMARY",
×
3666
                            )
3667
                            if (primaryConstraint) {
×
3668
                                tableColumn.isPrimary = true
×
3669
                                // find another columns involved in primary key constraint
3670
                                const anotherPrimaryConstraints =
3671
                                    dbConstraints.filter(
×
3672
                                        (constraint) =>
3673
                                            constraint["table_name"] ===
×
3674
                                                dbColumn["table_name"] &&
3675
                                            constraint["table_schema"] ===
3676
                                                dbColumn["table_schema"] &&
3677
                                            constraint["column_name"] !==
3678
                                                dbColumn["column_name"] &&
3679
                                            constraint["constraint_type"] ===
3680
                                                "PRIMARY",
3681
                                    )
3682

3683
                                // collect all column names
3684
                                const columnNames =
3685
                                    anotherPrimaryConstraints.map(
×
3686
                                        (constraint) =>
3687
                                            constraint["column_name"],
×
3688
                                    )
3689
                                columnNames.push(dbColumn["column_name"])
×
3690

3691
                                // build default primary key constraint name
3692
                                const pkName =
3693
                                    this.connection.namingStrategy.primaryKeyName(
×
3694
                                        table,
3695
                                        columnNames,
3696
                                    )
3697

3698
                                // if primary key has user-defined constraint name, write it in table column
3699
                                if (
×
3700
                                    primaryConstraint["constraint_name"] !==
3701
                                    pkName
3702
                                ) {
3703
                                    tableColumn.primaryKeyConstraintName =
×
3704
                                        primaryConstraint["constraint_name"]
3705
                                }
3706
                            }
3707

3708
                            const uniqueConstraints = columnConstraints.filter(
×
3709
                                (constraint) =>
3710
                                    constraint["constraint_type"] === "UNIQUE",
×
3711
                            )
3712
                            const isConstraintComposite =
3713
                                uniqueConstraints.every((uniqueConstraint) => {
×
3714
                                    return dbConstraints.some(
×
3715
                                        (dbConstraint) =>
3716
                                            dbConstraint["constraint_type"] ===
×
3717
                                                "UNIQUE" &&
3718
                                            dbConstraint["constraint_name"] ===
3719
                                                uniqueConstraint[
3720
                                                    "constraint_name"
3721
                                                ] &&
3722
                                            dbConstraint["column_name"] !==
3723
                                                dbColumn["column_name"],
3724
                                    )
3725
                                })
3726
                            tableColumn.isUnique =
×
3727
                                uniqueConstraints.length > 0 &&
×
3728
                                !isConstraintComposite
3729

3730
                            if (dbColumn.is_identity === "YES") {
×
3731
                                // Postgres 10+ Identity column
3732
                                tableColumn.isGenerated = true
×
3733
                                tableColumn.generationStrategy = "identity"
×
3734
                                tableColumn.generatedIdentity =
×
3735
                                    dbColumn.identity_generation
3736
                            } else if (
×
3737
                                dbColumn["column_default"] !== null &&
×
3738
                                dbColumn["column_default"] !== undefined
3739
                            ) {
3740
                                const serialDefaultName = `nextval('${this.buildSequenceName(
×
3741
                                    table,
3742
                                    dbColumn["column_name"],
3743
                                )}'::regclass)`
3744
                                const serialDefaultPath = `nextval('${this.buildSequencePath(
×
3745
                                    table,
3746
                                    dbColumn["column_name"],
3747
                                )}'::regclass)`
3748

3749
                                const defaultWithoutQuotes = dbColumn[
×
3750
                                    "column_default"
3751
                                ].replace(/"/g, "")
3752

3753
                                if (
×
3754
                                    defaultWithoutQuotes ===
×
3755
                                        serialDefaultName ||
3756
                                    defaultWithoutQuotes === serialDefaultPath
3757
                                ) {
3758
                                    tableColumn.isGenerated = true
×
3759
                                    tableColumn.generationStrategy = "increment"
×
3760
                                } else if (
×
3761
                                    dbColumn["column_default"] ===
×
3762
                                        "gen_random_uuid()" ||
3763
                                    /^uuid_generate_v\d\(\)/.test(
3764
                                        dbColumn["column_default"],
3765
                                    )
3766
                                ) {
3767
                                    if (tableColumn.type === "uuid") {
×
3768
                                        tableColumn.isGenerated = true
×
3769
                                        tableColumn.generationStrategy = "uuid"
×
3770
                                    } else {
3771
                                        tableColumn.default =
×
3772
                                            dbColumn["column_default"]
3773
                                    }
3774
                                } else if (
×
3775
                                    dbColumn["column_default"] === "now()" ||
×
3776
                                    dbColumn["column_default"].indexOf(
3777
                                        "'now'::text",
3778
                                    ) !== -1
3779
                                ) {
3780
                                    tableColumn.default =
×
3781
                                        dbColumn["column_default"]
3782
                                } else {
3783
                                    tableColumn.default = dbColumn[
×
3784
                                        "column_default"
3785
                                    ].replace(/::[\w\s.[\]\-"]+/g, "")
3786
                                    tableColumn.default =
×
3787
                                        tableColumn.default.replace(
3788
                                            /^(-?\d+)$/,
3789
                                            "'$1'",
3790
                                        )
3791
                                }
3792
                            }
3793

3794
                            if (
×
3795
                                dbColumn["is_generated"] === "ALWAYS" &&
×
3796
                                dbColumn["generation_expression"]
3797
                            ) {
3798
                                // In postgres there is no VIRTUAL generated column type
3799
                                tableColumn.generatedType = "STORED"
×
3800
                                // We cannot relay on information_schema.columns.generation_expression, because it is formatted different.
3801
                                const asExpressionQuery =
3802
                                    this.selectTypeormMetadataSql({
×
3803
                                        database: currentDatabase,
3804
                                        schema: dbTable["table_schema"],
3805
                                        table: dbTable["table_name"],
3806
                                        type: MetadataTableType.GENERATED_COLUMN,
3807
                                        name: tableColumn.name,
3808
                                    })
3809

3810
                                const results = await this.query(
×
3811
                                    asExpressionQuery.query,
3812
                                    asExpressionQuery.parameters,
3813
                                )
3814
                                if (results[0] && results[0].value) {
×
3815
                                    tableColumn.asExpression = results[0].value
×
3816
                                } else {
3817
                                    tableColumn.asExpression = ""
×
3818
                                }
3819
                            }
3820

3821
                            tableColumn.comment = dbColumn["description"]
×
3822
                                ? dbColumn["description"]
3823
                                : undefined
3824
                            if (dbColumn["character_set_name"])
×
3825
                                tableColumn.charset =
×
3826
                                    dbColumn["character_set_name"]
3827
                            if (dbColumn["collation_name"])
×
3828
                                tableColumn.collation =
×
3829
                                    dbColumn["collation_name"]
3830
                            return tableColumn
×
3831
                        }),
3832
                )
3833

3834
                // find unique constraints of table, group them by constraint name and build TableUnique.
3835
                const tableUniqueConstraints = OrmUtils.uniq(
×
3836
                    dbConstraints.filter((dbConstraint) => {
3837
                        return (
×
3838
                            dbConstraint["table_name"] ===
×
3839
                                dbTable["table_name"] &&
3840
                            dbConstraint["table_schema"] ===
3841
                                dbTable["table_schema"] &&
3842
                            dbConstraint["constraint_type"] === "UNIQUE"
3843
                        )
3844
                    }),
3845
                    (dbConstraint) => dbConstraint["constraint_name"],
×
3846
                )
3847

3848
                table.uniques = tableUniqueConstraints.map((constraint) => {
×
3849
                    const uniques = dbConstraints.filter(
×
3850
                        (dbC) =>
3851
                            dbC["constraint_name"] ===
×
3852
                            constraint["constraint_name"],
3853
                    )
3854
                    return new TableUnique({
×
3855
                        name: constraint["constraint_name"],
3856
                        columnNames: uniques.map((u) => u["column_name"]),
×
3857
                        deferrable: constraint["deferrable"]
×
3858
                            ? constraint["deferred"]
3859
                            : undefined,
3860
                    })
3861
                })
3862

3863
                // find check constraints of table, group them by constraint name and build TableCheck.
3864
                const tableCheckConstraints = OrmUtils.uniq(
×
3865
                    dbConstraints.filter((dbConstraint) => {
3866
                        return (
×
3867
                            dbConstraint["table_name"] ===
×
3868
                                dbTable["table_name"] &&
3869
                            dbConstraint["table_schema"] ===
3870
                                dbTable["table_schema"] &&
3871
                            dbConstraint["constraint_type"] === "CHECK"
3872
                        )
3873
                    }),
3874
                    (dbConstraint) => dbConstraint["constraint_name"],
×
3875
                )
3876

3877
                table.checks = tableCheckConstraints.map((constraint) => {
×
3878
                    const checks = dbConstraints.filter(
×
3879
                        (dbC) =>
3880
                            dbC["constraint_name"] ===
×
3881
                            constraint["constraint_name"],
3882
                    )
3883
                    return new TableCheck({
×
3884
                        name: constraint["constraint_name"],
3885
                        columnNames: checks.map((c) => c["column_name"]),
×
3886
                        expression: constraint["expression"].replace(
3887
                            /^\s*CHECK\s*\((.*)\)\s*$/i,
3888
                            "$1",
3889
                        ),
3890
                    })
3891
                })
3892

3893
                // find exclusion constraints of table, group them by constraint name and build TableExclusion.
3894
                const tableExclusionConstraints = OrmUtils.uniq(
×
3895
                    dbConstraints.filter((dbConstraint) => {
3896
                        return (
×
3897
                            dbConstraint["table_name"] ===
×
3898
                                dbTable["table_name"] &&
3899
                            dbConstraint["table_schema"] ===
3900
                                dbTable["table_schema"] &&
3901
                            dbConstraint["constraint_type"] === "EXCLUDE"
3902
                        )
3903
                    }),
3904
                    (dbConstraint) => dbConstraint["constraint_name"],
×
3905
                )
3906

3907
                table.exclusions = tableExclusionConstraints.map(
×
3908
                    (constraint) => {
3909
                        return new TableExclusion({
×
3910
                            name: constraint["constraint_name"],
3911
                            expression: constraint["expression"].substring(8), // trim EXCLUDE from start of expression
3912
                        })
3913
                    },
3914
                )
3915

3916
                // find foreign key constraints of table, group them by constraint name and build TableForeignKey.
3917
                const tableForeignKeyConstraints = OrmUtils.uniq(
×
3918
                    dbForeignKeys.filter((dbForeignKey) => {
3919
                        return (
×
3920
                            dbForeignKey["table_name"] ===
×
3921
                                dbTable["table_name"] &&
3922
                            dbForeignKey["table_schema"] ===
3923
                                dbTable["table_schema"]
3924
                        )
3925
                    }),
3926
                    (dbForeignKey) => dbForeignKey["constraint_name"],
×
3927
                )
3928

3929
                table.foreignKeys = tableForeignKeyConstraints.map(
×
3930
                    (dbForeignKey) => {
3931
                        const foreignKeys = dbForeignKeys.filter(
×
3932
                            (dbFk) =>
3933
                                dbFk["constraint_name"] ===
×
3934
                                dbForeignKey["constraint_name"],
3935
                        )
3936

3937
                        // if referenced table located in currently used schema, we don't need to concat schema name to table name.
3938
                        const schema = getSchemaFromKey(
×
3939
                            dbForeignKey,
3940
                            "referenced_table_schema",
3941
                        )
3942
                        const referencedTableName = this.driver.buildTableName(
×
3943
                            dbForeignKey["referenced_table_name"],
3944
                            schema,
3945
                        )
3946

3947
                        return new TableForeignKey({
×
3948
                            name: dbForeignKey["constraint_name"],
3949
                            columnNames: foreignKeys.map(
3950
                                (dbFk) => dbFk["column_name"],
×
3951
                            ),
3952
                            referencedSchema:
3953
                                dbForeignKey["referenced_table_schema"],
3954
                            referencedTableName: referencedTableName,
3955
                            referencedColumnNames: foreignKeys.map(
3956
                                (dbFk) => dbFk["referenced_column_name"],
×
3957
                            ),
3958
                            onDelete: dbForeignKey["on_delete"],
3959
                            onUpdate: dbForeignKey["on_update"],
3960
                            deferrable: dbForeignKey["deferrable"]
×
3961
                                ? dbForeignKey["deferred"]
3962
                                : undefined,
3963
                        })
3964
                    },
3965
                )
3966

3967
                // find index constraints of table, group them by constraint name and build TableIndex.
3968
                const tableIndexConstraints = OrmUtils.uniq(
×
3969
                    dbIndices.filter((dbIndex) => {
3970
                        return (
×
3971
                            dbIndex["table_name"] === dbTable["table_name"] &&
×
3972
                            dbIndex["table_schema"] === dbTable["table_schema"]
3973
                        )
3974
                    }),
3975
                    (dbIndex) => dbIndex["constraint_name"],
×
3976
                )
3977

3978
                table.indices = tableIndexConstraints.map((constraint) => {
×
3979
                    const indices = dbIndices.filter((index) => {
×
3980
                        return (
×
3981
                            index["table_schema"] ===
×
3982
                                constraint["table_schema"] &&
3983
                            index["table_name"] === constraint["table_name"] &&
3984
                            index["constraint_name"] ===
3985
                                constraint["constraint_name"]
3986
                        )
3987
                    })
3988
                    return new TableIndex(<TableIndexOptions>{
×
3989
                        table: table,
3990
                        name: constraint["constraint_name"],
3991
                        columnNames: indices.map((i) => i["column_name"]),
×
3992
                        isUnique: constraint["is_unique"] === "TRUE",
3993
                        where: constraint["condition"],
3994
                        isSpatial: constraint["index_type"] === "gist",
3995
                        isFulltext: false,
3996
                    })
3997
                })
3998

3999
                return table
×
4000
            }),
4001
        )
4002
    }
4003

4004
    /**
4005
     * Builds create table sql.
4006
     */
4007
    protected createTableSql(table: Table, createForeignKeys?: boolean): Query {
4008
        const columnDefinitions = table.columns
×
4009
            .map((column) => this.buildCreateColumnSql(table, column))
×
4010
            .join(", ")
4011
        let sql = `CREATE TABLE ${this.escapePath(table)} (${columnDefinitions}`
×
4012

4013
        table.columns
×
4014
            .filter((column) => column.isUnique)
×
4015
            .forEach((column) => {
4016
                const isUniqueExist = table.uniques.some(
×
4017
                    (unique) =>
4018
                        unique.columnNames.length === 1 &&
×
4019
                        unique.columnNames[0] === column.name,
4020
                )
4021
                if (!isUniqueExist)
×
4022
                    table.uniques.push(
×
4023
                        new TableUnique({
4024
                            name: this.connection.namingStrategy.uniqueConstraintName(
4025
                                table,
4026
                                [column.name],
4027
                            ),
4028
                            columnNames: [column.name],
4029
                        }),
4030
                    )
4031
            })
4032

4033
        if (table.uniques.length > 0) {
×
4034
            const uniquesSql = table.uniques
×
4035
                .map((unique) => {
4036
                    const uniqueName = unique.name
×
4037
                        ? unique.name
4038
                        : this.connection.namingStrategy.uniqueConstraintName(
4039
                              table,
4040
                              unique.columnNames,
4041
                          )
4042
                    const columnNames = unique.columnNames
×
4043
                        .map((columnName) => `"${columnName}"`)
×
4044
                        .join(", ")
4045
                    let constraint = `CONSTRAINT "${uniqueName}" UNIQUE (${columnNames})`
×
4046
                    if (unique.deferrable)
×
4047
                        constraint += ` DEFERRABLE ${unique.deferrable}`
×
4048
                    return constraint
×
4049
                })
4050
                .join(", ")
4051

4052
            sql += `, ${uniquesSql}`
×
4053
        }
4054

4055
        if (table.checks.length > 0) {
×
4056
            const checksSql = table.checks
×
4057
                .map((check) => {
4058
                    const checkName = check.name
×
4059
                        ? check.name
4060
                        : this.connection.namingStrategy.checkConstraintName(
4061
                              table,
4062
                              check.expression!,
4063
                          )
4064
                    return `CONSTRAINT "${checkName}" CHECK (${check.expression})`
×
4065
                })
4066
                .join(", ")
4067

4068
            sql += `, ${checksSql}`
×
4069
        }
4070

4071
        if (table.exclusions.length > 0) {
×
4072
            const exclusionsSql = table.exclusions
×
4073
                .map((exclusion) => {
4074
                    const exclusionName = exclusion.name
×
4075
                        ? exclusion.name
4076
                        : this.connection.namingStrategy.exclusionConstraintName(
4077
                              table,
4078
                              exclusion.expression!,
4079
                          )
4080
                    return `CONSTRAINT "${exclusionName}" EXCLUDE ${exclusion.expression}`
×
4081
                })
4082
                .join(", ")
4083

4084
            sql += `, ${exclusionsSql}`
×
4085
        }
4086

4087
        if (table.foreignKeys.length > 0 && createForeignKeys) {
×
4088
            const foreignKeysSql = table.foreignKeys
×
4089
                .map((fk) => {
4090
                    const columnNames = fk.columnNames
×
4091
                        .map((columnName) => `"${columnName}"`)
×
4092
                        .join(", ")
4093
                    if (!fk.name)
×
4094
                        fk.name = this.connection.namingStrategy.foreignKeyName(
×
4095
                            table,
4096
                            fk.columnNames,
4097
                            this.getTablePath(fk),
4098
                            fk.referencedColumnNames,
4099
                        )
4100

4101
                    const referencedColumnNames = fk.referencedColumnNames
×
4102
                        .map((columnName) => `"${columnName}"`)
×
4103
                        .join(", ")
4104

4105
                    let constraint = `CONSTRAINT "${
×
4106
                        fk.name
4107
                    }" FOREIGN KEY (${columnNames}) REFERENCES ${this.escapePath(
4108
                        this.getTablePath(fk),
4109
                    )} (${referencedColumnNames})`
4110
                    if (fk.onDelete) constraint += ` ON DELETE ${fk.onDelete}`
×
4111
                    if (fk.onUpdate) constraint += ` ON UPDATE ${fk.onUpdate}`
×
4112
                    if (fk.deferrable)
×
4113
                        constraint += ` DEFERRABLE ${fk.deferrable}`
×
4114

4115
                    return constraint
×
4116
                })
4117
                .join(", ")
4118

4119
            sql += `, ${foreignKeysSql}`
×
4120
        }
4121

4122
        const primaryColumns = table.columns.filter(
×
4123
            (column) => column.isPrimary,
×
4124
        )
4125
        if (primaryColumns.length > 0) {
×
4126
            const primaryKeyName = primaryColumns[0].primaryKeyConstraintName
×
4127
                ? primaryColumns[0].primaryKeyConstraintName
4128
                : this.connection.namingStrategy.primaryKeyName(
4129
                      table,
4130
                      primaryColumns.map((column) => column.name),
×
4131
                  )
4132

4133
            const columnNames = primaryColumns
×
4134
                .map((column) => `"${column.name}"`)
×
4135
                .join(", ")
4136
            sql += `, CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNames})`
×
4137
        }
4138

4139
        sql += `)`
×
4140

4141
        table.columns
×
4142
            .filter((it) => it.comment)
×
4143
            .forEach(
4144
                (it) =>
4145
                    (sql += `; COMMENT ON COLUMN ${this.escapePath(table)}."${
×
4146
                        it.name
4147
                    }" IS ${this.escapeComment(it.comment)}`),
4148
            )
4149

4150
        return new Query(sql)
×
4151
    }
4152

4153
    /**
4154
     * Loads Postgres version.
4155
     */
4156
    async getVersion(): Promise<string> {
4157
        // we use `SELECT version()` instead of `SHOW server_version` or `SHOW server_version_num`
4158
        // to maintain compatability with Amazon Redshift.
4159
        //
4160
        // see:
4161
        //  - https://github.com/typeorm/typeorm/pull/9319
4162
        //  - https://docs.aws.amazon.com/redshift/latest/dg/c_unsupported-postgresql-functions.html
4163
        const result: [{ version: string }] = await this.query(
×
4164
            `SELECT version()`,
4165
        )
4166

4167
        // Examples:
4168
        // 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"
4169
        // 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"
4170
        return result[0].version.replace(/^PostgreSQL ([\d.]+).*$/, "$1")
×
4171
    }
4172

4173
    /**
4174
     * Builds drop table sql.
4175
     */
4176
    protected dropTableSql(tableOrPath: Table | string): Query {
4177
        return new Query(`DROP TABLE ${this.escapePath(tableOrPath)}`)
×
4178
    }
4179

4180
    protected createViewSql(view: View): Query {
4181
        const materializedClause = view.materialized ? "MATERIALIZED " : ""
×
4182
        const viewName = this.escapePath(view)
×
4183

4184
        if (typeof view.expression === "string") {
×
4185
            return new Query(
×
4186
                `CREATE ${materializedClause}VIEW ${viewName} AS ${view.expression}`,
4187
            )
4188
        } else {
4189
            return new Query(
×
4190
                `CREATE ${materializedClause}VIEW ${viewName} AS ${view
4191
                    .expression(this.connection)
4192
                    .getQuery()}`,
4193
            )
4194
        }
4195
    }
4196

4197
    protected async insertViewDefinitionSql(view: View): Promise<Query> {
4198
        const currentSchema = await this.getCurrentSchema()
×
4199

4200
        let { schema, tableName: name } = this.driver.parseTableName(view)
×
4201

4202
        if (!schema) {
×
4203
            schema = currentSchema
×
4204
        }
4205

4206
        const type = view.materialized
×
4207
            ? MetadataTableType.MATERIALIZED_VIEW
4208
            : MetadataTableType.VIEW
4209
        const expression =
4210
            typeof view.expression === "string"
×
4211
                ? view.expression.trim()
4212
                : view.expression(this.connection).getQuery()
4213
        return this.insertTypeormMetadataSql({
×
4214
            type,
4215
            schema,
4216
            name,
4217
            value: expression,
4218
        })
4219
    }
4220

4221
    /**
4222
     * Builds drop view sql.
4223
     */
4224
    protected dropViewSql(view: View): Query {
4225
        const materializedClause = view.materialized ? "MATERIALIZED " : ""
×
4226
        return new Query(
×
4227
            `DROP ${materializedClause}VIEW ${this.escapePath(view)}`,
4228
        )
4229
    }
4230

4231
    /**
4232
     * Builds remove view sql.
4233
     */
4234
    protected async deleteViewDefinitionSql(view: View): Promise<Query> {
4235
        const currentSchema = await this.getCurrentSchema()
×
4236

4237
        let { schema, tableName: name } = this.driver.parseTableName(view)
×
4238

4239
        if (!schema) {
×
4240
            schema = currentSchema
×
4241
        }
4242

4243
        const type = view.materialized
×
4244
            ? MetadataTableType.MATERIALIZED_VIEW
4245
            : MetadataTableType.VIEW
4246
        return this.deleteTypeormMetadataSql({ type, schema, name })
×
4247
    }
4248

4249
    /**
4250
     * Drops ENUM type from given schemas.
4251
     */
4252
    protected async dropEnumTypes(schemaNames: string): Promise<void> {
4253
        const selectDropsQuery =
4254
            `SELECT 'DROP TYPE IF EXISTS "' || n.nspname || '"."' || t.typname || '" CASCADE;' as "query" FROM "pg_type" "t" ` +
×
4255
            `INNER JOIN "pg_enum" "e" ON "e"."enumtypid" = "t"."oid" ` +
4256
            `INNER JOIN "pg_namespace" "n" ON "n"."oid" = "t"."typnamespace" ` +
4257
            `WHERE "n"."nspname" IN (${schemaNames}) GROUP BY "n"."nspname", "t"."typname"`
4258
        const dropQueries: ObjectLiteral[] = await this.query(selectDropsQuery)
×
4259
        await Promise.all(dropQueries.map((q) => this.query(q["query"])))
×
4260
    }
4261

4262
    /**
4263
     * Checks if enum with the given name exist in the database.
4264
     */
4265
    protected async hasEnumType(
4266
        table: Table,
4267
        column: TableColumn,
4268
    ): Promise<boolean> {
4269
        let { schema } = this.driver.parseTableName(table)
×
4270

4271
        if (!schema) {
×
4272
            schema = await this.getCurrentSchema()
×
4273
        }
4274

4275
        const enumName = this.buildEnumName(table, column, false, true)
×
4276
        const sql =
4277
            `SELECT "n"."nspname", "t"."typname" FROM "pg_type" "t" ` +
×
4278
            `INNER JOIN "pg_namespace" "n" ON "n"."oid" = "t"."typnamespace" ` +
4279
            `WHERE "n"."nspname" = '${schema}' AND "t"."typname" = '${enumName}'`
4280
        const result = await this.query(sql)
×
4281
        return result.length ? true : false
×
4282
    }
4283

4284
    /**
4285
     * Builds create ENUM type sql.
4286
     */
4287
    protected createEnumTypeSql(
4288
        table: Table,
4289
        column: TableColumn,
4290
        enumName?: string,
4291
    ): Query {
4292
        if (!enumName) enumName = this.buildEnumName(table, column)
×
4293
        const enumValues = column
×
4294
            .enum!.map((value) => `'${value.replaceAll("'", "''")}'`)
×
4295
            .join(", ")
4296
        return new Query(`CREATE TYPE ${enumName} AS ENUM(${enumValues})`)
×
4297
    }
4298

4299
    /**
4300
     * Builds create ENUM type sql.
4301
     */
4302
    protected dropEnumTypeSql(
4303
        table: Table,
4304
        column: TableColumn,
4305
        enumName?: string,
4306
    ): Query {
4307
        if (!enumName) enumName = this.buildEnumName(table, column)
×
4308
        return new Query(`DROP TYPE ${enumName}`)
×
4309
    }
4310

4311
    /**
4312
     * Builds create index sql.
4313
     */
4314
    protected createIndexSql(table: Table, index: TableIndex): Query {
4315
        const columns = index.columnNames
×
4316
            .map((columnName) => `"${columnName}"`)
×
4317
            .join(", ")
4318
        return new Query(
×
4319
            `CREATE ${index.isUnique ? "UNIQUE " : ""}INDEX${
×
4320
                index.isConcurrent ? " CONCURRENTLY" : ""
×
4321
            } "${index.name}" ON ${this.escapePath(table)} ${
4322
                index.isSpatial ? "USING GiST " : ""
×
4323
            }(${columns}) ${index.where ? "WHERE " + index.where : ""}`,
×
4324
        )
4325
    }
4326

4327
    /**
4328
     * Builds create view index sql.
4329
     */
4330
    protected createViewIndexSql(view: View, index: TableIndex): Query {
4331
        const columns = index.columnNames
×
4332
            .map((columnName) => `"${columnName}"`)
×
4333
            .join(", ")
4334
        return new Query(
×
4335
            `CREATE ${index.isUnique ? "UNIQUE " : ""}INDEX "${
×
4336
                index.name
4337
            }" ON ${this.escapePath(view)} (${columns}) ${
4338
                index.where ? "WHERE " + index.where : ""
×
4339
            }`,
4340
        )
4341
    }
4342

4343
    /**
4344
     * Builds drop index sql.
4345
     */
4346
    protected dropIndexSql(
4347
        table: Table | View,
4348
        indexOrName: TableIndex | string,
4349
    ): Query {
4350
        const indexName = InstanceChecker.isTableIndex(indexOrName)
×
4351
            ? indexOrName.name
4352
            : indexOrName
4353
        const concurrent = InstanceChecker.isTableIndex(indexOrName)
×
4354
            ? indexOrName.isConcurrent
4355
            : false
4356
        const { schema } = this.driver.parseTableName(table)
×
4357
        return schema
×
4358
            ? new Query(
4359
                  `DROP INDEX ${
4360
                      concurrent ? "CONCURRENTLY " : ""
×
4361
                  }"${schema}"."${indexName}"`,
4362
              )
4363
            : new Query(
4364
                  `DROP INDEX ${
4365
                      concurrent ? "CONCURRENTLY " : ""
×
4366
                  }"${indexName}"`,
4367
              )
4368
    }
4369

4370
    /**
4371
     * Builds create primary key sql.
4372
     */
4373
    protected createPrimaryKeySql(
4374
        table: Table,
4375
        columnNames: string[],
4376
        constraintName?: string,
4377
    ): Query {
4378
        const primaryKeyName = constraintName
×
4379
            ? constraintName
4380
            : this.connection.namingStrategy.primaryKeyName(table, columnNames)
4381

4382
        const columnNamesString = columnNames
×
4383
            .map((columnName) => `"${columnName}"`)
×
4384
            .join(", ")
4385

4386
        return new Query(
×
4387
            `ALTER TABLE ${this.escapePath(
4388
                table,
4389
            )} ADD CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNamesString})`,
4390
        )
4391
    }
4392

4393
    /**
4394
     * Builds drop primary key sql.
4395
     */
4396
    protected dropPrimaryKeySql(table: Table): Query {
4397
        if (!table.primaryColumns.length)
×
4398
            throw new TypeORMError(`Table ${table} has no primary keys.`)
×
4399

4400
        const columnNames = table.primaryColumns.map((column) => column.name)
×
4401
        const constraintName = table.primaryColumns[0].primaryKeyConstraintName
×
4402
        const primaryKeyName = constraintName
×
4403
            ? constraintName
4404
            : this.connection.namingStrategy.primaryKeyName(table, columnNames)
4405

4406
        return new Query(
×
4407
            `ALTER TABLE ${this.escapePath(
4408
                table,
4409
            )} DROP CONSTRAINT "${primaryKeyName}"`,
4410
        )
4411
    }
4412

4413
    /**
4414
     * Builds create unique constraint sql.
4415
     */
4416
    protected createUniqueConstraintSql(
4417
        table: Table,
4418
        uniqueConstraint: TableUnique,
4419
    ): Query {
4420
        const columnNames = uniqueConstraint.columnNames
×
4421
            .map((column) => `"` + column + `"`)
×
4422
            .join(", ")
4423
        let sql = `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
×
4424
            uniqueConstraint.name
4425
        }" UNIQUE (${columnNames})`
4426
        if (uniqueConstraint.deferrable)
×
4427
            sql += ` DEFERRABLE ${uniqueConstraint.deferrable}`
×
4428
        return new Query(sql)
×
4429
    }
4430

4431
    /**
4432
     * Builds drop unique constraint sql.
4433
     */
4434
    protected dropUniqueConstraintSql(
4435
        table: Table,
4436
        uniqueOrName: TableUnique | string,
4437
    ): Query {
4438
        const uniqueName = InstanceChecker.isTableUnique(uniqueOrName)
×
4439
            ? uniqueOrName.name
4440
            : uniqueOrName
4441
        return new Query(
×
4442
            `ALTER TABLE ${this.escapePath(
4443
                table,
4444
            )} DROP CONSTRAINT "${uniqueName}"`,
4445
        )
4446
    }
4447

4448
    /**
4449
     * Builds create check constraint sql.
4450
     */
4451
    protected createCheckConstraintSql(
4452
        table: Table,
4453
        checkConstraint: TableCheck,
4454
    ): Query {
4455
        return new Query(
×
4456
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
4457
                checkConstraint.name
4458
            }" CHECK (${checkConstraint.expression})`,
4459
        )
4460
    }
4461

4462
    /**
4463
     * Builds drop check constraint sql.
4464
     */
4465
    protected dropCheckConstraintSql(
4466
        table: Table,
4467
        checkOrName: TableCheck | string,
4468
    ): Query {
4469
        const checkName = InstanceChecker.isTableCheck(checkOrName)
×
4470
            ? checkOrName.name
4471
            : checkOrName
4472
        return new Query(
×
4473
            `ALTER TABLE ${this.escapePath(
4474
                table,
4475
            )} DROP CONSTRAINT "${checkName}"`,
4476
        )
4477
    }
4478

4479
    /**
4480
     * Builds create exclusion constraint sql.
4481
     */
4482
    protected createExclusionConstraintSql(
4483
        table: Table,
4484
        exclusionConstraint: TableExclusion,
4485
    ): Query {
4486
        return new Query(
×
4487
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
4488
                exclusionConstraint.name
4489
            }" EXCLUDE ${exclusionConstraint.expression}`,
4490
        )
4491
    }
4492

4493
    /**
4494
     * Builds drop exclusion constraint sql.
4495
     */
4496
    protected dropExclusionConstraintSql(
4497
        table: Table,
4498
        exclusionOrName: TableExclusion | string,
4499
    ): Query {
4500
        const exclusionName = InstanceChecker.isTableExclusion(exclusionOrName)
×
4501
            ? exclusionOrName.name
4502
            : exclusionOrName
4503
        return new Query(
×
4504
            `ALTER TABLE ${this.escapePath(
4505
                table,
4506
            )} DROP CONSTRAINT "${exclusionName}"`,
4507
        )
4508
    }
4509

4510
    /**
4511
     * Builds create foreign key sql.
4512
     */
4513
    protected createForeignKeySql(
4514
        table: Table,
4515
        foreignKey: TableForeignKey,
4516
    ): Query {
4517
        const columnNames = foreignKey.columnNames
×
4518
            .map((column) => `"` + column + `"`)
×
4519
            .join(", ")
4520
        const referencedColumnNames = foreignKey.referencedColumnNames
×
4521
            .map((column) => `"` + column + `"`)
×
4522
            .join(",")
4523
        let sql =
4524
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
×
4525
                foreignKey.name
4526
            }" FOREIGN KEY (${columnNames}) ` +
4527
            `REFERENCES ${this.escapePath(
4528
                this.getTablePath(foreignKey),
4529
            )}(${referencedColumnNames})`
4530
        if (foreignKey.onDelete) sql += ` ON DELETE ${foreignKey.onDelete}`
×
4531
        if (foreignKey.onUpdate) sql += ` ON UPDATE ${foreignKey.onUpdate}`
×
4532
        if (foreignKey.deferrable) sql += ` DEFERRABLE ${foreignKey.deferrable}`
×
4533

4534
        return new Query(sql)
×
4535
    }
4536

4537
    /**
4538
     * Builds drop foreign key sql.
4539
     */
4540
    protected dropForeignKeySql(
4541
        table: Table,
4542
        foreignKeyOrName: TableForeignKey | string,
4543
    ): Query {
4544
        const foreignKeyName = InstanceChecker.isTableForeignKey(
×
4545
            foreignKeyOrName,
4546
        )
4547
            ? foreignKeyOrName.name
4548
            : foreignKeyOrName
4549
        return new Query(
×
4550
            `ALTER TABLE ${this.escapePath(
4551
                table,
4552
            )} DROP CONSTRAINT "${foreignKeyName}"`,
4553
        )
4554
    }
4555

4556
    /**
4557
     * Builds sequence name from given table and column.
4558
     */
4559
    protected buildSequenceName(
4560
        table: Table,
4561
        columnOrName: TableColumn | string,
4562
    ): string {
4563
        const { tableName } = this.driver.parseTableName(table)
×
4564

4565
        const columnName = InstanceChecker.isTableColumn(columnOrName)
×
4566
            ? columnOrName.name
4567
            : columnOrName
4568

4569
        let seqName = `${tableName}_${columnName}_seq`
×
4570

4571
        if (seqName.length > this.connection.driver.maxAliasLength!) {
×
4572
            // note doesn't yet handle corner cases where .length differs from number of UTF-8 bytes
4573
            seqName = `${tableName.substring(0, 29)}_${columnName.substring(
×
4574
                0,
4575
                Math.max(29, 63 - table.name.length - 5),
4576
            )}_seq`
4577
        }
4578

4579
        return seqName
×
4580
    }
4581

4582
    protected buildSequencePath(
4583
        table: Table,
4584
        columnOrName: TableColumn | string,
4585
    ): string {
4586
        const { schema } = this.driver.parseTableName(table)
×
4587

4588
        return schema
×
4589
            ? `${schema}.${this.buildSequenceName(table, columnOrName)}`
4590
            : this.buildSequenceName(table, columnOrName)
4591
    }
4592

4593
    /**
4594
     * Builds ENUM type name from given table and column.
4595
     */
4596
    protected buildEnumName(
4597
        table: Table,
4598
        column: TableColumn,
4599
        withSchema: boolean = true,
×
4600
        disableEscape?: boolean,
4601
        toOld?: boolean,
4602
    ): string {
4603
        const { schema, tableName } = this.driver.parseTableName(table)
×
4604
        let enumName = column.enumName
×
4605
            ? column.enumName
4606
            : `${tableName}_${column.name.toLowerCase()}_enum`
4607
        if (schema && withSchema) enumName = `${schema}.${enumName}`
×
4608
        if (toOld) enumName = enumName + "_old"
×
4609
        return enumName
×
4610
            .split(".")
4611
            .map((i) => {
4612
                return disableEscape ? i : `"${i}"`
×
4613
            })
4614
            .join(".")
4615
    }
4616

4617
    protected async getUserDefinedTypeName(table: Table, column: TableColumn) {
4618
        let { schema, tableName: name } = this.driver.parseTableName(table)
×
4619

4620
        if (!schema) {
×
4621
            schema = await this.getCurrentSchema()
×
4622
        }
4623

4624
        const result = await this.query(
×
4625
            `SELECT "udt_schema", "udt_name" ` +
4626
                `FROM "information_schema"."columns" WHERE "table_schema" = '${schema}' AND "table_name" = '${name}' AND "column_name"='${column.name}'`,
4627
        )
4628

4629
        // docs: https://www.postgresql.org/docs/current/xtypes.html
4630
        // When you define a new base type, PostgreSQL automatically provides support for arrays of that type.
4631
        // The array type typically has the same name as the base type with the underscore character (_) prepended.
4632
        // ----
4633
        // so, we must remove this underscore character from enum type name
4634
        let udtName = result[0]["udt_name"]
×
4635
        if (udtName.indexOf("_") === 0) {
×
4636
            udtName = udtName.substr(1, udtName.length)
×
4637
        }
4638
        return {
×
4639
            schema: result[0]["udt_schema"],
4640
            name: udtName,
4641
        }
4642
    }
4643

4644
    /**
4645
     * Escapes a given comment so it's safe to include in a query.
4646
     */
4647
    protected escapeComment(comment?: string) {
4648
        if (!comment || comment.length === 0) {
×
4649
            return "NULL"
×
4650
        }
4651

4652
        comment = comment.replace(/'/g, "''").replace(/\u0000/g, "") // Null bytes aren't allowed in comments
×
4653

4654
        return `'${comment}'`
×
4655
    }
4656

4657
    /**
4658
     * Escapes given table or view path.
4659
     */
4660
    protected escapePath(target: Table | View | string): string {
4661
        const { schema, tableName } = this.driver.parseTableName(target)
×
4662

4663
        if (schema && schema !== this.driver.searchSchema) {
×
4664
            return `"${schema}"."${tableName}"`
×
4665
        }
4666

4667
        return `"${tableName}"`
×
4668
    }
4669

4670
    /**
4671
     * Get the table name with table schema
4672
     * Note: Without ' or "
4673
     */
4674
    protected async getTableNameWithSchema(target: Table | string) {
4675
        const tableName = InstanceChecker.isTable(target) ? target.name : target
×
4676
        if (tableName.indexOf(".") === -1) {
×
4677
            const schemaResult = await this.query(`SELECT current_schema()`)
×
4678
            const schema = schemaResult[0]["current_schema"]
×
4679
            return `${schema}.${tableName}`
×
4680
        } else {
4681
            return `${tableName.split(".")[0]}.${tableName.split(".")[1]}`
×
4682
        }
4683
    }
4684

4685
    /**
4686
     * Builds a query for create column.
4687
     */
4688
    protected buildCreateColumnSql(table: Table, column: TableColumn) {
4689
        let c = '"' + column.name + '"'
×
4690
        if (
×
4691
            column.isGenerated === true &&
×
4692
            column.generationStrategy !== "uuid"
4693
        ) {
4694
            if (column.generationStrategy === "identity") {
×
4695
                // Postgres 10+ Identity generated column
4696
                const generatedIdentityOrDefault =
4697
                    column.generatedIdentity || "BY DEFAULT"
×
4698
                c += ` ${column.type} GENERATED ${generatedIdentityOrDefault} AS IDENTITY`
×
4699
            } else {
4700
                // classic SERIAL primary column
4701
                if (
×
4702
                    column.type === "integer" ||
×
4703
                    column.type === "int" ||
4704
                    column.type === "int4"
4705
                )
4706
                    c += " SERIAL"
×
4707
                if (column.type === "smallint" || column.type === "int2")
×
4708
                    c += " SMALLSERIAL"
×
4709
                if (column.type === "bigint" || column.type === "int8")
×
4710
                    c += " BIGSERIAL"
×
4711
            }
4712
        }
4713
        if (column.type === "enum" || column.type === "simple-enum") {
×
4714
            c += " " + this.buildEnumName(table, column)
×
4715
            if (column.isArray) c += " array"
×
4716
        } else if (!column.isGenerated || column.type === "uuid") {
×
4717
            c += " " + this.connection.driver.createFullType(column)
×
4718
        }
4719

4720
        // Postgres only supports the stored generated column type
4721
        if (column.generatedType === "STORED" && column.asExpression) {
×
4722
            c += ` GENERATED ALWAYS AS (${column.asExpression}) STORED`
×
4723
        }
4724

4725
        if (column.charset) c += ' CHARACTER SET "' + column.charset + '"'
×
4726
        if (column.collation) c += ' COLLATE "' + column.collation + '"'
×
4727
        if (column.isNullable !== true) c += " NOT NULL"
×
4728
        if (column.default !== undefined && column.default !== null)
×
4729
            c += " DEFAULT " + column.default
×
4730
        if (
×
4731
            column.isGenerated &&
×
4732
            column.generationStrategy === "uuid" &&
4733
            !column.default
4734
        )
4735
            c += ` DEFAULT ${this.driver.uuidGenerator}`
×
4736

4737
        return c
×
4738
    }
4739

4740
    /**
4741
     * Checks if the PostgreSQL server has support for partitioned tables
4742
     */
4743
    protected async hasSupportForPartitionedTables() {
4744
        const result = await this.query(
×
4745
            `SELECT TRUE FROM information_schema.columns WHERE table_name = 'pg_class' and column_name = 'relispartition'`,
4746
        )
4747
        return result.length ? true : false
×
4748
    }
4749

4750
    /**
4751
     * Change table comment.
4752
     */
4753
    async changeTableComment(
4754
        tableOrName: Table | string,
4755
        newComment?: string,
4756
    ): Promise<void> {
4757
        const upQueries: Query[] = []
×
4758
        const downQueries: Query[] = []
×
4759

4760
        const table = InstanceChecker.isTable(tableOrName)
×
4761
            ? tableOrName
4762
            : await this.getCachedTable(tableOrName)
4763

4764
        newComment = this.escapeComment(newComment)
×
4765
        const comment = this.escapeComment(table.comment)
×
4766

4767
        if (newComment === comment) {
×
4768
            return
×
4769
        }
4770

4771
        const newTable = table.clone()
×
4772

4773
        upQueries.push(
×
4774
            new Query(
4775
                `COMMENT ON TABLE ${this.escapePath(
4776
                    newTable,
4777
                )} IS ${newComment}`,
4778
            ),
4779
        )
4780

4781
        downQueries.push(
×
4782
            new Query(
4783
                `COMMENT ON TABLE ${this.escapePath(table)} IS ${comment}`,
4784
            ),
4785
        )
4786

4787
        await this.executeQueries(upQueries, downQueries)
×
4788

4789
        table.comment = newTable.comment
×
4790
        this.replaceCachedTable(table, newTable)
×
4791
    }
4792
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc