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

typeorm / typeorm / 22160751351

18 Feb 2026 10:46PM UTC coverage: 81.182% (+0.02%) from 81.16%
22160751351

push

github

web-flow
feat: add support for table comments in SAP HANA (#11939)

27477 of 33302 branches covered (82.51%)

Branch coverage included in aggregate %.

183 of 192 new or added lines in 4 files covered. (95.31%)

2 existing lines in 2 files now uncovered.

93413 of 115610 relevant lines covered (80.8%)

71337.57 hits per line

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

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

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

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

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

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

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

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

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

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

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

57,968✔
86
        if (this.databaseConnectionPromise)
57,968✔
87
            return this.databaseConnectionPromise
323,412✔
88

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

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

12✔
107
                    return this.databaseConnection
12✔
108
                })
12✔
109
        } else {
323,412✔
110
            // master
57,700✔
111
            this.databaseConnectionPromise = this.driver
57,700✔
112
                .obtainMasterConnection()
57,700✔
113
                .then(([connection, release]: any[]) => {
57,700✔
114
                    this.driver.connectedQueryRunners.push(this)
57,696✔
115
                    this.databaseConnection = connection
57,696✔
116

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

57,696✔
128
                    return this.databaseConnection
57,696✔
129
                })
57,700✔
130
        }
57,700✔
131

57,712✔
132
        return this.databaseConnectionPromise
57,712✔
133
    }
57,712✔
134

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

60,476✔
145
        this.isReleased = true
60,476✔
146
        if (this.releaseCallback) {
60,476✔
147
            this.releaseCallback(err)
57,708✔
148
            this.releaseCallback = undefined
57,708✔
149
        }
57,708✔
150

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

60,476✔
153
        if (index !== -1) {
60,476✔
154
            this.driver.connectedQueryRunners.splice(index, 1)
57,708✔
155
        }
57,708✔
156
    }
60,476✔
157

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

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

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

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

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

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

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

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

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

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

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

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

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

323,380✔
251
        const databaseConnection = await this.connect()
323,380✔
252

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

323,376✔
256
        const broadcasterResult = new BroadcasterResult()
323,376✔
257

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

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

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

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

323,300✔
294
                if (raw.hasOwnProperty("rowCount")) {
323,300✔
295
                    result.affected = raw.rowCount
323,152✔
296
                }
323,152✔
297

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

323,300✔
308
                if (!useStructuredResult) {
323,300✔
309
                    return result.raw
255,264✔
310
                }
255,264✔
311
            }
323,300✔
312

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

76✔
331
            throw new QueryFailedError(query, parameters, err)
76✔
332
        } finally {
323,380!
333
            await broadcasterResult.wait()
323,376✔
334
        }
323,376✔
335
    }
323,380✔
336

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

26✔
742
    /**
26✔
743
     * Drops the view.
26✔
744
     * @param target
26✔
745
     */
26✔
746
    async dropView(target: View | string): Promise<void> {
26✔
747
        const viewName = InstanceChecker.isView(target) ? target.name : target
20!
748
        const view = await this.getCachedView(viewName)
20✔
749

20✔
750
        const upQueries: Query[] = []
20✔
751
        const downQueries: Query[] = []
20✔
752
        upQueries.push(await this.deleteViewDefinitionSql(view))
20✔
753
        upQueries.push(this.dropViewSql(view))
20✔
754
        downQueries.push(await this.insertViewDefinitionSql(view))
20✔
755
        downQueries.push(this.createViewSql(view))
20✔
756
        await this.executeQueries(upQueries, downQueries)
20✔
757
    }
20✔
758

26✔
759
    /**
26✔
760
     * Renames the given table.
26✔
761
     * @param oldTableOrName
26✔
762
     * @param newTableName
26✔
763
     */
26✔
764
    async renameTable(
26✔
765
        oldTableOrName: Table | string,
76✔
766
        newTableName: string,
76✔
767
    ): Promise<void> {
76✔
768
        const upQueries: Query[] = []
76✔
769
        const downQueries: Query[] = []
76✔
770
        const oldTable = InstanceChecker.isTable(oldTableOrName)
76✔
771
            ? oldTableOrName
76✔
772
            : await this.getCachedTable(oldTableOrName)
76✔
773
        const newTable = oldTable.clone()
60✔
774

60✔
775
        const { schema: schemaName, tableName: oldTableName } =
60✔
776
            this.driver.parseTableName(oldTable)
60✔
777

60✔
778
        newTable.name = schemaName
60✔
779
            ? `${schemaName}.${newTableName}`
76✔
780
            : newTableName
76!
781

76✔
782
        upQueries.push(
76✔
783
            new Query(
76✔
784
                `ALTER TABLE ${this.escapePath(
76✔
785
                    oldTable,
76✔
786
                )} RENAME TO "${newTableName}"`,
76✔
787
            ),
76✔
788
        )
76✔
789
        downQueries.push(
76✔
790
            new Query(
76✔
791
                `ALTER TABLE ${this.escapePath(
76✔
792
                    newTable,
76✔
793
                )} RENAME TO "${oldTableName}"`,
76✔
794
            ),
76✔
795
        )
76✔
796

76✔
797
        // rename column primary key constraint if it has default constraint name
76✔
798
        if (
76✔
799
            newTable.primaryColumns.length > 0 &&
76✔
800
            !newTable.primaryColumns[0].primaryKeyConstraintName
76✔
801
        ) {
76✔
802
            const columnNames = newTable.primaryColumns.map(
60✔
803
                (column) => column.name,
60✔
804
            )
60✔
805

60✔
806
            const oldPkName = this.connection.namingStrategy.primaryKeyName(
60✔
807
                oldTable,
60✔
808
                columnNames,
60✔
809
            )
60✔
810

60✔
811
            const newPkName = this.connection.namingStrategy.primaryKeyName(
60✔
812
                newTable,
60✔
813
                columnNames,
60✔
814
            )
60✔
815

60✔
816
            upQueries.push(
60✔
817
                new Query(
60✔
818
                    `ALTER TABLE ${this.escapePath(
60✔
819
                        newTable,
60✔
820
                    )} RENAME CONSTRAINT "${oldPkName}" TO "${newPkName}"`,
60✔
821
                ),
60✔
822
            )
60✔
823
            downQueries.push(
60✔
824
                new Query(
60✔
825
                    `ALTER TABLE ${this.escapePath(
60✔
826
                        newTable,
60✔
827
                    )} RENAME CONSTRAINT "${newPkName}" TO "${oldPkName}"`,
60✔
828
                ),
60✔
829
            )
60✔
830
        }
60✔
831

76✔
832
        // rename sequences
76✔
833
        newTable.columns.map((col) => {
76✔
834
            if (col.isGenerated && col.generationStrategy === "increment") {
192✔
835
                const sequencePath = this.buildSequencePath(oldTable, col.name)
52✔
836
                const sequenceName = this.buildSequenceName(oldTable, col.name)
52✔
837

52✔
838
                const newSequencePath = this.buildSequencePath(
52✔
839
                    newTable,
52✔
840
                    col.name,
52✔
841
                )
52✔
842
                const newSequenceName = this.buildSequenceName(
52✔
843
                    newTable,
52✔
844
                    col.name,
52✔
845
                )
52✔
846

52✔
847
                const up = `ALTER SEQUENCE ${this.escapePath(
52✔
848
                    sequencePath,
52✔
849
                )} RENAME TO "${newSequenceName}"`
52✔
850
                const down = `ALTER SEQUENCE ${this.escapePath(
52✔
851
                    newSequencePath,
52✔
852
                )} RENAME TO "${sequenceName}"`
52✔
853

52✔
854
                upQueries.push(new Query(up))
52✔
855
                downQueries.push(new Query(down))
52✔
856
            }
52✔
857
        })
76✔
858

76✔
859
        // rename unique constraints
76✔
860
        newTable.uniques.forEach((unique) => {
76✔
861
            const oldUniqueName =
28✔
862
                this.connection.namingStrategy.uniqueConstraintName(
28✔
863
                    oldTable,
28✔
864
                    unique.columnNames,
28✔
865
                )
28✔
866

28✔
867
            // Skip renaming if Unique has user defined constraint name
28✔
868
            if (unique.name !== oldUniqueName) return
28✔
869

12✔
870
            // build new constraint name
12✔
871
            const newUniqueName =
12✔
872
                this.connection.namingStrategy.uniqueConstraintName(
12✔
873
                    newTable,
12✔
874
                    unique.columnNames,
12✔
875
                )
12✔
876

12✔
877
            // build queries
12✔
878
            upQueries.push(
12✔
879
                new Query(
12✔
880
                    `ALTER TABLE ${this.escapePath(
12✔
881
                        newTable,
12✔
882
                    )} RENAME CONSTRAINT "${
12✔
883
                        unique.name
12✔
884
                    }" TO "${newUniqueName}"`,
12✔
885
                ),
12✔
886
            )
12✔
887
            downQueries.push(
12✔
888
                new Query(
12✔
889
                    `ALTER TABLE ${this.escapePath(
12✔
890
                        newTable,
12✔
891
                    )} RENAME CONSTRAINT "${newUniqueName}" TO "${
12✔
892
                        unique.name
12✔
893
                    }"`,
12✔
894
                ),
12✔
895
            )
12✔
896

12✔
897
            // replace constraint name
12✔
898
            unique.name = newUniqueName
12✔
899
        })
76✔
900

76✔
901
        // rename index constraints
76✔
902
        newTable.indices.forEach((index) => {
76✔
903
            const oldIndexName = this.connection.namingStrategy.indexName(
36✔
904
                oldTable,
36✔
905
                index.columnNames,
36✔
906
                index.where,
36✔
907
            )
36✔
908

36✔
909
            // Skip renaming if Index has user defined constraint name
36✔
910
            if (index.name !== oldIndexName) return
36✔
911

20✔
912
            // build new constraint name
20✔
913
            const { schema } = this.driver.parseTableName(newTable)
20✔
914
            const newIndexName = this.connection.namingStrategy.indexName(
20✔
915
                newTable,
20✔
916
                index.columnNames,
20✔
917
                index.where,
20✔
918
            )
20✔
919

20✔
920
            // build queries
20✔
921
            const up = schema
20✔
922
                ? `ALTER INDEX "${schema}"."${index.name}" RENAME TO "${newIndexName}"`
20✔
923
                : `ALTER INDEX "${index.name}" RENAME TO "${newIndexName}"`
36!
924
            const down = schema
36✔
925
                ? `ALTER INDEX "${schema}"."${newIndexName}" RENAME TO "${index.name}"`
36✔
926
                : `ALTER INDEX "${newIndexName}" RENAME TO "${index.name}"`
36!
927
            upQueries.push(new Query(up))
36✔
928
            downQueries.push(new Query(down))
36✔
929

36✔
930
            // replace constraint name
36✔
931
            index.name = newIndexName
36✔
932
        })
76✔
933

76✔
934
        // rename foreign key constraints
76✔
935
        newTable.foreignKeys.forEach((foreignKey) => {
76✔
936
            const oldForeignKeyName =
36✔
937
                this.connection.namingStrategy.foreignKeyName(
36✔
938
                    oldTable,
36✔
939
                    foreignKey.columnNames,
36✔
940
                    this.getTablePath(foreignKey),
36✔
941
                    foreignKey.referencedColumnNames,
36✔
942
                )
36✔
943

36✔
944
            // Skip renaming if foreign key has user defined constraint name
36✔
945
            if (foreignKey.name !== oldForeignKeyName) return
36✔
946

4✔
947
            // build new constraint name
4✔
948
            const newForeignKeyName =
4✔
949
                this.connection.namingStrategy.foreignKeyName(
4✔
950
                    newTable,
4✔
951
                    foreignKey.columnNames,
4✔
952
                    this.getTablePath(foreignKey),
4✔
953
                    foreignKey.referencedColumnNames,
4✔
954
                )
4✔
955

4✔
956
            // build queries
4✔
957
            upQueries.push(
4✔
958
                new Query(
4✔
959
                    `ALTER TABLE ${this.escapePath(
4✔
960
                        newTable,
4✔
961
                    )} RENAME CONSTRAINT "${
4✔
962
                        foreignKey.name
4✔
963
                    }" TO "${newForeignKeyName}"`,
4✔
964
                ),
4✔
965
            )
4✔
966
            downQueries.push(
4✔
967
                new Query(
4✔
968
                    `ALTER TABLE ${this.escapePath(
4✔
969
                        newTable,
4✔
970
                    )} RENAME CONSTRAINT "${newForeignKeyName}" TO "${
4✔
971
                        foreignKey.name
4✔
972
                    }"`,
4✔
973
                ),
4✔
974
            )
4✔
975

4✔
976
            // replace constraint name
4✔
977
            foreignKey.name = newForeignKeyName
4✔
978
        })
76✔
979

76✔
980
        // rename ENUM types
76✔
981
        const enumColumns = newTable.columns.filter(
76✔
982
            (column) => column.type === "enum" || column.type === "simple-enum",
76✔
983
        )
76✔
984
        for (const column of enumColumns) {
76✔
985
            // skip renaming for user-defined enum name
20✔
986
            if (column.enumName) continue
20✔
987

16✔
988
            const oldEnumType = await this.getUserDefinedTypeName(
16✔
989
                oldTable,
16✔
990
                column,
16✔
991
            )
16✔
992
            upQueries.push(
16✔
993
                new Query(
16✔
994
                    `ALTER TYPE "${oldEnumType.schema}"."${
16✔
995
                        oldEnumType.name
16✔
996
                    }" RENAME TO ${this.buildEnumName(
16✔
997
                        newTable,
16✔
998
                        column,
16✔
999
                        false,
16✔
1000
                    )}`,
16✔
1001
                ),
16✔
1002
            )
16✔
1003
            downQueries.push(
16✔
1004
                new Query(
16✔
1005
                    `ALTER TYPE ${this.buildEnumName(
16✔
1006
                        newTable,
16✔
1007
                        column,
16✔
1008
                    )} RENAME TO "${oldEnumType.name}"`,
16✔
1009
                ),
16✔
1010
            )
16✔
1011
        }
16✔
1012
        await this.executeQueries(upQueries, downQueries)
76✔
1013
    }
76✔
1014

26✔
1015
    /**
26✔
1016
     * Creates a new column from the column in the table.
26✔
1017
     * @param tableOrName
26✔
1018
     * @param column
26✔
1019
     */
26✔
1020
    async addColumn(
26✔
1021
        tableOrName: Table | string,
152✔
1022
        column: TableColumn,
152✔
1023
    ): Promise<void> {
152✔
1024
        const table = InstanceChecker.isTable(tableOrName)
152✔
1025
            ? tableOrName
152✔
1026
            : await this.getCachedTable(tableOrName)
152✔
1027
        const clonedTable = table.clone()
16✔
1028
        const upQueries: Query[] = []
16✔
1029
        const downQueries: Query[] = []
16✔
1030

16✔
1031
        if (column.type === "enum" || column.type === "simple-enum") {
152✔
1032
            const hasEnum = await this.hasEnumType(table, column)
20✔
1033
            if (!hasEnum) {
20✔
1034
                upQueries.push(this.createEnumTypeSql(table, column))
20✔
1035
                downQueries.push(this.dropEnumTypeSql(table, column))
20✔
1036
            }
20✔
1037
        }
20✔
1038

152✔
1039
        upQueries.push(
152✔
1040
            new Query(
152✔
1041
                `ALTER TABLE ${this.escapePath(
152✔
1042
                    table,
152✔
1043
                )} ADD ${this.buildCreateColumnSql(table, column)}`,
152✔
1044
            ),
152✔
1045
        )
152✔
1046
        downQueries.push(
152✔
1047
            new Query(
152✔
1048
                `ALTER TABLE ${this.escapePath(table)} DROP COLUMN "${
152✔
1049
                    column.name
152✔
1050
                }"`,
152✔
1051
            ),
152✔
1052
        )
152✔
1053

152✔
1054
        // create or update primary key constraint
152✔
1055
        if (column.isPrimary) {
152✔
1056
            const primaryColumns = clonedTable.primaryColumns
12✔
1057
            // if table already have primary key, me must drop it and recreate again
12✔
1058
            if (primaryColumns.length > 0) {
12✔
1059
                const pkName = primaryColumns[0].primaryKeyConstraintName
4✔
1060
                    ? primaryColumns[0].primaryKeyConstraintName
4!
1061
                    : this.connection.namingStrategy.primaryKeyName(
4✔
1062
                          clonedTable,
4✔
1063
                          primaryColumns.map((column) => column.name),
4✔
1064
                      )
4✔
1065

4✔
1066
                const columnNames = primaryColumns
4✔
1067
                    .map((column) => `"${column.name}"`)
4✔
1068
                    .join(", ")
4✔
1069

4✔
1070
                upQueries.push(
4✔
1071
                    new Query(
4✔
1072
                        `ALTER TABLE ${this.escapePath(
4✔
1073
                            table,
4✔
1074
                        )} DROP CONSTRAINT "${pkName}"`,
4✔
1075
                    ),
4✔
1076
                )
4✔
1077
                downQueries.push(
4✔
1078
                    new Query(
4✔
1079
                        `ALTER TABLE ${this.escapePath(
4✔
1080
                            table,
4✔
1081
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
4✔
1082
                    ),
4✔
1083
                )
4✔
1084
            }
4✔
1085

12✔
1086
            primaryColumns.push(column)
12✔
1087
            const pkName = primaryColumns[0].primaryKeyConstraintName
12✔
1088
                ? primaryColumns[0].primaryKeyConstraintName
12!
1089
                : this.connection.namingStrategy.primaryKeyName(
12✔
1090
                      clonedTable,
12✔
1091
                      primaryColumns.map((column) => column.name),
12✔
1092
                  )
12✔
1093

12✔
1094
            const columnNames = primaryColumns
12✔
1095
                .map((column) => `"${column.name}"`)
12✔
1096
                .join(", ")
12✔
1097

12✔
1098
            upQueries.push(
12✔
1099
                new Query(
12✔
1100
                    `ALTER TABLE ${this.escapePath(
12✔
1101
                        table,
12✔
1102
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
12✔
1103
                ),
12✔
1104
            )
12✔
1105
            downQueries.push(
12✔
1106
                new Query(
12✔
1107
                    `ALTER TABLE ${this.escapePath(
12✔
1108
                        table,
12✔
1109
                    )} DROP CONSTRAINT "${pkName}"`,
12✔
1110
                ),
12✔
1111
            )
12✔
1112
        }
12✔
1113

152✔
1114
        // create column index
152✔
1115
        const columnIndex = clonedTable.indices.find(
152✔
1116
            (index) =>
152✔
1117
                index.columnNames.length === 1 &&
4✔
1118
                index.columnNames[0] === column.name,
152✔
1119
        )
152✔
1120
        if (columnIndex) {
152!
1121
            upQueries.push(this.createIndexSql(table, columnIndex))
×
1122
            downQueries.push(this.dropIndexSql(table, columnIndex))
×
1123
        }
×
1124

152✔
1125
        // create unique constraint
152✔
1126
        if (column.isUnique) {
152✔
1127
            const uniqueConstraint = new TableUnique({
12✔
1128
                name: this.connection.namingStrategy.uniqueConstraintName(
12✔
1129
                    table,
12✔
1130
                    [column.name],
12✔
1131
                ),
12✔
1132
                columnNames: [column.name],
12✔
1133
            })
12✔
1134
            clonedTable.uniques.push(uniqueConstraint)
12✔
1135
            upQueries.push(
12✔
1136
                new Query(
12✔
1137
                    `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
12✔
1138
                        uniqueConstraint.name
12✔
1139
                    }" UNIQUE ("${column.name}")`,
12✔
1140
                ),
12✔
1141
            )
12✔
1142
            downQueries.push(
12✔
1143
                new Query(
12✔
1144
                    `ALTER TABLE ${this.escapePath(table)} DROP CONSTRAINT "${
12✔
1145
                        uniqueConstraint.name
12✔
1146
                    }"`,
12✔
1147
                ),
12✔
1148
            )
12✔
1149
        }
12✔
1150

152✔
1151
        if (column.generatedType === "STORED" && column.asExpression) {
152✔
1152
            const tableNameWithSchema = (
24✔
1153
                await this.getTableNameWithSchema(table.name)
24✔
1154
            ).split(".")
24✔
1155
            const tableName = tableNameWithSchema[1]
24✔
1156
            const schema = tableNameWithSchema[0]
24✔
1157

24✔
1158
            const insertQuery = this.insertTypeormMetadataSql({
24✔
1159
                database: this.driver.database,
24✔
1160
                schema,
24✔
1161
                table: tableName,
24✔
1162
                type: MetadataTableType.GENERATED_COLUMN,
24✔
1163
                name: column.name,
24✔
1164
                value: column.asExpression,
24✔
1165
            })
24✔
1166

24✔
1167
            const deleteQuery = this.deleteTypeormMetadataSql({
24✔
1168
                database: this.driver.database,
24✔
1169
                schema,
24✔
1170
                table: tableName,
24✔
1171
                type: MetadataTableType.GENERATED_COLUMN,
24✔
1172
                name: column.name,
24✔
1173
            })
24✔
1174

24✔
1175
            upQueries.push(insertQuery)
24✔
1176
            downQueries.push(deleteQuery)
24✔
1177
        }
24✔
1178

152✔
1179
        // create column's comment
152✔
1180
        if (column.comment) {
152!
1181
            upQueries.push(
×
1182
                new Query(
×
1183
                    `COMMENT ON COLUMN ${this.escapePath(table)}."${
×
1184
                        column.name
×
1185
                    }" IS ${this.escapeComment(column.comment)}`,
×
1186
                ),
×
1187
            )
×
1188
            downQueries.push(
×
1189
                new Query(
×
1190
                    `COMMENT ON COLUMN ${this.escapePath(table)}."${
×
1191
                        column.name
×
1192
                    }" IS ${this.escapeComment(column.comment)}`,
×
1193
                ),
×
1194
            )
×
1195
        }
×
1196

152✔
1197
        await this.executeQueries(upQueries, downQueries)
152✔
1198

148✔
1199
        clonedTable.addColumn(column)
148✔
1200
        this.replaceCachedTable(table, clonedTable)
148✔
1201
    }
148✔
1202

26✔
1203
    /**
26✔
1204
     * Creates a new columns from the column in the table.
26✔
1205
     * @param tableOrName
26✔
1206
     * @param columns
26✔
1207
     */
26✔
1208
    async addColumns(
26✔
1209
        tableOrName: Table | string,
16✔
1210
        columns: TableColumn[],
16✔
1211
    ): Promise<void> {
16✔
1212
        for (const column of columns) {
16✔
1213
            await this.addColumn(tableOrName, column)
20✔
1214
        }
20✔
1215
    }
16✔
1216

26✔
1217
    /**
26✔
1218
     * Renames column in the given table.
26✔
1219
     * @param tableOrName
26✔
1220
     * @param oldTableColumnOrName
26✔
1221
     * @param newTableColumnOrName
26✔
1222
     */
26✔
1223
    async renameColumn(
26✔
1224
        tableOrName: Table | string,
64✔
1225
        oldTableColumnOrName: TableColumn | string,
64✔
1226
        newTableColumnOrName: TableColumn | string,
64✔
1227
    ): Promise<void> {
64✔
1228
        const table = InstanceChecker.isTable(tableOrName)
64✔
1229
            ? tableOrName
64✔
1230
            : await this.getCachedTable(tableOrName)
64✔
1231
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
8✔
1232
            ? oldTableColumnOrName
64✔
1233
            : table.columns.find((c) => c.name === oldTableColumnOrName)
64✔
1234
        if (!oldColumn)
64✔
1235
            throw new TypeORMError(
64!
1236
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
×
1237
            )
×
1238

64✔
1239
        let newColumn
64✔
1240
        if (InstanceChecker.isTableColumn(newTableColumnOrName)) {
64✔
1241
            newColumn = newTableColumnOrName
44✔
1242
        } else {
64✔
1243
            newColumn = oldColumn.clone()
20✔
1244
            newColumn.name = newTableColumnOrName
20✔
1245
        }
20✔
1246

64✔
1247
        return this.changeColumn(table, oldColumn, newColumn)
64✔
1248
    }
64✔
1249

26✔
1250
    /**
26✔
1251
     * Changes a column in the table.
26✔
1252
     * @param tableOrName
26✔
1253
     * @param oldTableColumnOrName
26✔
1254
     * @param newColumn
26✔
1255
     */
26✔
1256
    async changeColumn(
26✔
1257
        tableOrName: Table | string,
504✔
1258
        oldTableColumnOrName: TableColumn | string,
504✔
1259
        newColumn: TableColumn,
504✔
1260
    ): Promise<void> {
504✔
1261
        const table = InstanceChecker.isTable(tableOrName)
504✔
1262
            ? tableOrName
504✔
1263
            : await this.getCachedTable(tableOrName)
504!
1264
        let clonedTable = table.clone()
×
1265
        const upQueries: Query[] = []
×
1266
        const downQueries: Query[] = []
×
1267
        let defaultValueChanged = false
×
1268

×
1269
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
×
1270
            ? oldTableColumnOrName
504✔
1271
            : table.columns.find(
504!
1272
                  (column) => column.name === oldTableColumnOrName,
×
1273
              )
504✔
1274
        if (!oldColumn)
504✔
1275
            throw new TypeORMError(
504!
1276
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
×
1277
            )
×
1278

504✔
1279
        if (
504✔
1280
            oldColumn.type !== newColumn.type ||
504✔
1281
            oldColumn.length !== newColumn.length ||
504✔
1282
            newColumn.isArray !== oldColumn.isArray ||
504✔
1283
            (!oldColumn.generatedType &&
416✔
1284
                newColumn.generatedType === "STORED") ||
504✔
1285
            (oldColumn.asExpression !== newColumn.asExpression &&
412✔
1286
                newColumn.generatedType === "STORED")
412✔
1287
        ) {
504✔
1288
            // To avoid data conversion, we just recreate column
100✔
1289
            await this.dropColumn(table, oldColumn)
100✔
1290
            await this.addColumn(table, newColumn)
100✔
1291

100✔
1292
            // update cloned table
100✔
1293
            clonedTable = table.clone()
100✔
1294
        } else {
504✔
1295
            if (oldColumn.name !== newColumn.name) {
404✔
1296
                // rename column
100✔
1297
                upQueries.push(
100✔
1298
                    new Query(
100✔
1299
                        `ALTER TABLE ${this.escapePath(table)} RENAME COLUMN "${
100✔
1300
                            oldColumn.name
100✔
1301
                        }" TO "${newColumn.name}"`,
100✔
1302
                    ),
100✔
1303
                )
100✔
1304
                downQueries.push(
100✔
1305
                    new Query(
100✔
1306
                        `ALTER TABLE ${this.escapePath(table)} RENAME COLUMN "${
100✔
1307
                            newColumn.name
100✔
1308
                        }" TO "${oldColumn.name}"`,
100✔
1309
                    ),
100✔
1310
                )
100✔
1311

100✔
1312
                // rename ENUM type
100✔
1313
                if (
100✔
1314
                    oldColumn.type === "enum" ||
100✔
1315
                    oldColumn.type === "simple-enum"
92✔
1316
                ) {
100✔
1317
                    const oldEnumType = await this.getUserDefinedTypeName(
8✔
1318
                        table,
8✔
1319
                        oldColumn,
8✔
1320
                    )
8✔
1321
                    upQueries.push(
8✔
1322
                        new Query(
8✔
1323
                            `ALTER TYPE "${oldEnumType.schema}"."${
8✔
1324
                                oldEnumType.name
8✔
1325
                            }" RENAME TO ${this.buildEnumName(
8✔
1326
                                table,
8✔
1327
                                newColumn,
8✔
1328
                                false,
8✔
1329
                            )}`,
8✔
1330
                        ),
8✔
1331
                    )
8✔
1332
                    downQueries.push(
8✔
1333
                        new Query(
8✔
1334
                            `ALTER TYPE ${this.buildEnumName(
8✔
1335
                                table,
8✔
1336
                                newColumn,
8✔
1337
                            )} RENAME TO "${oldEnumType.name}"`,
8✔
1338
                        ),
8✔
1339
                    )
8✔
1340
                }
8✔
1341

100✔
1342
                // rename column primary key constraint
100✔
1343
                if (
100✔
1344
                    oldColumn.isPrimary === true &&
100✔
1345
                    !oldColumn.primaryKeyConstraintName
28✔
1346
                ) {
100✔
1347
                    const primaryColumns = clonedTable.primaryColumns
20✔
1348

20✔
1349
                    // build old primary constraint name
20✔
1350
                    const columnNames = primaryColumns.map(
20✔
1351
                        (column) => column.name,
20✔
1352
                    )
20✔
1353
                    const oldPkName =
20✔
1354
                        this.connection.namingStrategy.primaryKeyName(
20✔
1355
                            clonedTable,
20✔
1356
                            columnNames,
20✔
1357
                        )
20✔
1358

20✔
1359
                    // replace old column name with new column name
20✔
1360
                    columnNames.splice(columnNames.indexOf(oldColumn.name), 1)
20✔
1361
                    columnNames.push(newColumn.name)
20✔
1362

20✔
1363
                    // build new primary constraint name
20✔
1364
                    const newPkName =
20✔
1365
                        this.connection.namingStrategy.primaryKeyName(
20✔
1366
                            clonedTable,
20✔
1367
                            columnNames,
20✔
1368
                        )
20✔
1369

20✔
1370
                    upQueries.push(
20✔
1371
                        new Query(
20✔
1372
                            `ALTER TABLE ${this.escapePath(
20✔
1373
                                table,
20✔
1374
                            )} RENAME CONSTRAINT "${oldPkName}" TO "${newPkName}"`,
20✔
1375
                        ),
20✔
1376
                    )
20✔
1377
                    downQueries.push(
20✔
1378
                        new Query(
20✔
1379
                            `ALTER TABLE ${this.escapePath(
20✔
1380
                                table,
20✔
1381
                            )} RENAME CONSTRAINT "${newPkName}" TO "${oldPkName}"`,
20✔
1382
                        ),
20✔
1383
                    )
20✔
1384
                }
20✔
1385

100✔
1386
                // rename column sequence
100✔
1387
                if (
100✔
1388
                    oldColumn.isGenerated === true &&
100!
1389
                    newColumn.generationStrategy === "increment"
×
1390
                ) {
100!
1391
                    const sequencePath = this.buildSequencePath(
×
1392
                        table,
×
1393
                        oldColumn.name,
×
1394
                    )
×
1395
                    const sequenceName = this.buildSequenceName(
×
1396
                        table,
×
1397
                        oldColumn.name,
×
1398
                    )
×
1399

×
1400
                    const newSequencePath = this.buildSequencePath(
×
1401
                        table,
×
1402
                        newColumn.name,
×
1403
                    )
×
1404
                    const newSequenceName = this.buildSequenceName(
×
1405
                        table,
×
1406
                        newColumn.name,
×
1407
                    )
×
1408

×
1409
                    const up = `ALTER SEQUENCE ${this.escapePath(
×
1410
                        sequencePath,
×
1411
                    )} RENAME TO "${newSequenceName}"`
×
1412
                    const down = `ALTER SEQUENCE ${this.escapePath(
×
1413
                        newSequencePath,
×
1414
                    )} RENAME TO "${sequenceName}"`
×
1415

×
1416
                    upQueries.push(new Query(up))
×
1417
                    downQueries.push(new Query(down))
×
1418
                }
×
1419

100✔
1420
                // rename unique constraints
100✔
1421
                clonedTable.findColumnUniques(oldColumn).forEach((unique) => {
100✔
1422
                    const oldUniqueName =
32✔
1423
                        this.connection.namingStrategy.uniqueConstraintName(
32✔
1424
                            clonedTable,
32✔
1425
                            unique.columnNames,
32✔
1426
                        )
32✔
1427

32✔
1428
                    // Skip renaming if Unique has user defined constraint name
32✔
1429
                    if (unique.name !== oldUniqueName) return
32✔
1430

16✔
1431
                    // build new constraint name
16✔
1432
                    unique.columnNames.splice(
16✔
1433
                        unique.columnNames.indexOf(oldColumn.name),
16✔
1434
                        1,
16✔
1435
                    )
16✔
1436
                    unique.columnNames.push(newColumn.name)
16✔
1437
                    const newUniqueName =
16✔
1438
                        this.connection.namingStrategy.uniqueConstraintName(
16✔
1439
                            clonedTable,
16✔
1440
                            unique.columnNames,
16✔
1441
                        )
16✔
1442

16✔
1443
                    // build queries
16✔
1444
                    upQueries.push(
16✔
1445
                        new Query(
16✔
1446
                            `ALTER TABLE ${this.escapePath(
16✔
1447
                                table,
16✔
1448
                            )} RENAME CONSTRAINT "${
16✔
1449
                                unique.name
16✔
1450
                            }" TO "${newUniqueName}"`,
16✔
1451
                        ),
16✔
1452
                    )
16✔
1453
                    downQueries.push(
16✔
1454
                        new Query(
16✔
1455
                            `ALTER TABLE ${this.escapePath(
16✔
1456
                                table,
16✔
1457
                            )} RENAME CONSTRAINT "${newUniqueName}" TO "${
16✔
1458
                                unique.name
16✔
1459
                            }"`,
16✔
1460
                        ),
16✔
1461
                    )
16✔
1462

16✔
1463
                    // replace constraint name
16✔
1464
                    unique.name = newUniqueName
16✔
1465
                })
100✔
1466

100✔
1467
                // rename index constraints
100✔
1468
                clonedTable.findColumnIndices(oldColumn).forEach((index) => {
100✔
1469
                    const oldIndexName =
28✔
1470
                        this.connection.namingStrategy.indexName(
28✔
1471
                            clonedTable,
28✔
1472
                            index.columnNames,
28✔
1473
                            index.where,
28✔
1474
                        )
28✔
1475

28✔
1476
                    // Skip renaming if Index has user defined constraint name
28✔
1477
                    if (index.name !== oldIndexName) return
28✔
1478

20✔
1479
                    // build new constraint name
20✔
1480
                    index.columnNames.splice(
20✔
1481
                        index.columnNames.indexOf(oldColumn.name),
20✔
1482
                        1,
20✔
1483
                    )
20✔
1484
                    index.columnNames.push(newColumn.name)
20✔
1485
                    const { schema } = this.driver.parseTableName(table)
20✔
1486
                    const newIndexName =
20✔
1487
                        this.connection.namingStrategy.indexName(
20✔
1488
                            clonedTable,
20✔
1489
                            index.columnNames,
20✔
1490
                            index.where,
20✔
1491
                        )
20✔
1492

20✔
1493
                    // build queries
20✔
1494
                    const up = schema
20✔
1495
                        ? `ALTER INDEX "${schema}"."${index.name}" RENAME TO "${newIndexName}"`
20✔
1496
                        : `ALTER INDEX "${index.name}" RENAME TO "${newIndexName}"`
28!
1497
                    const down = schema
28✔
1498
                        ? `ALTER INDEX "${schema}"."${newIndexName}" RENAME TO "${index.name}"`
28✔
1499
                        : `ALTER INDEX "${newIndexName}" RENAME TO "${index.name}"`
28!
1500

28✔
1501
                    upQueries.push(new Query(up))
28✔
1502
                    downQueries.push(new Query(down))
28✔
1503

28✔
1504
                    // replace constraint name
28✔
1505
                    index.name = newIndexName
28✔
1506
                })
100✔
1507

100✔
1508
                // rename foreign key constraints
100✔
1509
                clonedTable
100✔
1510
                    .findColumnForeignKeys(oldColumn)
100✔
1511
                    .forEach((foreignKey) => {
100✔
1512
                        const foreignKeyName =
36✔
1513
                            this.connection.namingStrategy.foreignKeyName(
36✔
1514
                                clonedTable,
36✔
1515
                                foreignKey.columnNames,
36✔
1516
                                this.getTablePath(foreignKey),
36✔
1517
                                foreignKey.referencedColumnNames,
36✔
1518
                            )
36✔
1519

36✔
1520
                        // Skip renaming if foreign key has user defined constraint name
36✔
1521
                        if (foreignKey.name !== foreignKeyName) return
36✔
1522

4✔
1523
                        // build new constraint name
4✔
1524
                        foreignKey.columnNames.splice(
4✔
1525
                            foreignKey.columnNames.indexOf(oldColumn.name),
4✔
1526
                            1,
4✔
1527
                        )
4✔
1528
                        foreignKey.columnNames.push(newColumn.name)
4✔
1529
                        const newForeignKeyName =
4✔
1530
                            this.connection.namingStrategy.foreignKeyName(
4✔
1531
                                clonedTable,
4✔
1532
                                foreignKey.columnNames,
4✔
1533
                                this.getTablePath(foreignKey),
4✔
1534
                                foreignKey.referencedColumnNames,
4✔
1535
                            )
4✔
1536

4✔
1537
                        // build queries
4✔
1538
                        upQueries.push(
4✔
1539
                            new Query(
4✔
1540
                                `ALTER TABLE ${this.escapePath(
4✔
1541
                                    table,
4✔
1542
                                )} RENAME CONSTRAINT "${
4✔
1543
                                    foreignKey.name
4✔
1544
                                }" TO "${newForeignKeyName}"`,
4✔
1545
                            ),
4✔
1546
                        )
4✔
1547
                        downQueries.push(
4✔
1548
                            new Query(
4✔
1549
                                `ALTER TABLE ${this.escapePath(
4✔
1550
                                    table,
4✔
1551
                                )} RENAME CONSTRAINT "${newForeignKeyName}" TO "${
4✔
1552
                                    foreignKey.name
4✔
1553
                                }"`,
4✔
1554
                            ),
4✔
1555
                        )
4✔
1556

4✔
1557
                        // replace constraint name
4✔
1558
                        foreignKey.name = newForeignKeyName
4✔
1559
                    })
100✔
1560

100✔
1561
                // rename old column in the Table object
100✔
1562
                const oldTableColumn = clonedTable.columns.find(
100✔
1563
                    (column) => column.name === oldColumn.name,
100✔
1564
                )
100✔
1565
                clonedTable.columns[
100✔
1566
                    clonedTable.columns.indexOf(oldTableColumn!)
100✔
1567
                ].name = newColumn.name
100✔
1568
                oldColumn.name = newColumn.name
100✔
1569
            }
100✔
1570

404✔
1571
            if (
404✔
1572
                newColumn.precision !== oldColumn.precision ||
404✔
1573
                newColumn.scale !== oldColumn.scale
404✔
1574
            ) {
404!
1575
                upQueries.push(
×
1576
                    new Query(
×
1577
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
×
1578
                            newColumn.name
×
1579
                        }" TYPE ${this.driver.createFullType(newColumn)}`,
×
1580
                    ),
×
1581
                )
×
1582
                downQueries.push(
×
1583
                    new Query(
×
1584
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
×
1585
                            newColumn.name
×
1586
                        }" TYPE ${this.driver.createFullType(oldColumn)}`,
×
1587
                    ),
×
1588
                )
×
1589
            }
×
1590

404✔
1591
            if (
404✔
1592
                (newColumn.type === "enum" ||
404✔
1593
                    newColumn.type === "simple-enum") &&
404✔
1594
                (oldColumn.type === "enum" ||
80!
1595
                    oldColumn.type === "simple-enum") &&
404✔
1596
                (!OrmUtils.isArraysEqual(newColumn.enum!, oldColumn.enum!) ||
80✔
1597
                    newColumn.enumName !== oldColumn.enumName)
80✔
1598
            ) {
404✔
1599
                const arraySuffix = newColumn.isArray ? "[]" : ""
68✔
1600

68✔
1601
                const { extraItems, missingItems } = OrmUtils.getArraysDiff(
68✔
1602
                    newColumn.enum!,
68✔
1603
                    oldColumn.enum!,
68✔
1604
                )
68✔
1605

68✔
1606
                const version = this.driver.version
68✔
1607

68✔
1608
                // when the only change is new enum value(s) we can use ADD VALUE syntax
68✔
1609
                const useAddValueForUp =
68✔
1610
                    VersionUtils.isGreaterOrEqual(version, "12.0") &&
68✔
1611
                    missingItems.length === 0 &&
68✔
1612
                    extraItems.length > 0
24✔
1613

68✔
1614
                // "public"."new_enum"
68✔
1615
                const newEnumName = this.buildEnumName(table, newColumn)
68✔
1616

68✔
1617
                // "public"."old_enum"
68✔
1618
                const oldEnumName = this.buildEnumName(table, oldColumn)
68✔
1619

68✔
1620
                // "old_enum"
68✔
1621
                const oldEnumNameWithoutSchema = this.buildEnumName(
68✔
1622
                    table,
68✔
1623
                    oldColumn,
68✔
1624
                    false,
68✔
1625
                )
68✔
1626

68✔
1627
                //"public"."old_enum_old"
68✔
1628
                const oldEnumNameWithSchema_old = this.buildEnumName(
68✔
1629
                    table,
68✔
1630
                    oldColumn,
68✔
1631
                    true,
68✔
1632
                    false,
68✔
1633
                    true,
68✔
1634
                )
68✔
1635

68✔
1636
                //"old_enum_old"
68✔
1637
                const oldEnumNameWithoutSchema_old = this.buildEnumName(
68✔
1638
                    table,
68✔
1639
                    oldColumn,
68✔
1640
                    false,
68✔
1641
                    false,
68✔
1642
                    true,
68✔
1643
                )
68✔
1644

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

12✔
1651
                        upQueries.push(
12✔
1652
                            new Query(
12✔
1653
                                `ALTER TYPE ${oldEnumName} ADD VALUE '${escapedValue}'`,
12✔
1654
                            ),
12✔
1655
                        )
12✔
1656
                    }
12✔
1657

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

8✔
1660
                    downQueries.push(
8✔
1661
                        new Query(
8✔
1662
                            `ALTER TYPE ${oldEnumNameWithSchema_old} RENAME TO ${oldEnumNameWithoutSchema}`,
8✔
1663
                        ),
8✔
1664
                    )
8✔
1665

8✔
1666
                    downQueries.push(
8✔
1667
                        this.dropEnumTypeSql(table, newColumn, newEnumName),
8✔
1668
                    )
8✔
1669

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

8✔
1672
                    downQueries.push(
8✔
1673
                        new Query(
8✔
1674
                            `ALTER TABLE ${this.escapePath(
8✔
1675
                                table,
8✔
1676
                            )} ALTER COLUMN "${
8✔
1677
                                newColumn.name
8✔
1678
                            }" TYPE ${downType}`,
8✔
1679
                        ),
8✔
1680
                    )
8✔
1681

8✔
1682
                    downQueries.push(
8✔
1683
                        this.createEnumTypeSql(
8✔
1684
                            table,
8✔
1685
                            oldColumn,
8✔
1686
                            oldEnumNameWithSchema_old,
8✔
1687
                        ),
8✔
1688
                    )
8✔
1689
                } else {
68✔
1690
                    // rename old ENUM
60✔
1691
                    upQueries.push(
60✔
1692
                        new Query(
60✔
1693
                            `ALTER TYPE ${oldEnumName} RENAME TO ${oldEnumNameWithoutSchema_old}`,
60✔
1694
                        ),
60✔
1695
                    )
60✔
1696
                    downQueries.push(
60✔
1697
                        new Query(
60✔
1698
                            `ALTER TYPE ${oldEnumNameWithSchema_old} RENAME TO ${oldEnumNameWithoutSchema}`,
60✔
1699
                        ),
60✔
1700
                    )
60✔
1701

60✔
1702
                    // create new ENUM
60✔
1703
                    upQueries.push(
60✔
1704
                        this.createEnumTypeSql(table, newColumn, newEnumName),
60✔
1705
                    )
60✔
1706
                    downQueries.push(
60✔
1707
                        this.dropEnumTypeSql(table, newColumn, newEnumName),
60✔
1708
                    )
60✔
1709

60✔
1710
                    // if column have default value, we must drop it to avoid issues with type casting
60✔
1711
                    if (
60✔
1712
                        oldColumn.default !== null &&
60✔
1713
                        oldColumn.default !== undefined
60✔
1714
                    ) {
60✔
1715
                        // mark default as changed to prevent double update
20✔
1716
                        defaultValueChanged = true
20✔
1717
                        upQueries.push(
20✔
1718
                            new Query(
20✔
1719
                                `ALTER TABLE ${this.escapePath(
20✔
1720
                                    table,
20✔
1721
                                )} ALTER COLUMN "${
20✔
1722
                                    oldColumn.name
20✔
1723
                                }" DROP DEFAULT`,
20✔
1724
                            ),
20✔
1725
                        )
20✔
1726
                        downQueries.push(
20✔
1727
                            new Query(
20✔
1728
                                `ALTER TABLE ${this.escapePath(
20✔
1729
                                    table,
20✔
1730
                                )} ALTER COLUMN "${
20✔
1731
                                    oldColumn.name
20✔
1732
                                }" SET DEFAULT ${oldColumn.default}`,
20✔
1733
                            ),
20✔
1734
                        )
20✔
1735
                    }
20✔
1736

60✔
1737
                    // build column types
60✔
1738
                    const upType = `${newEnumName}${arraySuffix} USING "${newColumn.name}"::"text"::${newEnumName}${arraySuffix}`
60✔
1739
                    const downType = `${oldEnumNameWithSchema_old}${arraySuffix} USING "${newColumn.name}"::"text"::${oldEnumNameWithSchema_old}${arraySuffix}`
60✔
1740

60✔
1741
                    // update column to use new type
60✔
1742
                    upQueries.push(
60✔
1743
                        new Query(
60✔
1744
                            `ALTER TABLE ${this.escapePath(
60✔
1745
                                table,
60✔
1746
                            )} ALTER COLUMN "${newColumn.name}" TYPE ${upType}`,
60✔
1747
                        ),
60✔
1748
                    )
60✔
1749
                    downQueries.push(
60✔
1750
                        new Query(
60✔
1751
                            `ALTER TABLE ${this.escapePath(
60✔
1752
                                table,
60✔
1753
                            )} ALTER COLUMN "${
60✔
1754
                                newColumn.name
60✔
1755
                            }" TYPE ${downType}`,
60✔
1756
                        ),
60✔
1757
                    )
60✔
1758

60✔
1759
                    // restore column default or create new one
60✔
1760
                    if (
60✔
1761
                        newColumn.default !== null &&
60✔
1762
                        newColumn.default !== undefined
60✔
1763
                    ) {
60✔
1764
                        upQueries.push(
20✔
1765
                            new Query(
20✔
1766
                                `ALTER TABLE ${this.escapePath(
20✔
1767
                                    table,
20✔
1768
                                )} ALTER COLUMN "${
20✔
1769
                                    newColumn.name
20✔
1770
                                }" SET DEFAULT ${newColumn.default}`,
20✔
1771
                            ),
20✔
1772
                        )
20✔
1773
                        downQueries.push(
20✔
1774
                            new Query(
20✔
1775
                                `ALTER TABLE ${this.escapePath(
20✔
1776
                                    table,
20✔
1777
                                )} ALTER COLUMN "${
20✔
1778
                                    newColumn.name
20✔
1779
                                }" DROP DEFAULT`,
20✔
1780
                            ),
20✔
1781
                        )
20✔
1782
                    }
20✔
1783

60✔
1784
                    // remove old ENUM
60✔
1785
                    upQueries.push(
60✔
1786
                        this.dropEnumTypeSql(
60✔
1787
                            table,
60✔
1788
                            oldColumn,
60✔
1789
                            oldEnumNameWithSchema_old,
60✔
1790
                        ),
60✔
1791
                    )
60✔
1792
                    downQueries.push(
60✔
1793
                        this.createEnumTypeSql(
60✔
1794
                            table,
60✔
1795
                            oldColumn,
60✔
1796
                            oldEnumNameWithSchema_old,
60✔
1797
                        ),
60✔
1798
                    )
60✔
1799
                }
60✔
1800
            }
68✔
1801

404✔
1802
            if (oldColumn.isNullable !== newColumn.isNullable) {
404✔
1803
                if (newColumn.isNullable) {
8✔
1804
                    upQueries.push(
4✔
1805
                        new Query(
4✔
1806
                            `ALTER TABLE ${this.escapePath(
4✔
1807
                                table,
4✔
1808
                            )} ALTER COLUMN "${oldColumn.name}" DROP NOT NULL`,
4✔
1809
                        ),
4✔
1810
                    )
4✔
1811
                    downQueries.push(
4✔
1812
                        new Query(
4✔
1813
                            `ALTER TABLE ${this.escapePath(
4✔
1814
                                table,
4✔
1815
                            )} ALTER COLUMN "${oldColumn.name}" SET NOT NULL`,
4✔
1816
                        ),
4✔
1817
                    )
4✔
1818
                } else {
4✔
1819
                    upQueries.push(
4✔
1820
                        new Query(
4✔
1821
                            `ALTER TABLE ${this.escapePath(
4✔
1822
                                table,
4✔
1823
                            )} ALTER COLUMN "${oldColumn.name}" SET NOT NULL`,
4✔
1824
                        ),
4✔
1825
                    )
4✔
1826
                    downQueries.push(
4✔
1827
                        new Query(
4✔
1828
                            `ALTER TABLE ${this.escapePath(
4✔
1829
                                table,
4✔
1830
                            )} ALTER COLUMN "${oldColumn.name}" DROP NOT NULL`,
4✔
1831
                        ),
4✔
1832
                    )
4✔
1833
                }
4✔
1834
            }
8✔
1835

404✔
1836
            if (oldColumn.comment !== newColumn.comment) {
404✔
1837
                upQueries.push(
20✔
1838
                    new Query(
20✔
1839
                        `COMMENT ON COLUMN ${this.escapePath(table)}."${
20✔
1840
                            oldColumn.name
20✔
1841
                        }" IS ${this.escapeComment(newColumn.comment)}`,
20✔
1842
                    ),
20✔
1843
                )
20✔
1844
                downQueries.push(
20✔
1845
                    new Query(
20✔
1846
                        `COMMENT ON COLUMN ${this.escapePath(table)}."${
20✔
1847
                            newColumn.name
20✔
1848
                        }" IS ${this.escapeComment(oldColumn.comment)}`,
20✔
1849
                    ),
20✔
1850
                )
20✔
1851
            }
20✔
1852

404✔
1853
            if (newColumn.isPrimary !== oldColumn.isPrimary) {
404✔
1854
                const primaryColumns = clonedTable.primaryColumns
16✔
1855

16✔
1856
                // if primary column state changed, we must always drop existed constraint.
16✔
1857
                if (primaryColumns.length > 0) {
16✔
1858
                    const pkName = primaryColumns[0].primaryKeyConstraintName
12✔
1859
                        ? primaryColumns[0].primaryKeyConstraintName
12!
1860
                        : this.connection.namingStrategy.primaryKeyName(
12✔
1861
                              clonedTable,
12✔
1862
                              primaryColumns.map((column) => column.name),
12✔
1863
                          )
12✔
1864

12✔
1865
                    const columnNames = primaryColumns
12✔
1866
                        .map((column) => `"${column.name}"`)
12✔
1867
                        .join(", ")
12✔
1868

12✔
1869
                    upQueries.push(
12✔
1870
                        new Query(
12✔
1871
                            `ALTER TABLE ${this.escapePath(
12✔
1872
                                table,
12✔
1873
                            )} DROP CONSTRAINT "${pkName}"`,
12✔
1874
                        ),
12✔
1875
                    )
12✔
1876
                    downQueries.push(
12✔
1877
                        new Query(
12✔
1878
                            `ALTER TABLE ${this.escapePath(
12✔
1879
                                table,
12✔
1880
                            )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
12✔
1881
                        ),
12✔
1882
                    )
12✔
1883
                }
12✔
1884

16✔
1885
                if (newColumn.isPrimary === true) {
16✔
1886
                    primaryColumns.push(newColumn)
8✔
1887
                    // update column in table
8✔
1888
                    const column = clonedTable.columns.find(
8✔
1889
                        (column) => column.name === newColumn.name,
8✔
1890
                    )
8✔
1891
                    column!.isPrimary = true
8✔
1892
                    const pkName = primaryColumns[0].primaryKeyConstraintName
8✔
1893
                        ? primaryColumns[0].primaryKeyConstraintName
8!
1894
                        : this.connection.namingStrategy.primaryKeyName(
8✔
1895
                              clonedTable,
8✔
1896
                              primaryColumns.map((column) => column.name),
8✔
1897
                          )
8✔
1898

8✔
1899
                    const columnNames = primaryColumns
8✔
1900
                        .map((column) => `"${column.name}"`)
8✔
1901
                        .join(", ")
8✔
1902

8✔
1903
                    upQueries.push(
8✔
1904
                        new Query(
8✔
1905
                            `ALTER TABLE ${this.escapePath(
8✔
1906
                                table,
8✔
1907
                            )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
8✔
1908
                        ),
8✔
1909
                    )
8✔
1910
                    downQueries.push(
8✔
1911
                        new Query(
8✔
1912
                            `ALTER TABLE ${this.escapePath(
8✔
1913
                                table,
8✔
1914
                            )} DROP CONSTRAINT "${pkName}"`,
8✔
1915
                        ),
8✔
1916
                    )
8✔
1917
                } else {
8✔
1918
                    const primaryColumn = primaryColumns.find(
8✔
1919
                        (c) => c.name === newColumn.name,
8✔
1920
                    )
8✔
1921
                    primaryColumns.splice(
8✔
1922
                        primaryColumns.indexOf(primaryColumn!),
8✔
1923
                        1,
8✔
1924
                    )
8✔
1925

8✔
1926
                    // update column in table
8✔
1927
                    const column = clonedTable.columns.find(
8✔
1928
                        (column) => column.name === newColumn.name,
8✔
1929
                    )
8✔
1930
                    column!.isPrimary = false
8✔
1931

8✔
1932
                    // if we have another primary keys, we must recreate constraint.
8✔
1933
                    if (primaryColumns.length > 0) {
8✔
1934
                        const pkName = primaryColumns[0]
4✔
1935
                            .primaryKeyConstraintName
4✔
1936
                            ? primaryColumns[0].primaryKeyConstraintName
4!
1937
                            : this.connection.namingStrategy.primaryKeyName(
4✔
1938
                                  clonedTable,
4✔
1939
                                  primaryColumns.map((column) => column.name),
4✔
1940
                              )
4✔
1941

4✔
1942
                        const columnNames = primaryColumns
4✔
1943
                            .map((column) => `"${column.name}"`)
4✔
1944
                            .join(", ")
4✔
1945

4✔
1946
                        upQueries.push(
4✔
1947
                            new Query(
4✔
1948
                                `ALTER TABLE ${this.escapePath(
4✔
1949
                                    table,
4✔
1950
                                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
4✔
1951
                            ),
4✔
1952
                        )
4✔
1953
                        downQueries.push(
4✔
1954
                            new Query(
4✔
1955
                                `ALTER TABLE ${this.escapePath(
4✔
1956
                                    table,
4✔
1957
                                )} DROP CONSTRAINT "${pkName}"`,
4✔
1958
                            ),
4✔
1959
                        )
4✔
1960
                    }
4✔
1961
                }
8✔
1962
            }
16✔
1963

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

404✔
2025
            if (oldColumn.isGenerated !== newColumn.isGenerated) {
404✔
2026
                // if old column was "generated", we should clear defaults
48✔
2027
                if (oldColumn.isGenerated) {
48✔
2028
                    if (oldColumn.generationStrategy === "uuid") {
20✔
2029
                        upQueries.push(
8✔
2030
                            new Query(
8✔
2031
                                `ALTER TABLE ${this.escapePath(
8✔
2032
                                    table,
8✔
2033
                                )} ALTER COLUMN "${
8✔
2034
                                    oldColumn.name
8✔
2035
                                }" DROP DEFAULT`,
8✔
2036
                            ),
8✔
2037
                        )
8✔
2038
                        downQueries.push(
8✔
2039
                            new Query(
8✔
2040
                                `ALTER TABLE ${this.escapePath(
8✔
2041
                                    table,
8✔
2042
                                )} ALTER COLUMN "${
8✔
2043
                                    oldColumn.name
8✔
2044
                                }" SET DEFAULT ${this.driver.uuidGenerator}`,
8✔
2045
                            ),
8✔
2046
                        )
8✔
2047
                    } else if (oldColumn.generationStrategy === "increment") {
20✔
2048
                        upQueries.push(
12✔
2049
                            new Query(
12✔
2050
                                `ALTER TABLE ${this.escapePath(
12✔
2051
                                    table,
12✔
2052
                                )} ALTER COLUMN "${
12✔
2053
                                    newColumn.name
12✔
2054
                                }" DROP DEFAULT`,
12✔
2055
                            ),
12✔
2056
                        )
12✔
2057
                        downQueries.push(
12✔
2058
                            new Query(
12✔
2059
                                `ALTER TABLE ${this.escapePath(
12✔
2060
                                    table,
12✔
2061
                                )} ALTER COLUMN "${
12✔
2062
                                    newColumn.name
12✔
2063
                                }" SET DEFAULT nextval('${this.escapePath(
12✔
2064
                                    this.buildSequencePath(table, newColumn),
12✔
2065
                                )}')`,
12✔
2066
                            ),
12✔
2067
                        )
12✔
2068

12✔
2069
                        upQueries.push(
12✔
2070
                            new Query(
12✔
2071
                                `DROP SEQUENCE ${this.escapePath(
12✔
2072
                                    this.buildSequencePath(table, newColumn),
12✔
2073
                                )}`,
12✔
2074
                            ),
12✔
2075
                        )
12✔
2076
                        downQueries.push(
12✔
2077
                            new Query(
12✔
2078
                                `CREATE SEQUENCE IF NOT EXISTS ${this.escapePath(
12✔
2079
                                    this.buildSequencePath(table, newColumn),
12✔
2080
                                )} OWNED BY ${this.escapePath(table)}."${
12✔
2081
                                    newColumn.name
12✔
2082
                                }"`,
12✔
2083
                            ),
12✔
2084
                        )
12✔
2085
                    }
12✔
2086
                }
20✔
2087

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

20✔
2147
                        upQueries.push(
20✔
2148
                            new Query(
20✔
2149
                                `ALTER TABLE ${this.escapePath(
20✔
2150
                                    table,
20✔
2151
                                )} ALTER COLUMN "${
20✔
2152
                                    newColumn.name
20✔
2153
                                }" SET DEFAULT nextval('${this.escapePath(
20✔
2154
                                    this.buildSequencePath(table, newColumn),
20✔
2155
                                )}')`,
20✔
2156
                            ),
20✔
2157
                        )
20✔
2158
                        downQueries.push(
20✔
2159
                            new Query(
20✔
2160
                                `ALTER TABLE ${this.escapePath(
20✔
2161
                                    table,
20✔
2162
                                )} ALTER COLUMN "${
20✔
2163
                                    newColumn.name
20✔
2164
                                }" DROP DEFAULT`,
20✔
2165
                            ),
20✔
2166
                        )
20✔
2167
                    } else {
20!
2168
                        upQueries.push(
×
2169
                            new Query(
×
2170
                                `ALTER TABLE ${this.escapePath(
×
2171
                                    table,
×
2172
                                )} ALTER COLUMN "${
×
2173
                                    newColumn.name
×
2174
                                }" DROP DEFAULT`,
×
2175
                            ),
×
2176
                        )
×
2177
                        downQueries.push(
×
2178
                            new Query(
×
2179
                                `ALTER TABLE ${this.escapePath(
×
2180
                                    table,
×
2181
                                )} ALTER COLUMN "${
×
2182
                                    newColumn.name
×
2183
                                }" SET DEFAULT nextval('${this.escapePath(
×
2184
                                    this.buildSequencePath(table, newColumn),
×
2185
                                )}')`,
×
2186
                            ),
×
2187
                        )
×
2188

×
2189
                        upQueries.push(
×
2190
                            new Query(
×
2191
                                `DROP SEQUENCE ${this.escapePath(
×
2192
                                    this.buildSequencePath(table, newColumn),
×
2193
                                )}`,
×
2194
                            ),
×
2195
                        )
×
2196
                        downQueries.push(
×
2197
                            new Query(
×
2198
                                `CREATE SEQUENCE IF NOT EXISTS ${this.escapePath(
×
2199
                                    this.buildSequencePath(table, newColumn),
×
2200
                                )} OWNED BY ${this.escapePath(table)}."${
×
2201
                                    newColumn.name
×
2202
                                }"`,
×
2203
                            ),
×
2204
                        )
×
2205
                    }
×
2206
                }
20✔
2207
            }
48✔
2208

404✔
2209
            // the default might have changed when the enum changed
404✔
2210
            if (
404✔
2211
                newColumn.default !== oldColumn.default &&
404✔
2212
                !defaultValueChanged
32✔
2213
            ) {
404✔
2214
                if (
20✔
2215
                    newColumn.default !== null &&
20✔
2216
                    newColumn.default !== undefined
20✔
2217
                ) {
20✔
2218
                    upQueries.push(
20✔
2219
                        new Query(
20✔
2220
                            `ALTER TABLE ${this.escapePath(
20✔
2221
                                table,
20✔
2222
                            )} ALTER COLUMN "${newColumn.name}" SET DEFAULT ${
20✔
2223
                                newColumn.default
20✔
2224
                            }`,
20✔
2225
                        ),
20✔
2226
                    )
20✔
2227

20✔
2228
                    if (
20✔
2229
                        oldColumn.default !== null &&
20✔
2230
                        oldColumn.default !== undefined
20✔
2231
                    ) {
20✔
2232
                        downQueries.push(
12✔
2233
                            new Query(
12✔
2234
                                `ALTER TABLE ${this.escapePath(
12✔
2235
                                    table,
12✔
2236
                                )} ALTER COLUMN "${
12✔
2237
                                    newColumn.name
12✔
2238
                                }" SET DEFAULT ${oldColumn.default}`,
12✔
2239
                            ),
12✔
2240
                        )
12✔
2241
                    } else {
20✔
2242
                        downQueries.push(
8✔
2243
                            new Query(
8✔
2244
                                `ALTER TABLE ${this.escapePath(
8✔
2245
                                    table,
8✔
2246
                                )} ALTER COLUMN "${
8✔
2247
                                    newColumn.name
8✔
2248
                                }" DROP DEFAULT`,
8✔
2249
                            ),
8✔
2250
                        )
8✔
2251
                    }
8✔
2252
                } else if (
20!
2253
                    oldColumn.default !== null &&
×
2254
                    oldColumn.default !== undefined
×
2255
                ) {
×
2256
                    upQueries.push(
×
2257
                        new Query(
×
2258
                            `ALTER TABLE ${this.escapePath(
×
2259
                                table,
×
2260
                            )} ALTER COLUMN "${newColumn.name}" DROP DEFAULT`,
×
2261
                        ),
×
2262
                    )
×
2263
                    downQueries.push(
×
2264
                        new Query(
×
2265
                            `ALTER TABLE ${this.escapePath(
×
2266
                                table,
×
2267
                            )} ALTER COLUMN "${newColumn.name}" SET DEFAULT ${
×
2268
                                oldColumn.default
×
2269
                            }`,
×
2270
                        ),
×
2271
                    )
×
2272
                }
×
2273
            }
20✔
2274

404✔
2275
            if (
404✔
2276
                (newColumn.spatialFeatureType || "").toLowerCase() !==
404✔
2277
                    (oldColumn.spatialFeatureType || "").toLowerCase() ||
404✔
2278
                newColumn.srid !== oldColumn.srid
404✔
2279
            ) {
404!
2280
                upQueries.push(
×
2281
                    new Query(
×
2282
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
×
2283
                            newColumn.name
×
2284
                        }" TYPE ${this.driver.createFullType(newColumn)}`,
×
2285
                    ),
×
2286
                )
×
2287
                downQueries.push(
×
2288
                    new Query(
×
2289
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
×
2290
                            newColumn.name
×
2291
                        }" TYPE ${this.driver.createFullType(oldColumn)}`,
×
2292
                    ),
×
2293
                )
×
2294
            }
×
2295

404✔
2296
            // update column collation
404✔
2297
            if (newColumn.collation !== oldColumn.collation) {
404✔
2298
                upQueries.push(
8✔
2299
                    new Query(
8✔
2300
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
8✔
2301
                            newColumn.name
8✔
2302
                        }" TYPE ${newColumn.type} COLLATE "${
8✔
2303
                            newColumn.collation
8✔
2304
                        }"`,
8✔
2305
                    ),
8✔
2306
                )
8✔
2307

8✔
2308
                const oldCollation = oldColumn.collation
8✔
2309
                    ? `"${oldColumn.collation}"`
8✔
2310
                    : `pg_catalog."default"` // if there's no old collation, use default
8!
2311

8✔
2312
                downQueries.push(
8✔
2313
                    new Query(
8✔
2314
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
8✔
2315
                            newColumn.name
8✔
2316
                        }" TYPE ${newColumn.type} COLLATE ${oldCollation}`,
8✔
2317
                    ),
8✔
2318
                )
8✔
2319
            }
8✔
2320

404✔
2321
            if (newColumn.generatedType !== oldColumn.generatedType) {
404✔
2322
                // Convert generated column data to normal column
8✔
2323
                if (
8✔
2324
                    !newColumn.generatedType ||
8!
2325
                    newColumn.generatedType === "VIRTUAL"
×
2326
                ) {
8✔
2327
                    // We can copy the generated data to the new column
8✔
2328
                    const tableNameWithSchema = (
8✔
2329
                        await this.getTableNameWithSchema(table.name)
8✔
2330
                    ).split(".")
8✔
2331
                    const tableName = tableNameWithSchema[1]
8✔
2332
                    const schema = tableNameWithSchema[0]
8✔
2333

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

504✔
2417
        await this.executeQueries(upQueries, downQueries)
504✔
2418
        this.replaceCachedTable(table, clonedTable)
504✔
2419
    }
504✔
2420

26✔
2421
    /**
26✔
2422
     * Changes a column in the table.
26✔
2423
     * @param tableOrName
26✔
2424
     * @param changedColumns
26✔
2425
     */
26✔
2426
    async changeColumns(
26✔
2427
        tableOrName: Table | string,
248✔
2428
        changedColumns: { newColumn: TableColumn; oldColumn: TableColumn }[],
248✔
2429
    ): Promise<void> {
248✔
2430
        for (const { oldColumn, newColumn } of changedColumns) {
248✔
2431
            await this.changeColumn(tableOrName, oldColumn, newColumn)
324✔
2432
        }
324✔
2433
    }
248✔
2434

26✔
2435
    /**
26✔
2436
     * Drops column in the table.
26✔
2437
     * @param tableOrName
26✔
2438
     * @param columnOrName
26✔
2439
     */
26✔
2440
    async dropColumn(
26✔
2441
        tableOrName: Table | string,
188✔
2442
        columnOrName: TableColumn | string,
188✔
2443
    ): Promise<void> {
188✔
2444
        const table = InstanceChecker.isTable(tableOrName)
188✔
2445
            ? tableOrName
188✔
2446
            : await this.getCachedTable(tableOrName)
188✔
2447
        const column = InstanceChecker.isTableColumn(columnOrName)
12✔
2448
            ? columnOrName
188✔
2449
            : table.findColumnByName(columnOrName)
188✔
2450
        if (!column)
188✔
2451
            throw new TypeORMError(
188✔
2452
                `Column "${columnOrName}" was not found in table "${table.name}"`,
4✔
2453
            )
4✔
2454

184✔
2455
        const clonedTable = table.clone()
184✔
2456
        const upQueries: Query[] = []
184✔
2457
        const downQueries: Query[] = []
184✔
2458

184✔
2459
        // drop primary key constraint
184✔
2460
        if (column.isPrimary) {
188✔
2461
            const pkName = column.primaryKeyConstraintName
16✔
2462
                ? column.primaryKeyConstraintName
16!
2463
                : this.connection.namingStrategy.primaryKeyName(
16✔
2464
                      clonedTable,
16✔
2465
                      clonedTable.primaryColumns.map((column) => column.name),
16✔
2466
                  )
16✔
2467

16✔
2468
            const columnNames = clonedTable.primaryColumns
16✔
2469
                .map((primaryColumn) => `"${primaryColumn.name}"`)
16✔
2470
                .join(", ")
16✔
2471

16✔
2472
            upQueries.push(
16✔
2473
                new Query(
16✔
2474
                    `ALTER TABLE ${this.escapePath(
16✔
2475
                        clonedTable,
16✔
2476
                    )} DROP CONSTRAINT "${pkName}"`,
16✔
2477
                ),
16✔
2478
            )
16✔
2479
            downQueries.push(
16✔
2480
                new Query(
16✔
2481
                    `ALTER TABLE ${this.escapePath(
16✔
2482
                        clonedTable,
16✔
2483
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
16✔
2484
                ),
16✔
2485
            )
16✔
2486

16✔
2487
            // update column in table
16✔
2488
            const tableColumn = clonedTable.findColumnByName(column.name)
16✔
2489
            tableColumn!.isPrimary = false
16✔
2490

16✔
2491
            // if primary key have multiple columns, we must recreate it without dropped column
16✔
2492
            if (clonedTable.primaryColumns.length > 0) {
16!
2493
                const pkName = clonedTable.primaryColumns[0]
×
2494
                    .primaryKeyConstraintName
×
2495
                    ? clonedTable.primaryColumns[0].primaryKeyConstraintName
×
2496
                    : this.connection.namingStrategy.primaryKeyName(
×
2497
                          clonedTable,
×
2498
                          clonedTable.primaryColumns.map(
×
2499
                              (column) => column.name,
×
2500
                          ),
×
2501
                      )
×
2502

×
2503
                const columnNames = clonedTable.primaryColumns
×
2504
                    .map((primaryColumn) => `"${primaryColumn.name}"`)
×
2505
                    .join(", ")
×
2506

×
2507
                upQueries.push(
×
2508
                    new Query(
×
2509
                        `ALTER TABLE ${this.escapePath(
×
2510
                            clonedTable,
×
2511
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
×
2512
                    ),
×
2513
                )
×
2514
                downQueries.push(
×
2515
                    new Query(
×
2516
                        `ALTER TABLE ${this.escapePath(
×
2517
                            clonedTable,
×
2518
                        )} DROP CONSTRAINT "${pkName}"`,
×
2519
                    ),
×
2520
                )
×
2521
            }
×
2522
        }
16✔
2523

184✔
2524
        // drop column index
184✔
2525
        const columnIndex = clonedTable.indices.find(
184✔
2526
            (index) =>
184✔
2527
                index.columnNames.length === 1 &&
12✔
2528
                index.columnNames[0] === column.name,
184✔
2529
        )
184✔
2530
        if (columnIndex) {
188!
2531
            clonedTable.indices.splice(
×
2532
                clonedTable.indices.indexOf(columnIndex),
×
2533
                1,
×
2534
            )
×
2535
            upQueries.push(this.dropIndexSql(table, columnIndex))
×
2536
            downQueries.push(this.createIndexSql(table, columnIndex))
×
2537
        }
×
2538

184✔
2539
        // drop column check
184✔
2540
        const columnCheck = clonedTable.checks.find(
184✔
2541
            (check) =>
184✔
2542
                !!check.columnNames &&
56✔
2543
                check.columnNames.length === 1 &&
56✔
2544
                check.columnNames[0] === column.name,
184✔
2545
        )
184✔
2546
        if (columnCheck) {
188✔
2547
            clonedTable.checks.splice(
8✔
2548
                clonedTable.checks.indexOf(columnCheck),
8✔
2549
                1,
8✔
2550
            )
8✔
2551
            upQueries.push(this.dropCheckConstraintSql(table, columnCheck))
8✔
2552
            downQueries.push(this.createCheckConstraintSql(table, columnCheck))
8✔
2553
        }
8✔
2554

184✔
2555
        // drop column unique
184✔
2556
        const columnUnique = clonedTable.uniques.find(
184✔
2557
            (unique) =>
184✔
2558
                unique.columnNames.length === 1 &&
94✔
2559
                unique.columnNames[0] === column.name,
184✔
2560
        )
184✔
2561
        if (columnUnique) {
188✔
2562
            clonedTable.uniques.splice(
12✔
2563
                clonedTable.uniques.indexOf(columnUnique),
12✔
2564
                1,
12✔
2565
            )
12✔
2566
            upQueries.push(this.dropUniqueConstraintSql(table, columnUnique))
12✔
2567
            downQueries.push(
12✔
2568
                this.createUniqueConstraintSql(table, columnUnique),
12✔
2569
            )
12✔
2570
        }
12✔
2571

184✔
2572
        upQueries.push(
184✔
2573
            new Query(
184✔
2574
                `ALTER TABLE ${this.escapePath(table)} DROP COLUMN "${
184✔
2575
                    column.name
184✔
2576
                }"`,
184✔
2577
            ),
184✔
2578
        )
184✔
2579
        downQueries.push(
184✔
2580
            new Query(
184✔
2581
                `ALTER TABLE ${this.escapePath(
184✔
2582
                    table,
184✔
2583
                )} ADD ${this.buildCreateColumnSql(table, column)}`,
184✔
2584
            ),
184✔
2585
        )
184✔
2586

184✔
2587
        // drop enum type
184✔
2588
        if (column.type === "enum" || column.type === "simple-enum") {
188✔
2589
            const hasEnum = await this.hasEnumType(table, column)
20✔
2590
            if (hasEnum) {
20✔
2591
                const enumType = await this.getUserDefinedTypeName(
20✔
2592
                    table,
20✔
2593
                    column,
20✔
2594
                )
20✔
2595
                const escapedEnumName = `"${enumType.schema}"."${enumType.name}"`
20✔
2596
                upQueries.push(
20✔
2597
                    this.dropEnumTypeSql(table, column, escapedEnumName),
20✔
2598
                )
20✔
2599
                downQueries.push(
20✔
2600
                    this.createEnumTypeSql(table, column, escapedEnumName),
20✔
2601
                )
20✔
2602
            }
20✔
2603
        }
20✔
2604

184✔
2605
        if (column.generatedType === "STORED") {
188✔
2606
            const tableNameWithSchema = (
12✔
2607
                await this.getTableNameWithSchema(table.name)
12✔
2608
            ).split(".")
12✔
2609
            const tableName = tableNameWithSchema[1]
12✔
2610
            const schema = tableNameWithSchema[0]
12✔
2611
            const deleteQuery = this.deleteTypeormMetadataSql({
12✔
2612
                database: this.driver.database,
12✔
2613
                schema,
12✔
2614
                table: tableName,
12✔
2615
                type: MetadataTableType.GENERATED_COLUMN,
12✔
2616
                name: column.name,
12✔
2617
            })
12✔
2618
            const insertQuery = this.insertTypeormMetadataSql({
12✔
2619
                database: this.driver.database,
12✔
2620
                schema,
12✔
2621
                table: tableName,
12✔
2622
                type: MetadataTableType.GENERATED_COLUMN,
12✔
2623
                name: column.name,
12✔
2624
                value: column.asExpression,
12✔
2625
            })
12✔
2626

12✔
2627
            upQueries.push(deleteQuery)
12✔
2628
            downQueries.push(insertQuery)
12✔
2629
        }
12✔
2630

184✔
2631
        await this.executeQueries(upQueries, downQueries)
184✔
2632

184✔
2633
        clonedTable.removeColumn(column)
184✔
2634
        this.replaceCachedTable(table, clonedTable)
184✔
2635
    }
184✔
2636

26✔
2637
    /**
26✔
2638
     * Drops the columns in the table.
26✔
2639
     * @param tableOrName
26✔
2640
     * @param columns
26✔
2641
     */
26✔
2642
    async dropColumns(
26✔
2643
        tableOrName: Table | string,
32✔
2644
        columns: TableColumn[] | string[],
32✔
2645
    ): Promise<void> {
32✔
2646
        for (const column of [...columns]) {
32✔
2647
            await this.dropColumn(tableOrName, column)
64✔
2648
        }
64✔
2649
    }
32✔
2650

26✔
2651
    /**
26✔
2652
     * Creates a new primary key.
26✔
2653
     * @param tableOrName
26✔
2654
     * @param columnNames
26✔
2655
     * @param constraintName
26✔
2656
     */
26✔
2657
    async createPrimaryKey(
26✔
2658
        tableOrName: Table | string,
8✔
2659
        columnNames: string[],
8✔
2660
        constraintName?: string,
8✔
2661
    ): Promise<void> {
8✔
2662
        const table = InstanceChecker.isTable(tableOrName)
8✔
2663
            ? tableOrName
8!
2664
            : await this.getCachedTable(tableOrName)
8✔
2665
        const clonedTable = table.clone()
8✔
2666

8✔
2667
        const up = this.createPrimaryKeySql(table, columnNames, constraintName)
8✔
2668

8✔
2669
        // mark columns as primary, because dropPrimaryKeySql build constraint name from table primary column names.
8✔
2670
        clonedTable.columns.forEach((column) => {
8✔
2671
            if (columnNames.find((columnName) => columnName === column.name))
20✔
2672
                column.isPrimary = true
20✔
2673
        })
8✔
2674
        const down = this.dropPrimaryKeySql(clonedTable)
8✔
2675

8✔
2676
        await this.executeQueries(up, down)
8✔
2677
        this.replaceCachedTable(table, clonedTable)
8✔
2678
    }
8✔
2679

26✔
2680
    /**
26✔
2681
     * Updates composite primary keys.
26✔
2682
     * @param tableOrName
26✔
2683
     * @param columns
26✔
2684
     */
26✔
2685
    async updatePrimaryKeys(
26✔
2686
        tableOrName: Table | string,
12✔
2687
        columns: TableColumn[],
12✔
2688
    ): Promise<void> {
12✔
2689
        const table = InstanceChecker.isTable(tableOrName)
12✔
2690
            ? tableOrName
12✔
2691
            : await this.getCachedTable(tableOrName)
12!
2692
        const clonedTable = table.clone()
×
2693
        const columnNames = columns.map((column) => column.name)
✔
2694
        const upQueries: Query[] = []
×
2695
        const downQueries: Query[] = []
×
2696

×
2697
        // if table already have primary columns, we must drop them.
×
2698
        const primaryColumns = clonedTable.primaryColumns
×
2699
        if (primaryColumns.length > 0) {
12✔
2700
            const pkName = primaryColumns[0].primaryKeyConstraintName
12✔
2701
                ? primaryColumns[0].primaryKeyConstraintName
12!
2702
                : this.connection.namingStrategy.primaryKeyName(
12✔
2703
                      clonedTable,
12✔
2704
                      primaryColumns.map((column) => column.name),
12✔
2705
                  )
12✔
2706

12✔
2707
            const columnNamesString = primaryColumns
12✔
2708
                .map((column) => `"${column.name}"`)
12✔
2709
                .join(", ")
12✔
2710

12✔
2711
            upQueries.push(
12✔
2712
                new Query(
12✔
2713
                    `ALTER TABLE ${this.escapePath(
12✔
2714
                        table,
12✔
2715
                    )} DROP CONSTRAINT "${pkName}"`,
12✔
2716
                ),
12✔
2717
            )
12✔
2718
            downQueries.push(
12✔
2719
                new Query(
12✔
2720
                    `ALTER TABLE ${this.escapePath(
12✔
2721
                        table,
12✔
2722
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
12✔
2723
                ),
12✔
2724
            )
12✔
2725
        }
12✔
2726

12✔
2727
        // update columns in table.
12✔
2728
        clonedTable.columns
12✔
2729
            .filter((column) => columnNames.indexOf(column.name) !== -1)
12✔
2730
            .forEach((column) => (column.isPrimary = true))
12✔
2731

12✔
2732
        const pkName = primaryColumns[0]?.primaryKeyConstraintName
12✔
2733
            ? primaryColumns[0].primaryKeyConstraintName
12!
2734
            : this.connection.namingStrategy.primaryKeyName(
12✔
2735
                  clonedTable,
12✔
2736
                  columnNames,
12✔
2737
              )
12✔
2738

12✔
2739
        const columnNamesString = columnNames
12✔
2740
            .map((columnName) => `"${columnName}"`)
12✔
2741
            .join(", ")
12✔
2742

12✔
2743
        upQueries.push(
12✔
2744
            new Query(
12✔
2745
                `ALTER TABLE ${this.escapePath(
12✔
2746
                    table,
12✔
2747
                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
12✔
2748
            ),
12✔
2749
        )
12✔
2750
        downQueries.push(
12✔
2751
            new Query(
12✔
2752
                `ALTER TABLE ${this.escapePath(
12✔
2753
                    table,
12✔
2754
                )} DROP CONSTRAINT "${pkName}"`,
12✔
2755
            ),
12✔
2756
        )
12✔
2757

12✔
2758
        await this.executeQueries(upQueries, downQueries)
12✔
2759
        this.replaceCachedTable(table, clonedTable)
12✔
2760
    }
12✔
2761

26✔
2762
    /**
26✔
2763
     * Drops a primary key.
26✔
2764
     * @param tableOrName
26✔
2765
     * @param constraintName
26✔
2766
     */
26✔
2767
    async dropPrimaryKey(
26✔
2768
        tableOrName: Table | string,
12✔
2769
        constraintName?: string,
12✔
2770
    ): Promise<void> {
12✔
2771
        const table = InstanceChecker.isTable(tableOrName)
12✔
2772
            ? tableOrName
12✔
2773
            : await this.getCachedTable(tableOrName)
12!
2774
        const up = this.dropPrimaryKeySql(table)
×
2775
        const down = this.createPrimaryKeySql(
×
2776
            table,
×
2777
            table.primaryColumns.map((column) => column.name),
✔
2778
            constraintName,
×
2779
        )
×
2780
        await this.executeQueries(up, down)
×
2781
        table.primaryColumns.forEach((column) => {
12✔
2782
            column.isPrimary = false
12✔
2783
        })
12✔
2784
    }
12✔
2785

26✔
2786
    /**
26✔
2787
     * Creates new unique constraint.
26✔
2788
     * @param tableOrName
26✔
2789
     * @param uniqueConstraint
26✔
2790
     */
26✔
2791
    async createUniqueConstraint(
26✔
2792
        tableOrName: Table | string,
40✔
2793
        uniqueConstraint: TableUnique,
40✔
2794
    ): Promise<void> {
40✔
2795
        const table = InstanceChecker.isTable(tableOrName)
40✔
2796
            ? tableOrName
40✔
2797
            : await this.getCachedTable(tableOrName)
40✔
2798

24✔
2799
        // new unique constraint may be passed without name. In this case we generate unique name manually.
24✔
2800
        if (!uniqueConstraint.name)
24✔
2801
            uniqueConstraint.name =
40✔
2802
                this.connection.namingStrategy.uniqueConstraintName(
8✔
2803
                    table,
8✔
2804
                    uniqueConstraint.columnNames,
8✔
2805
                )
8✔
2806

40✔
2807
        const up = this.createUniqueConstraintSql(table, uniqueConstraint)
40✔
2808
        const down = this.dropUniqueConstraintSql(table, uniqueConstraint)
40✔
2809
        await this.executeQueries(up, down)
40✔
2810
        table.addUniqueConstraint(uniqueConstraint)
40✔
2811
    }
40✔
2812

26✔
2813
    /**
26✔
2814
     * Creates new unique constraints.
26✔
2815
     * @param tableOrName
26✔
2816
     * @param uniqueConstraints
26✔
2817
     */
26✔
2818
    async createUniqueConstraints(
26✔
2819
        tableOrName: Table | string,
20✔
2820
        uniqueConstraints: TableUnique[],
20✔
2821
    ): Promise<void> {
20✔
2822
        for (const uniqueConstraint of uniqueConstraints) {
20✔
2823
            await this.createUniqueConstraint(tableOrName, uniqueConstraint)
32✔
2824
        }
32✔
2825
    }
20✔
2826

26✔
2827
    /**
26✔
2828
     * Drops unique constraint.
26✔
2829
     * @param tableOrName
26✔
2830
     * @param uniqueOrName
26✔
2831
     */
26✔
2832
    async dropUniqueConstraint(
26✔
2833
        tableOrName: Table | string,
40✔
2834
        uniqueOrName: TableUnique | string,
40✔
2835
    ): Promise<void> {
40✔
2836
        const table = InstanceChecker.isTable(tableOrName)
40✔
2837
            ? tableOrName
40✔
2838
            : await this.getCachedTable(tableOrName)
40✔
2839
        const uniqueConstraint = InstanceChecker.isTableUnique(uniqueOrName)
16✔
2840
            ? uniqueOrName
40✔
2841
            : table.uniques.find((u) => u.name === uniqueOrName)
40!
2842
        if (!uniqueConstraint)
40✔
2843
            throw new TypeORMError(
40!
2844
                `Supplied unique constraint was not found in table ${table.name}`,
×
2845
            )
×
2846

40✔
2847
        const up = this.dropUniqueConstraintSql(table, uniqueConstraint)
40✔
2848
        const down = this.createUniqueConstraintSql(table, uniqueConstraint)
40✔
2849
        await this.executeQueries(up, down)
40✔
2850
        table.removeUniqueConstraint(uniqueConstraint)
40✔
2851
    }
40✔
2852

26✔
2853
    /**
26✔
2854
     * Drops unique constraints.
26✔
2855
     * @param tableOrName
26✔
2856
     * @param uniqueConstraints
26✔
2857
     */
26✔
2858
    async dropUniqueConstraints(
26✔
2859
        tableOrName: Table | string,
24✔
2860
        uniqueConstraints: TableUnique[],
24✔
2861
    ): Promise<void> {
24✔
2862
        for (const uniqueConstraint of [...uniqueConstraints]) {
24✔
2863
            await this.dropUniqueConstraint(tableOrName, uniqueConstraint)
36✔
2864
        }
36✔
2865
    }
24✔
2866

26✔
2867
    /**
26✔
2868
     * Creates new check constraint.
26✔
2869
     * @param tableOrName
26✔
2870
     * @param checkConstraint
26✔
2871
     */
26✔
2872
    async createCheckConstraint(
26✔
2873
        tableOrName: Table | string,
20✔
2874
        checkConstraint: TableCheck,
20✔
2875
    ): Promise<void> {
20✔
2876
        const table = InstanceChecker.isTable(tableOrName)
20✔
2877
            ? tableOrName
20✔
2878
            : await this.getCachedTable(tableOrName)
20✔
2879

12✔
2880
        // new unique constraint may be passed without name. In this case we generate unique name manually.
12✔
2881
        if (!checkConstraint.name)
12✔
2882
            checkConstraint.name =
12✔
2883
                this.connection.namingStrategy.checkConstraintName(
12✔
2884
                    table,
12✔
2885
                    checkConstraint.expression!,
12✔
2886
                )
12✔
2887

20✔
2888
        const up = this.createCheckConstraintSql(table, checkConstraint)
20✔
2889
        const down = this.dropCheckConstraintSql(table, checkConstraint)
20✔
2890
        await this.executeQueries(up, down)
20✔
2891
        table.addCheckConstraint(checkConstraint)
20✔
2892
    }
20✔
2893

26✔
2894
    /**
26✔
2895
     * Creates new check constraints.
26✔
2896
     * @param tableOrName
26✔
2897
     * @param checkConstraints
26✔
2898
     */
26✔
2899
    async createCheckConstraints(
26✔
2900
        tableOrName: Table | string,
8✔
2901
        checkConstraints: TableCheck[],
8✔
2902
    ): Promise<void> {
8✔
2903
        const promises = checkConstraints.map((checkConstraint) =>
8✔
2904
            this.createCheckConstraint(tableOrName, checkConstraint),
8✔
2905
        )
8✔
2906
        await Promise.all(promises)
8✔
2907
    }
8✔
2908

26✔
2909
    /**
26✔
2910
     * Drops check constraint.
26✔
2911
     * @param tableOrName
26✔
2912
     * @param checkOrName
26✔
2913
     */
26✔
2914
    async dropCheckConstraint(
26✔
2915
        tableOrName: Table | string,
12✔
2916
        checkOrName: TableCheck | string,
12✔
2917
    ): Promise<void> {
12✔
2918
        const table = InstanceChecker.isTable(tableOrName)
12✔
2919
            ? tableOrName
12✔
2920
            : await this.getCachedTable(tableOrName)
12!
2921
        const checkConstraint = InstanceChecker.isTableCheck(checkOrName)
×
2922
            ? checkOrName
12✔
2923
            : table.checks.find((c) => c.name === checkOrName)
12!
2924
        if (!checkConstraint)
12✔
2925
            throw new TypeORMError(
12!
2926
                `Supplied check constraint was not found in table ${table.name}`,
×
2927
            )
×
2928

12✔
2929
        const up = this.dropCheckConstraintSql(table, checkConstraint)
12✔
2930
        const down = this.createCheckConstraintSql(table, checkConstraint)
12✔
2931
        await this.executeQueries(up, down)
12✔
2932
        table.removeCheckConstraint(checkConstraint)
12✔
2933
    }
12✔
2934

26✔
2935
    /**
26✔
2936
     * Drops check constraints.
26✔
2937
     * @param tableOrName
26✔
2938
     * @param checkConstraints
26✔
2939
     */
26✔
2940
    async dropCheckConstraints(
26✔
2941
        tableOrName: Table | string,
8✔
2942
        checkConstraints: TableCheck[],
8✔
2943
    ): Promise<void> {
8✔
2944
        const promises = checkConstraints.map((checkConstraint) =>
8✔
2945
            this.dropCheckConstraint(tableOrName, checkConstraint),
8✔
2946
        )
8✔
2947
        await Promise.all(promises)
8✔
2948
    }
8✔
2949

26✔
2950
    /**
26✔
2951
     * Creates new exclusion constraint.
26✔
2952
     * @param tableOrName
26✔
2953
     * @param exclusionConstraint
26✔
2954
     */
26✔
2955
    async createExclusionConstraint(
26✔
2956
        tableOrName: Table | string,
16✔
2957
        exclusionConstraint: TableExclusion,
16✔
2958
    ): Promise<void> {
16✔
2959
        const table = InstanceChecker.isTable(tableOrName)
16✔
2960
            ? tableOrName
16✔
2961
            : await this.getCachedTable(tableOrName)
16✔
2962

8✔
2963
        // new unique constraint may be passed without name. In this case we generate unique name manually.
8✔
2964
        if (!exclusionConstraint.name)
8✔
2965
            exclusionConstraint.name =
8✔
2966
                this.connection.namingStrategy.exclusionConstraintName(
8✔
2967
                    table,
8✔
2968
                    exclusionConstraint.expression!,
8✔
2969
                )
8✔
2970

16✔
2971
        const up = this.createExclusionConstraintSql(table, exclusionConstraint)
16✔
2972
        const down = this.dropExclusionConstraintSql(table, exclusionConstraint)
16✔
2973
        await this.executeQueries(up, down)
16✔
2974
        table.addExclusionConstraint(exclusionConstraint)
16✔
2975
    }
16✔
2976

26✔
2977
    /**
26✔
2978
     * Creates new exclusion constraints.
26✔
2979
     * @param tableOrName
26✔
2980
     * @param exclusionConstraints
26✔
2981
     */
26✔
2982
    async createExclusionConstraints(
26✔
2983
        tableOrName: Table | string,
12✔
2984
        exclusionConstraints: TableExclusion[],
12✔
2985
    ): Promise<void> {
12✔
2986
        const promises = exclusionConstraints.map((exclusionConstraint) =>
12✔
2987
            this.createExclusionConstraint(tableOrName, exclusionConstraint),
12✔
2988
        )
12✔
2989
        await Promise.all(promises)
12✔
2990
    }
12✔
2991

26✔
2992
    /**
26✔
2993
     * Drops exclusion constraint.
26✔
2994
     * @param tableOrName
26✔
2995
     * @param exclusionOrName
26✔
2996
     */
26✔
2997
    async dropExclusionConstraint(
26✔
2998
        tableOrName: Table | string,
12✔
2999
        exclusionOrName: TableExclusion | string,
12✔
3000
    ): Promise<void> {
12✔
3001
        const table = InstanceChecker.isTable(tableOrName)
12✔
3002
            ? tableOrName
12✔
3003
            : await this.getCachedTable(tableOrName)
12!
3004
        const exclusionConstraint = InstanceChecker.isTableExclusion(
×
3005
            exclusionOrName,
×
3006
        )
×
3007
            ? exclusionOrName
12✔
3008
            : table.exclusions.find((c) => c.name === exclusionOrName)
12!
3009
        if (!exclusionConstraint)
12✔
3010
            throw new TypeORMError(
12!
3011
                `Supplied exclusion constraint was not found in table ${table.name}`,
×
3012
            )
×
3013

12✔
3014
        const up = this.dropExclusionConstraintSql(table, exclusionConstraint)
12✔
3015
        const down = this.createExclusionConstraintSql(
12✔
3016
            table,
12✔
3017
            exclusionConstraint,
12✔
3018
        )
12✔
3019
        await this.executeQueries(up, down)
12✔
3020
        table.removeExclusionConstraint(exclusionConstraint)
12✔
3021
    }
12✔
3022

26✔
3023
    /**
26✔
3024
     * Drops exclusion constraints.
26✔
3025
     * @param tableOrName
26✔
3026
     * @param exclusionConstraints
26✔
3027
     */
26✔
3028
    async dropExclusionConstraints(
26✔
3029
        tableOrName: Table | string,
8✔
3030
        exclusionConstraints: TableExclusion[],
8✔
3031
    ): Promise<void> {
8✔
3032
        const promises = exclusionConstraints.map((exclusionConstraint) =>
8✔
3033
            this.dropExclusionConstraint(tableOrName, exclusionConstraint),
8✔
3034
        )
8✔
3035
        await Promise.all(promises)
8✔
3036
    }
8✔
3037

26✔
3038
    /**
26✔
3039
     * Creates a new foreign key.
26✔
3040
     * @param tableOrName
26✔
3041
     * @param foreignKey
26✔
3042
     */
26✔
3043
    async createForeignKey(
26✔
3044
        tableOrName: Table | string,
15,704✔
3045
        foreignKey: TableForeignKey,
15,704✔
3046
    ): Promise<void> {
15,704✔
3047
        const table = InstanceChecker.isTable(tableOrName)
15,704✔
3048
            ? tableOrName
15,704✔
3049
            : await this.getCachedTable(tableOrName)
15,704✔
3050

20✔
3051
        // new FK may be passed without name. In this case we generate FK name manually.
20✔
3052
        if (!foreignKey.name)
20✔
3053
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
15,704✔
3054
                table,
4✔
3055
                foreignKey.columnNames,
4✔
3056
                this.getTablePath(foreignKey),
4✔
3057
                foreignKey.referencedColumnNames,
4✔
3058
            )
4✔
3059

15,704✔
3060
        const up = this.createForeignKeySql(table, foreignKey)
15,704✔
3061
        const down = this.dropForeignKeySql(table, foreignKey)
15,704✔
3062
        await this.executeQueries(up, down)
15,704✔
3063
        table.addForeignKey(foreignKey)
15,704✔
3064
    }
15,704✔
3065

26✔
3066
    /**
26✔
3067
     * Creates a new foreign keys.
26✔
3068
     * @param tableOrName
26✔
3069
     * @param foreignKeys
26✔
3070
     */
26✔
3071
    async createForeignKeys(
26✔
3072
        tableOrName: Table | string,
9,436✔
3073
        foreignKeys: TableForeignKey[],
9,436✔
3074
    ): Promise<void> {
9,436✔
3075
        for (const foreignKey of foreignKeys) {
9,436✔
3076
            await this.createForeignKey(tableOrName, foreignKey)
15,700✔
3077
        }
15,700✔
3078
    }
9,436✔
3079

26✔
3080
    /**
26✔
3081
     * Drops a foreign key from the table.
26✔
3082
     * @param tableOrName
26✔
3083
     * @param foreignKeyOrName
26✔
3084
     */
26✔
3085
    async dropForeignKey(
26✔
3086
        tableOrName: Table | string,
72✔
3087
        foreignKeyOrName: TableForeignKey | string,
72✔
3088
    ): Promise<void> {
72✔
3089
        const table = InstanceChecker.isTable(tableOrName)
72✔
3090
            ? tableOrName
72✔
3091
            : await this.getCachedTable(tableOrName)
72✔
3092
        const foreignKey = InstanceChecker.isTableForeignKey(foreignKeyOrName)
16✔
3093
            ? foreignKeyOrName
72✔
3094
            : table.foreignKeys.find((fk) => fk.name === foreignKeyOrName)
72!
3095
        if (!foreignKey)
72✔
3096
            throw new TypeORMError(
72!
3097
                `Supplied foreign key was not found in table ${table.name}`,
×
3098
            )
×
3099

72✔
3100
        if (!foreignKey.name) {
72!
3101
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
×
3102
                table,
×
3103
                foreignKey.columnNames,
×
3104
                this.getTablePath(foreignKey),
×
3105
                foreignKey.referencedColumnNames,
×
3106
            )
×
3107
        }
×
3108

72✔
3109
        const up = this.dropForeignKeySql(table, foreignKey)
72✔
3110
        const down = this.createForeignKeySql(table, foreignKey)
72✔
3111
        await this.executeQueries(up, down)
72✔
3112
        table.removeForeignKey(foreignKey)
72✔
3113
    }
72✔
3114

26✔
3115
    /**
26✔
3116
     * Drops a foreign keys from the table.
26✔
3117
     * @param tableOrName
26✔
3118
     * @param foreignKeys
26✔
3119
     */
26✔
3120
    async dropForeignKeys(
26✔
3121
        tableOrName: Table | string,
56✔
3122
        foreignKeys: TableForeignKey[],
56✔
3123
    ): Promise<void> {
56✔
3124
        for (const foreignKey of [...foreignKeys]) {
56✔
3125
            await this.dropForeignKey(tableOrName, foreignKey)
68✔
3126
        }
68✔
3127
    }
56✔
3128

26✔
3129
    /**
26✔
3130
     * Creates a new index.
26✔
3131
     * @param tableOrName
26✔
3132
     * @param index
26✔
3133
     */
26✔
3134
    async createIndex(
26✔
3135
        tableOrName: Table | string,
64✔
3136
        index: TableIndex,
64✔
3137
    ): Promise<void> {
64✔
3138
        const table = InstanceChecker.isTable(tableOrName)
64✔
3139
            ? tableOrName
64✔
3140
            : await this.getCachedTable(tableOrName)
64✔
3141

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

64✔
3145
        const up = this.createIndexSql(table, index)
64✔
3146
        const down = this.dropIndexSql(table, index)
64✔
3147
        await this.executeQueries(up, down)
64✔
3148
        table.addIndex(index)
64✔
3149
    }
64✔
3150

26✔
3151
    /**
26✔
3152
     * Create a new view index.
26✔
3153
     * @param viewOrName
26✔
3154
     * @param index
26✔
3155
     */
26✔
3156
    async createViewIndex(
26✔
3157
        viewOrName: View | string,
16✔
3158
        index: TableIndex,
16✔
3159
    ): Promise<void> {
16✔
3160
        const view = InstanceChecker.isView(viewOrName)
16✔
3161
            ? viewOrName
16✔
3162
            : await this.getCachedView(viewOrName)
16!
3163

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

16✔
3167
        const up = this.createViewIndexSql(view, index)
16✔
3168
        const down = this.dropIndexSql(view, index)
16✔
3169
        await this.executeQueries(up, down)
16✔
3170
        view.addIndex(index)
16✔
3171
    }
16✔
3172

26✔
3173
    /**
26✔
3174
     * Creates a new indices
26✔
3175
     * @param tableOrName
26✔
3176
     * @param indices
26✔
3177
     */
26✔
3178
    async createIndices(
26✔
3179
        tableOrName: Table | string,
36✔
3180
        indices: TableIndex[],
36✔
3181
    ): Promise<void> {
36✔
3182
        for (const index of indices) {
36✔
3183
            await this.createIndex(tableOrName, index)
48✔
3184
        }
48✔
3185
    }
36✔
3186

26✔
3187
    /**
26✔
3188
     * Creates new view indices
26✔
3189
     * @param viewOrName
26✔
3190
     * @param indices
26✔
3191
     */
26✔
3192
    async createViewIndices(
26✔
3193
        viewOrName: View | string,
16✔
3194
        indices: TableIndex[],
16✔
3195
    ): Promise<void> {
16✔
3196
        for (const index of indices) {
16✔
3197
            await this.createViewIndex(viewOrName, index)
16✔
3198
        }
16✔
3199
    }
16✔
3200

26✔
3201
    /**
26✔
3202
     * Drops an index from the table.
26✔
3203
     * @param tableOrName
26✔
3204
     * @param indexOrName
26✔
3205
     */
26✔
3206
    async dropIndex(
26✔
3207
        tableOrName: Table | string,
76✔
3208
        indexOrName: TableIndex | string,
76✔
3209
    ): Promise<void> {
76✔
3210
        const table = InstanceChecker.isTable(tableOrName)
76✔
3211
            ? tableOrName
76✔
3212
            : await this.getCachedTable(tableOrName)
76✔
3213
        const index = InstanceChecker.isTableIndex(indexOrName)
20✔
3214
            ? indexOrName
76✔
3215
            : table.indices.find((i) => i.name === indexOrName)
76!
3216
        if (!index)
76✔
3217
            throw new TypeORMError(
76!
3218
                `Supplied index ${indexOrName} was not found in table ${table.name}`,
×
3219
            )
×
3220
        // old index may be passed without name. In this case we generate index name manually.
76✔
3221
        if (!index.name) index.name = this.generateIndexName(table, index)
76✔
3222

76✔
3223
        const up = this.dropIndexSql(table, index)
76✔
3224
        const down = this.createIndexSql(table, index)
76✔
3225
        await this.executeQueries(up, down)
76✔
3226
        table.removeIndex(index)
76✔
3227
    }
76✔
3228

26✔
3229
    /**
26✔
3230
     * Drops an index from a view.
26✔
3231
     * @param viewOrName
26✔
3232
     * @param indexOrName
26✔
3233
     */
26✔
3234
    async dropViewIndex(
26✔
3235
        viewOrName: View | string,
12✔
3236
        indexOrName: TableIndex | string,
12✔
3237
    ): Promise<void> {
12✔
3238
        const view = InstanceChecker.isView(viewOrName)
12✔
3239
            ? viewOrName
12✔
3240
            : await this.getCachedView(viewOrName)
12!
3241
        const index = InstanceChecker.isTableIndex(indexOrName)
×
3242
            ? indexOrName
12✔
3243
            : view.indices.find((i) => i.name === indexOrName)
12!
3244
        if (!index)
12✔
3245
            throw new TypeORMError(
12!
3246
                `Supplied index ${indexOrName} was not found in view ${view.name}`,
×
3247
            )
×
3248
        // old index may be passed without name. In this case we generate index name manually.
12✔
3249
        if (!index.name) index.name = this.generateIndexName(view, index)
12!
3250

12✔
3251
        const up = this.dropIndexSql(view, index)
12✔
3252
        const down = this.createViewIndexSql(view, index)
12✔
3253
        await this.executeQueries(up, down)
12✔
3254
        view.removeIndex(index)
12✔
3255
    }
12✔
3256

26✔
3257
    /**
26✔
3258
     * Drops an indices from the table.
26✔
3259
     * @param tableOrName
26✔
3260
     * @param indices
26✔
3261
     */
26✔
3262
    async dropIndices(
26✔
3263
        tableOrName: Table | string,
8✔
3264
        indices: TableIndex[],
8✔
3265
    ): Promise<void> {
8✔
3266
        for (const index of [...indices]) {
8✔
3267
            await this.dropIndex(tableOrName, index)
24✔
3268
        }
24✔
3269
    }
8✔
3270

26✔
3271
    /**
26✔
3272
     * Clears all table contents.
26✔
3273
     * Note: this operation uses SQL's TRUNCATE query which cannot be reverted in transactions.
26✔
3274
     * @param tableName
26✔
3275
     */
26✔
3276
    async clearTable(tableName: string): Promise<void> {
26✔
3277
        await this.query(`TRUNCATE TABLE ${this.escapePath(tableName)}`)
24✔
3278
    }
24✔
3279

26✔
3280
    /**
26✔
3281
     * Removes all tables from the currently connected database.
26✔
3282
     */
26✔
3283
    async clearDatabase(): Promise<void> {
26✔
3284
        const schemas: string[] = []
7,588✔
3285
        this.connection.entityMetadatas
7,588✔
3286
            .filter((metadata) => metadata.schema)
7,588✔
3287
            .forEach((metadata) => {
7,588✔
3288
                const isSchemaExist = !!schemas.find(
192✔
3289
                    (schema) => schema === metadata.schema,
192✔
3290
                )
192✔
3291
                if (!isSchemaExist) schemas.push(metadata.schema!)
192✔
3292
            })
7,588✔
3293
        schemas.push(this.driver.options.schema || "current_schema()")
7,588✔
3294
        const schemaNamesString = schemas
7,588✔
3295
            .map((name) => {
7,588✔
3296
                return name === "current_schema()" ? name : "'" + name + "'"
7,736✔
3297
            })
7,588✔
3298
            .join(", ")
7,588✔
3299

7,588✔
3300
        const isAnotherTransactionActive = this.isTransactionActive
7,588✔
3301
        if (!isAnotherTransactionActive) await this.startTransaction()
7,588✔
3302
        try {
7,588✔
3303
            // drop views
7,588✔
3304
            const selectViewDropsQuery =
7,588✔
3305
                `SELECT 'DROP VIEW IF EXISTS ' || quote_ident(schemaname) || '.' || quote_ident(viewname) || ' CASCADE;' as "query" ` +
7,588✔
3306
                `FROM "pg_views" WHERE "schemaname" IN (${schemaNamesString}) AND "viewname" NOT IN ('geography_columns', 'geometry_columns', 'raster_columns', 'raster_overviews')`
7,588✔
3307
            const dropViewQueries: ObjectLiteral[] =
7,588✔
3308
                await this.query(selectViewDropsQuery)
7,588✔
3309
            await Promise.all(
7,588✔
3310
                dropViewQueries.map((q) => this.query(q["query"])),
7,588✔
3311
            )
7,588✔
3312

7,588✔
3313
            // drop materialized views
7,588✔
3314
            // Note: materialized views introduced in Postgres 9.3
7,588✔
3315
            if (DriverUtils.isReleaseVersionOrGreater(this.driver, "9.3")) {
7,588✔
3316
                const selectMatViewDropsQuery =
7,588✔
3317
                    `SELECT 'DROP MATERIALIZED VIEW IF EXISTS ' || quote_ident(schemaname) || '.' || quote_ident(matviewname) || ' CASCADE;' as "query" ` +
7,588✔
3318
                    `FROM "pg_matviews" WHERE "schemaname" IN (${schemaNamesString})`
7,588✔
3319
                const dropMatViewQueries: ObjectLiteral[] = await this.query(
7,588✔
3320
                    selectMatViewDropsQuery,
7,588✔
3321
                )
7,588✔
3322
                await Promise.all(
7,588✔
3323
                    dropMatViewQueries.map((q) => this.query(q["query"])),
7,588✔
3324
                )
7,588✔
3325
            }
7,588✔
3326

7,588✔
3327
            // ignore spatial_ref_sys; it's a special table supporting PostGIS
7,588✔
3328
            // TODO generalize this as this.driver.ignoreTables
7,588✔
3329

7,588✔
3330
            // drop tables
7,588✔
3331
            const selectTableDropsQuery = `SELECT 'DROP TABLE IF EXISTS ' || quote_ident(schemaname) || '.' || quote_ident(tablename) || ' CASCADE;' as "query" FROM "pg_tables" WHERE "schemaname" IN (${schemaNamesString}) AND "tablename" NOT IN ('spatial_ref_sys')`
7,588✔
3332
            const dropTableQueries: ObjectLiteral[] = await this.query(
7,588✔
3333
                selectTableDropsQuery,
7,588✔
3334
            )
7,588✔
3335
            await Promise.all(
7,588✔
3336
                dropTableQueries.map((q) => this.query(q["query"])),
7,588✔
3337
            )
7,588✔
3338

7,588✔
3339
            // drop enum types
7,588✔
3340
            await this.dropEnumTypes(schemaNamesString)
7,588✔
3341

7,588✔
3342
            if (!isAnotherTransactionActive) {
7,588✔
3343
                await this.commitTransaction()
7,584✔
3344
            }
7,584✔
3345
        } catch (error) {
7,588!
3346
            try {
×
3347
                // we throw original error even if rollback thrown an error
×
3348
                if (!isAnotherTransactionActive) {
×
3349
                    await this.rollbackTransaction()
×
3350
                }
×
3351
            } catch {
×
3352
                // no-op
×
3353
            }
×
3354
            throw error
×
3355
        }
×
3356
    }
7,588✔
3357

26✔
3358
    // -------------------------------------------------------------------------
26✔
3359
    // Protected Methods
26✔
3360
    // -------------------------------------------------------------------------
26✔
3361

26✔
3362
    protected async loadViews(viewNames?: string[]): Promise<View[]> {
26✔
3363
        const hasTable = await this.hasTable(this.getTypeormMetadataTableName())
8,156✔
3364

8,156✔
3365
        if (!hasTable) return []
8,156✔
3366

188✔
3367
        if (!viewNames) {
8,156!
3368
            viewNames = []
×
3369
        }
×
3370

188✔
3371
        const currentDatabase = await this.getCurrentDatabase()
188✔
3372
        const currentSchema = await this.getCurrentSchema()
188✔
3373
        const viewsCondition =
188✔
3374
            viewNames.length === 0
188✔
3375
                ? "1=1"
8,156✔
3376
                : viewNames
8,156✔
3377
                      .map((tableName) => this.driver.parseTableName(tableName))
156✔
3378
                      .map(({ schema, tableName }) => {
156✔
3379
                          if (!schema) {
224!
3380
                              schema =
×
3381
                                  this.driver.options.schema || currentSchema
×
3382
                          }
×
3383

224✔
3384
                          return `("t"."schema" = '${schema}' AND "t"."name" = '${tableName}')`
224✔
3385
                      })
156✔
3386
                      .join(" OR ")
156✔
3387

8,156✔
3388
        const constraintsCondition =
8,156✔
3389
            viewNames.length === 0
8,156✔
3390
                ? "1=1"
8,156✔
3391
                : viewNames
8,156✔
3392
                      .map((tableName) => this.driver.parseTableName(tableName))
156✔
3393
                      .map(({ schema, tableName }) => {
156✔
3394
                          if (!schema) {
224!
3395
                              schema =
×
3396
                                  this.driver.options.schema || currentSchema
×
3397
                          }
×
3398

224✔
3399
                          return `("ns"."nspname" = '${schema}' AND "t"."relname" = '${tableName}')`
224✔
3400
                      })
156✔
3401
                      .join(" OR ")
156✔
3402

8,156✔
3403
        const indicesSql =
8,156✔
3404
            `SELECT "ns"."nspname" AS "table_schema", "t"."relname" AS "table_name", "i"."relname" AS "constraint_name", "a"."attname" AS "column_name", ` +
8,156✔
3405
            `CASE "ix"."indisunique" WHEN 't' THEN 'TRUE' ELSE'FALSE' END AS "is_unique", pg_get_expr("ix"."indpred", "ix"."indrelid") AS "condition", ` +
8,156✔
3406
            `"types"."typname" AS "type_name", "am"."amname" AS "index_type" ` +
8,156✔
3407
            `FROM "pg_class" "t" ` +
8,156✔
3408
            `INNER JOIN "pg_index" "ix" ON "ix"."indrelid" = "t"."oid" ` +
8,156✔
3409
            `INNER JOIN "pg_attribute" "a" ON "a"."attrelid" = "t"."oid"  AND "a"."attnum" = ANY ("ix"."indkey") ` +
8,156✔
3410
            `INNER JOIN "pg_namespace" "ns" ON "ns"."oid" = "t"."relnamespace" ` +
8,156✔
3411
            `INNER JOIN "pg_class" "i" ON "i"."oid" = "ix"."indexrelid" ` +
8,156✔
3412
            `INNER JOIN "pg_type" "types" ON "types"."oid" = "a"."atttypid" ` +
8,156✔
3413
            `INNER JOIN "pg_am" "am" ON "i"."relam" = "am"."oid" ` +
8,156✔
3414
            `LEFT JOIN "pg_constraint" "cnst" ON "cnst"."conname" = "i"."relname" ` +
8,156✔
3415
            `WHERE "t"."relkind" IN ('m') AND "cnst"."contype" IS NULL AND (${constraintsCondition})`
8,156✔
3416

8,156✔
3417
        const query =
8,156✔
3418
            `SELECT "t".* FROM ${this.escapePath(
8,156✔
3419
                this.getTypeormMetadataTableName(),
8,156✔
3420
            )} "t" ` +
8,156✔
3421
            `INNER JOIN "pg_catalog"."pg_class" "c" ON "c"."relname" = "t"."name" ` +
8,156✔
3422
            `INNER JOIN "pg_namespace" "n" ON "n"."oid" = "c"."relnamespace" AND "n"."nspname" = "t"."schema" ` +
8,156✔
3423
            `WHERE "t"."type" IN ('${MetadataTableType.VIEW}', '${
8,156✔
3424
                MetadataTableType.MATERIALIZED_VIEW
8,156✔
3425
            }') ${viewsCondition ? `AND (${viewsCondition})` : ""}`
8,156!
3426

8,156✔
3427
        const dbViews = await this.query(query)
8,156✔
3428
        const dbIndices: ObjectLiteral[] = await this.query(indicesSql)
188✔
3429
        return dbViews.map((dbView: any) => {
188✔
3430
            // find index constraints of table, group them by constraint name and build TableIndex.
88✔
3431
            const tableIndexConstraints = OrmUtils.uniq(
88✔
3432
                dbIndices.filter((dbIndex) => {
88✔
3433
                    return (
28✔
3434
                        dbIndex["table_name"] === dbView["name"] &&
28✔
3435
                        dbIndex["table_schema"] === dbView["schema"]
28✔
3436
                    )
28✔
3437
                }),
88✔
3438
                (dbIndex) => dbIndex["constraint_name"],
88✔
3439
            )
88✔
3440
            const view = new View()
88✔
3441
            const schema =
88✔
3442
                dbView["schema"] === currentSchema &&
88✔
3443
                !this.driver.options.schema
88✔
3444
                    ? undefined
88✔
3445
                    : dbView["schema"]
88!
3446
            view.database = currentDatabase
88✔
3447
            view.schema = dbView["schema"]
88✔
3448
            view.name = this.driver.buildTableName(dbView["name"], schema)
88✔
3449
            view.expression = dbView["value"]
88✔
3450
            view.materialized =
88✔
3451
                dbView["type"] === MetadataTableType.MATERIALIZED_VIEW
88✔
3452
            view.indices = tableIndexConstraints.map((constraint) => {
88✔
3453
                const indices = dbIndices.filter((index) => {
28✔
3454
                    return (
28✔
3455
                        index["table_schema"] === constraint["table_schema"] &&
28✔
3456
                        index["table_name"] === constraint["table_name"] &&
28✔
3457
                        index["constraint_name"] ===
28✔
3458
                            constraint["constraint_name"]
28✔
3459
                    )
28✔
3460
                })
28✔
3461

28✔
3462
                return new TableIndex(<TableIndexOptions>{
28✔
3463
                    view: view,
28✔
3464
                    name: constraint["constraint_name"],
28✔
3465
                    columnNames: indices.map((i) => i["column_name"]),
28✔
3466
                    isUnique: constraint["is_unique"] === "TRUE",
28✔
3467
                    where: constraint["condition"],
28✔
3468
                    isSpatial: constraint["index_type"] === "gist",
28✔
3469
                    isFulltext: false,
28✔
3470
                    type: constraint["index_type"],
28✔
3471
                })
28✔
3472
            })
88✔
3473
            return view
88✔
3474
        })
188✔
3475
    }
188✔
3476

26✔
3477
    /**
26✔
3478
     * Loads all tables (with given names) from the database and creates a Table from them.
26✔
3479
     * @param tableNames
26✔
3480
     */
26✔
3481
    protected async loadTables(tableNames?: string[]): Promise<Table[]> {
26✔
3482
        // if no tables given then no need to proceed
9,768✔
3483
        if (tableNames && tableNames.length === 0) {
9,768✔
3484
            return []
140✔
3485
        }
140✔
3486

9,628✔
3487
        const currentSchema = await this.getCurrentSchema()
9,628✔
3488
        const currentDatabase = await this.getCurrentDatabase()
9,628✔
3489

9,628✔
3490
        const dbTables: {
9,628✔
3491
            table_schema: string
9,628✔
3492
            table_name: string
9,628✔
3493
            table_comment: string
9,628✔
3494
        }[] = []
9,628✔
3495

9,628✔
3496
        if (!tableNames) {
9,768✔
3497
            const tablesSql = `SELECT "table_schema", "table_name", obj_description((quote_ident("table_schema") || '.' || quote_ident("table_name"))::regclass, 'pg_class') AS table_comment FROM "information_schema"."tables"`
4✔
3498
            dbTables.push(...(await this.query(tablesSql)))
4✔
3499
        } else {
9,768✔
3500
            const tablesCondition = tableNames
9,624✔
3501
                .map((tableName) => this.driver.parseTableName(tableName))
9,624✔
3502
                .map(({ schema, tableName }) => {
9,624✔
3503
                    return `("table_schema" = '${
26,948✔
3504
                        schema || currentSchema
26,948!
3505
                    }' AND "table_name" = '${tableName}')`
26,948✔
3506
                })
9,624✔
3507
                .join(" OR ")
9,624✔
3508

9,624✔
3509
            const tablesSql =
9,624✔
3510
                `SELECT "table_schema", "table_name", obj_description((quote_ident("table_schema") || '.' || quote_ident("table_name"))::regclass, 'pg_class') AS table_comment FROM "information_schema"."tables" WHERE ` +
9,624✔
3511
                tablesCondition
9,624✔
3512
            dbTables.push(...(await this.query(tablesSql)))
9,624✔
3513
        }
9,624✔
3514

9,628✔
3515
        // if tables were not found in the db, no need to proceed
9,628✔
3516
        if (dbTables.length === 0) {
9,768✔
3517
            return []
7,556✔
3518
        }
7,556✔
3519

2,072✔
3520
        /**
2,072✔
3521
         * Uses standard SQL information_schema.columns table and postgres-specific
2,072✔
3522
         * pg_catalog.pg_attribute table to get column information.
2,072✔
3523
         * @see https://stackoverflow.com/a/19541865
2,072✔
3524
         */
2,072✔
3525
        const columnsCondition = dbTables
2,072✔
3526
            .map(({ table_schema, table_name }) => {
2,072✔
3527
                return `("table_schema" = '${table_schema}' AND "table_name" = '${table_name}')`
3,824✔
3528
            })
2,072✔
3529
            .join(" OR ")
2,072✔
3530
        const columnsSql =
2,072✔
3531
            `SELECT columns.*, pg_catalog.col_description((quote_ident(table_catalog) || '.' || quote_ident(table_schema) || '.' || quote_ident(table_name))::regclass::oid, ordinal_position) AS description, ` +
2,072✔
3532
            `('"' || "udt_schema" || '"."' || "udt_name" || '"')::"regtype"::text AS "regtype", pg_catalog.format_type("col_attr"."atttypid", "col_attr"."atttypmod") AS "format_type" ` +
2,072✔
3533
            `FROM "information_schema"."columns" ` +
2,072✔
3534
            `LEFT JOIN "pg_catalog"."pg_attribute" AS "col_attr" ON "col_attr"."attname" = "columns"."column_name" ` +
2,072✔
3535
            `AND "col_attr"."attrelid" = ( ` +
2,072✔
3536
            `SELECT "cls"."oid" FROM "pg_catalog"."pg_class" AS "cls" ` +
2,072✔
3537
            `LEFT JOIN "pg_catalog"."pg_namespace" AS "ns" ON "ns"."oid" = "cls"."relnamespace" ` +
2,072✔
3538
            `WHERE "cls"."relname" = "columns"."table_name" ` +
2,072✔
3539
            `AND "ns"."nspname" = "columns"."table_schema" ` +
2,072✔
3540
            `) ` +
2,072✔
3541
            `WHERE ` +
2,072✔
3542
            columnsCondition
2,072✔
3543

2,072✔
3544
        const constraintsCondition = dbTables
2,072✔
3545
            .map(({ table_schema, table_name }) => {
2,072✔
3546
                return `("ns"."nspname" = '${table_schema}' AND "t"."relname" = '${table_name}')`
3,824✔
3547
            })
2,072✔
3548
            .join(" OR ")
2,072✔
3549

2,072✔
3550
        const constraintsSql =
2,072✔
3551
            `SELECT "ns"."nspname" AS "table_schema", "t"."relname" AS "table_name", "cnst"."conname" AS "constraint_name", ` +
2,072✔
3552
            `pg_get_constraintdef("cnst"."oid") AS "expression", ` +
2,072✔
3553
            `CASE "cnst"."contype" WHEN 'p' THEN 'PRIMARY' WHEN 'u' THEN 'UNIQUE' WHEN 'c' THEN 'CHECK' WHEN 'x' THEN 'EXCLUDE' END AS "constraint_type", "a"."attname" AS "column_name" ` +
2,072✔
3554
            `FROM "pg_constraint" "cnst" ` +
2,072✔
3555
            `INNER JOIN "pg_class" "t" ON "t"."oid" = "cnst"."conrelid" ` +
2,072✔
3556
            `INNER JOIN "pg_namespace" "ns" ON "ns"."oid" = "cnst"."connamespace" ` +
2,072✔
3557
            `LEFT JOIN "pg_attribute" "a" ON "a"."attrelid" = "cnst"."conrelid" AND "a"."attnum" = ANY ("cnst"."conkey") ` +
2,072✔
3558
            `WHERE "t"."relkind" IN ('r', 'p') AND (${constraintsCondition})`
2,072✔
3559

2,072✔
3560
        const indicesSql =
2,072✔
3561
            `SELECT "ns"."nspname" AS "table_schema", "t"."relname" AS "table_name", "i"."relname" AS "constraint_name", "a"."attname" AS "column_name", ` +
2,072✔
3562
            `CASE "ix"."indisunique" WHEN 't' THEN 'TRUE' ELSE'FALSE' END AS "is_unique", pg_get_expr("ix"."indpred", "ix"."indrelid") AS "condition", ` +
2,072✔
3563
            `"types"."typname" AS "type_name", "am"."amname" AS "index_type" ` +
2,072✔
3564
            `FROM "pg_class" "t" ` +
2,072✔
3565
            `INNER JOIN "pg_index" "ix" ON "ix"."indrelid" = "t"."oid" ` +
2,072✔
3566
            `INNER JOIN "pg_attribute" "a" ON "a"."attrelid" = "t"."oid"  AND "a"."attnum" = ANY ("ix"."indkey") ` +
2,072✔
3567
            `INNER JOIN "pg_namespace" "ns" ON "ns"."oid" = "t"."relnamespace" ` +
2,072✔
3568
            `INNER JOIN "pg_class" "i" ON "i"."oid" = "ix"."indexrelid" ` +
2,072✔
3569
            `INNER JOIN "pg_type" "types" ON "types"."oid" = "a"."atttypid" ` +
2,072✔
3570
            `INNER JOIN "pg_am" "am" ON "i"."relam" = "am"."oid" ` +
2,072✔
3571
            `LEFT JOIN "pg_constraint" "cnst" ON "cnst"."conname" = "i"."relname" ` +
2,072✔
3572
            `WHERE "t"."relkind" IN ('r', 'p') AND "cnst"."contype" IS NULL AND (${constraintsCondition})`
2,072✔
3573

2,072✔
3574
        const foreignKeysCondition = dbTables
2,072✔
3575
            .map(({ table_schema, table_name }) => {
2,072✔
3576
                return `("ns"."nspname" = '${table_schema}' AND "cl"."relname" = '${table_name}')`
3,824✔
3577
            })
2,072✔
3578
            .join(" OR ")
2,072✔
3579

2,072✔
3580
        const hasRelispartitionColumn =
2,072✔
3581
            await this.hasSupportForPartitionedTables()
2,072✔
3582
        const isPartitionCondition = hasRelispartitionColumn
2,072✔
3583
            ? ` AND "cl"."relispartition" = 'f'`
2,072✔
3584
            : ""
9,768!
3585

9,768✔
3586
        const foreignKeysSql =
9,768✔
3587
            `SELECT "con"."conname" AS "constraint_name", "con"."nspname" AS "table_schema", "con"."relname" AS "table_name", "att2"."attname" AS "column_name", ` +
9,768✔
3588
            `"ns"."nspname" AS "referenced_table_schema", "cl"."relname" AS "referenced_table_name", "att"."attname" AS "referenced_column_name", "con"."confdeltype" AS "on_delete", ` +
9,768✔
3589
            `"con"."confupdtype" AS "on_update", "con"."condeferrable" AS "deferrable", "con"."condeferred" AS "deferred" ` +
9,768✔
3590
            `FROM ( ` +
9,768✔
3591
            `SELECT UNNEST ("con1"."conkey") AS "parent", UNNEST ("con1"."confkey") AS "child", "con1"."confrelid", "con1"."conrelid", "con1"."conname", "con1"."contype", "ns"."nspname", ` +
9,768✔
3592
            `"cl"."relname", "con1"."condeferrable", ` +
9,768✔
3593
            `CASE WHEN "con1"."condeferred" THEN 'INITIALLY DEFERRED' ELSE 'INITIALLY IMMEDIATE' END as condeferred, ` +
9,768✔
3594
            `CASE "con1"."confdeltype" WHEN 'a' THEN 'NO ACTION' WHEN 'r' THEN 'RESTRICT' WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' END as "confdeltype", ` +
9,768✔
3595
            `CASE "con1"."confupdtype" WHEN 'a' THEN 'NO ACTION' WHEN 'r' THEN 'RESTRICT' WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' END as "confupdtype" ` +
9,768✔
3596
            `FROM "pg_class" "cl" ` +
9,768✔
3597
            `INNER JOIN "pg_namespace" "ns" ON "cl"."relnamespace" = "ns"."oid" ` +
9,768✔
3598
            `INNER JOIN "pg_constraint" "con1" ON "con1"."conrelid" = "cl"."oid" ` +
9,768✔
3599
            `WHERE "con1"."contype" = 'f' AND (${foreignKeysCondition}) ` +
9,768✔
3600
            `) "con" ` +
9,768✔
3601
            `INNER JOIN "pg_attribute" "att" ON "att"."attrelid" = "con"."confrelid" AND "att"."attnum" = "con"."child" ` +
9,768✔
3602
            `INNER JOIN "pg_class" "cl" ON "cl"."oid" = "con"."confrelid" ${isPartitionCondition}` +
9,768✔
3603
            `INNER JOIN "pg_namespace" "ns" ON "cl"."relnamespace" = "ns"."oid" ` +
9,768✔
3604
            `INNER JOIN "pg_attribute" "att2" ON "att2"."attrelid" = "con"."conrelid" AND "att2"."attnum" = "con"."parent"`
9,768✔
3605

9,768✔
3606
        const [
9,768✔
3607
            dbColumns,
9,768✔
3608
            dbConstraints,
9,768✔
3609
            dbIndices,
9,768✔
3610
            dbForeignKeys,
9,768✔
3611
        ]: ObjectLiteral[][] = await Promise.all([
9,768✔
3612
            this.query(columnsSql),
9,768✔
3613
            this.query(constraintsSql),
9,768✔
3614
            this.query(indicesSql),
9,768✔
3615
            this.query(foreignKeysSql),
9,768✔
3616
        ])
9,768✔
3617

2,072✔
3618
        // create tables for loaded tables
2,072✔
3619
        return Promise.all(
2,072✔
3620
            dbTables.map(async (dbTable) => {
2,072✔
3621
                const table = new Table()
3,824✔
3622

3,824✔
3623
                const getSchemaFromKey = (dbObject: any, key: string) => {
3,824✔
3624
                    return dbObject[key] === currentSchema &&
4,772✔
3625
                        (!this.driver.options.schema ||
3,556✔
3626
                            this.driver.options.schema === currentSchema)
3,556✔
3627
                        ? undefined
4,772✔
3628
                        : dbObject[key]
4,772✔
3629
                }
4,772✔
3630
                // We do not need to join schema name, when database is by default.
3,824✔
3631
                const schema = getSchemaFromKey(dbTable, "table_schema")
3,824✔
3632
                table.database = currentDatabase
3,824✔
3633
                table.schema = dbTable["table_schema"]
3,824✔
3634
                table.comment = dbTable["table_comment"]
3,824✔
3635
                table.name = this.driver.buildTableName(
3,824✔
3636
                    dbTable["table_name"],
3,824✔
3637
                    schema,
3,824✔
3638
                )
3,824✔
3639

3,824✔
3640
                // create columns from the loaded columns
3,824✔
3641
                table.columns = await Promise.all(
3,824✔
3642
                    dbColumns
3,824✔
3643
                        .filter(
3,824✔
3644
                            (dbColumn) =>
3,824✔
3645
                                dbColumn["table_name"] ===
1,946,100✔
3646
                                    dbTable["table_name"] &&
1,946,100✔
3647
                                dbColumn["table_schema"] ===
19,072✔
3648
                                    dbTable["table_schema"],
3,824✔
3649
                        )
3,824✔
3650
                        .map(async (dbColumn) => {
3,824✔
3651
                            const columnConstraints = dbConstraints.filter(
18,944✔
3652
                                (dbConstraint) => {
18,944✔
3653
                                    return (
1,841,508✔
3654
                                        dbConstraint["table_name"] ===
1,841,508✔
3655
                                            dbColumn["table_name"] &&
1,841,508✔
3656
                                        dbConstraint["table_schema"] ===
33,426✔
3657
                                            dbColumn["table_schema"] &&
1,841,508✔
3658
                                        dbConstraint["column_name"] ===
33,162✔
3659
                                            dbColumn["column_name"]
33,162✔
3660
                                    )
1,841,508✔
3661
                                },
18,944✔
3662
                            )
18,944✔
3663

18,944✔
3664
                            const tableColumn = new TableColumn()
18,944✔
3665
                            tableColumn.name = dbColumn["column_name"]
18,944✔
3666
                            tableColumn.type = dbColumn["regtype"].toLowerCase()
18,944✔
3667

18,944✔
3668
                            if (
18,944✔
3669
                                tableColumn.type === "vector" ||
18,944✔
3670
                                tableColumn.type === "halfvec"
18,928✔
3671
                            ) {
18,944✔
3672
                                const lengthMatch = dbColumn[
32✔
3673
                                    "format_type"
32✔
3674
                                ].match(/^(?:vector|halfvec)\((\d+)\)$/)
32✔
3675
                                if (lengthMatch && lengthMatch[1]) {
32✔
3676
                                    tableColumn.length = lengthMatch[1]
16✔
3677
                                }
16✔
3678
                            }
32✔
3679

18,944✔
3680
                            if (
18,944✔
3681
                                tableColumn.type === "numeric" ||
18,944✔
3682
                                tableColumn.type === "numeric[]" ||
18,944✔
3683
                                tableColumn.type === "decimal" ||
18,944✔
3684
                                tableColumn.type === "float"
18,892✔
3685
                            ) {
18,944✔
3686
                                let numericPrecision =
52✔
3687
                                    dbColumn["numeric_precision"]
52✔
3688
                                let numericScale = dbColumn["numeric_scale"]
52✔
3689
                                if (dbColumn["data_type"] === "ARRAY") {
52✔
3690
                                    const numericSize = dbColumn[
4✔
3691
                                        "format_type"
4✔
3692
                                    ].match(
4✔
3693
                                        /^numeric\(([0-9]+),([0-9]+)\)\[\]$/,
4✔
3694
                                    )
4✔
3695
                                    if (numericSize) {
4✔
3696
                                        numericPrecision = +numericSize[1]
4✔
3697
                                        numericScale = +numericSize[2]
4✔
3698
                                    }
4✔
3699
                                }
4✔
3700
                                // If one of these properties was set, and another was not, Postgres sets '0' in to unspecified property
52✔
3701
                                // we set 'undefined' in to unspecified property to avoid changing column on sync
52✔
3702
                                if (
52✔
3703
                                    numericPrecision !== null &&
52✔
3704
                                    !this.isDefaultColumnPrecision(
28✔
3705
                                        table,
28✔
3706
                                        tableColumn,
28✔
3707
                                        numericPrecision,
28✔
3708
                                    )
52✔
3709
                                ) {
52✔
3710
                                    tableColumn.precision = numericPrecision
28✔
3711
                                } else if (
52✔
3712
                                    numericScale !== null &&
24!
3713
                                    !this.isDefaultColumnScale(
×
3714
                                        table,
×
3715
                                        tableColumn,
×
3716
                                        numericScale,
×
3717
                                    )
24✔
3718
                                ) {
24!
3719
                                    tableColumn.precision = undefined
×
3720
                                }
×
3721
                                if (
52✔
3722
                                    numericScale !== null &&
52✔
3723
                                    !this.isDefaultColumnScale(
28✔
3724
                                        table,
28✔
3725
                                        tableColumn,
28✔
3726
                                        numericScale,
28✔
3727
                                    )
52✔
3728
                                ) {
52✔
3729
                                    tableColumn.scale = numericScale
28✔
3730
                                } else if (
52✔
3731
                                    numericPrecision !== null &&
24!
3732
                                    !this.isDefaultColumnPrecision(
×
3733
                                        table,
×
3734
                                        tableColumn,
×
3735
                                        numericPrecision,
×
3736
                                    )
24✔
3737
                                ) {
24!
3738
                                    tableColumn.scale = undefined
×
3739
                                }
×
3740
                            }
52✔
3741

18,944✔
3742
                            if (
18,944✔
3743
                                tableColumn.type === "interval" ||
18,944✔
3744
                                tableColumn.type === "time without time zone" ||
18,944✔
3745
                                tableColumn.type === "time with time zone" ||
18,944✔
3746
                                tableColumn.type ===
18,828✔
3747
                                    "timestamp without time zone" ||
18,944✔
3748
                                tableColumn.type === "timestamp with time zone"
18,696✔
3749
                            ) {
18,944✔
3750
                                tableColumn.precision =
476✔
3751
                                    !this.isDefaultColumnPrecision(
476✔
3752
                                        table,
476✔
3753
                                        tableColumn,
476✔
3754
                                        dbColumn["datetime_precision"],
476✔
3755
                                    )
476✔
3756
                                        ? dbColumn["datetime_precision"]
476✔
3757
                                        : undefined
476✔
3758
                            }
476✔
3759

18,944✔
3760
                            // check if column has user-defined data type.
18,944✔
3761
                            // NOTE: if ENUM type defined with "array:true" it comes with ARRAY type instead of USER-DEFINED
18,944✔
3762
                            if (
18,944✔
3763
                                dbColumn["data_type"] === "USER-DEFINED" ||
18,944✔
3764
                                dbColumn["data_type"] === "ARRAY"
18,284✔
3765
                            ) {
18,944✔
3766
                                const { name } =
1,290✔
3767
                                    await this.getUserDefinedTypeName(
1,290✔
3768
                                        table,
1,290✔
3769
                                        tableColumn,
1,290✔
3770
                                    )
1,290✔
3771

1,290✔
3772
                                // check if `enumName` is specified by user
1,290✔
3773
                                const builtEnumName = this.buildEnumName(
1,290✔
3774
                                    table,
1,290✔
3775
                                    tableColumn,
1,290✔
3776
                                    false,
1,290✔
3777
                                    true,
1,290✔
3778
                                )
1,290✔
3779
                                const enumName =
1,290✔
3780
                                    builtEnumName !== name ? name : undefined
1,290✔
3781

1,290✔
3782
                                // check if type is ENUM
1,290✔
3783
                                const sql =
1,290✔
3784
                                    `SELECT "e"."enumlabel" AS "value" FROM "pg_enum" "e" ` +
1,290✔
3785
                                    `INNER JOIN "pg_type" "t" ON "t"."oid" = "e"."enumtypid" ` +
1,290✔
3786
                                    `INNER JOIN "pg_namespace" "n" ON "n"."oid" = "t"."typnamespace" ` +
1,290✔
3787
                                    `WHERE "n"."nspname" = '${
1,290✔
3788
                                        dbTable["table_schema"]
1,290✔
3789
                                    }' AND "t"."typname" = '${
1,290✔
3790
                                        enumName || name
1,290✔
3791
                                    }'`
1,290✔
3792
                                const results: ObjectLiteral[] =
1,290✔
3793
                                    await this.query(sql)
1,290✔
3794

1,290✔
3795
                                if (results.length) {
1,290✔
3796
                                    tableColumn.type = "enum"
788✔
3797
                                    tableColumn.enum = results.map(
788✔
3798
                                        (result) => result["value"],
788✔
3799
                                    )
788✔
3800
                                    tableColumn.enumName = enumName
788✔
3801
                                }
788✔
3802

1,290✔
3803
                                if (dbColumn["data_type"] === "ARRAY") {
1,290✔
3804
                                    tableColumn.isArray = true
630✔
3805
                                    const type = tableColumn.type.replace(
630✔
3806
                                        "[]",
630✔
3807
                                        "",
630✔
3808
                                    )
630✔
3809
                                    tableColumn.type =
630✔
3810
                                        this.connection.driver.normalizeType({
630✔
3811
                                            type: type,
630✔
3812
                                        })
630✔
3813
                                }
630✔
3814
                            }
1,290✔
3815

18,944✔
3816
                            if (
18,944✔
3817
                                tableColumn.type === "geometry" ||
18,944✔
3818
                                tableColumn.type === "geography"
18,904✔
3819
                            ) {
18,944✔
3820
                                const sql =
52✔
3821
                                    `SELECT * FROM (` +
52✔
3822
                                    `SELECT "f_table_schema" "table_schema", "f_table_name" "table_name", ` +
52✔
3823
                                    `"f_${tableColumn.type}_column" "column_name", "srid", "type" ` +
52✔
3824
                                    `FROM "${tableColumn.type}_columns"` +
52✔
3825
                                    `) AS _ ` +
52✔
3826
                                    `WHERE "column_name" = '${dbColumn["column_name"]}' AND ` +
52✔
3827
                                    `"table_schema" = '${dbColumn["table_schema"]}' AND ` +
52✔
3828
                                    `"table_name" = '${dbColumn["table_name"]}'`
52✔
3829

52✔
3830
                                const results: ObjectLiteral[] =
52✔
3831
                                    await this.query(sql)
52✔
3832

52✔
3833
                                if (results.length > 0) {
52✔
3834
                                    tableColumn.spatialFeatureType =
52✔
3835
                                        results[0].type
52✔
3836
                                    tableColumn.srid = results[0].srid
52✔
3837
                                }
52✔
3838
                            }
52✔
3839

18,944✔
3840
                            // check only columns that have length property
18,944✔
3841
                            if (
18,944✔
3842
                                this.driver.withLengthColumnTypes.indexOf(
18,944✔
3843
                                    tableColumn.type as ColumnType,
18,944✔
3844
                                ) !== -1
18,944✔
3845
                            ) {
18,944✔
3846
                                let length
5,484✔
3847
                                if (tableColumn.isArray) {
5,484✔
3848
                                    const match = /\((\d+)\)/.exec(
32✔
3849
                                        dbColumn["format_type"],
32✔
3850
                                    )
32✔
3851
                                    length = match ? match[1] : undefined
32✔
3852
                                } else if (
5,484✔
3853
                                    dbColumn["character_maximum_length"]
5,452✔
3854
                                ) {
5,452✔
3855
                                    length =
778✔
3856
                                        dbColumn[
778✔
3857
                                            "character_maximum_length"
778✔
3858
                                        ].toString()
778✔
3859
                                }
778✔
3860
                                if (length) {
5,484✔
3861
                                    tableColumn.length =
782✔
3862
                                        !this.isDefaultColumnLength(
782✔
3863
                                            table,
782✔
3864
                                            tableColumn,
782✔
3865
                                            length,
782✔
3866
                                        )
782✔
3867
                                            ? length
782✔
3868
                                            : ""
782✔
3869
                                }
782✔
3870
                            }
5,484✔
3871
                            tableColumn.isNullable =
18,944✔
3872
                                dbColumn["is_nullable"] === "YES"
18,944✔
3873

18,944✔
3874
                            const primaryConstraint = columnConstraints.find(
18,944✔
3875
                                (constraint) =>
18,944✔
3876
                                    constraint["constraint_type"] === "PRIMARY",
18,944✔
3877
                            )
18,944✔
3878
                            if (primaryConstraint) {
18,944✔
3879
                                tableColumn.isPrimary = true
3,424✔
3880
                                // find another columns involved in primary key constraint
3,424✔
3881
                                const anotherPrimaryConstraints =
3,424✔
3882
                                    dbConstraints.filter(
3,424✔
3883
                                        (constraint) =>
3,424✔
3884
                                            constraint["table_name"] ===
105,114✔
3885
                                                dbColumn["table_name"] &&
105,114✔
3886
                                            constraint["table_schema"] ===
7,874✔
3887
                                                dbColumn["table_schema"] &&
105,114✔
3888
                                            constraint["column_name"] !==
7,786✔
3889
                                                dbColumn["column_name"] &&
105,114✔
3890
                                            constraint["constraint_type"] ===
4,146✔
3891
                                                "PRIMARY",
3,424✔
3892
                                    )
3,424✔
3893

3,424✔
3894
                                // collect all column names
3,424✔
3895
                                const columnNames =
3,424✔
3896
                                    anotherPrimaryConstraints.map(
3,424✔
3897
                                        (constraint) =>
3,424✔
3898
                                            constraint["column_name"],
3,424✔
3899
                                    )
3,424✔
3900
                                columnNames.push(dbColumn["column_name"])
3,424✔
3901

3,424✔
3902
                                // build default primary key constraint name
3,424✔
3903
                                const pkName =
3,424✔
3904
                                    this.connection.namingStrategy.primaryKeyName(
3,424✔
3905
                                        table,
3,424✔
3906
                                        columnNames,
3,424✔
3907
                                    )
3,424✔
3908

3,424✔
3909
                                // if primary key has user-defined constraint name, write it in table column
3,424✔
3910
                                if (
3,424✔
3911
                                    primaryConstraint["constraint_name"] !==
3,424✔
3912
                                    pkName
3,424✔
3913
                                ) {
3,424✔
3914
                                    tableColumn.primaryKeyConstraintName =
456✔
3915
                                        primaryConstraint["constraint_name"]
456✔
3916
                                }
456✔
3917
                            }
3,424✔
3918

18,944✔
3919
                            const uniqueConstraints = columnConstraints.filter(
18,944✔
3920
                                (constraint) =>
18,944✔
3921
                                    constraint["constraint_type"] === "UNIQUE",
18,944✔
3922
                            )
18,944✔
3923
                            const isConstraintComposite =
18,944✔
3924
                                uniqueConstraints.every((uniqueConstraint) => {
18,944✔
3925
                                    return dbConstraints.some(
1,890✔
3926
                                        (dbConstraint) =>
1,890✔
3927
                                            dbConstraint["constraint_type"] ===
49,615✔
3928
                                                "UNIQUE" &&
49,615✔
3929
                                            dbConstraint["constraint_name"] ===
26,041✔
3930
                                                uniqueConstraint[
26,041✔
3931
                                                    "constraint_name"
26,041✔
3932
                                                ] &&
49,615✔
3933
                                            dbConstraint["column_name"] !==
2,452✔
3934
                                                dbColumn["column_name"],
1,890✔
3935
                                    )
1,890✔
3936
                                })
18,944✔
3937
                            tableColumn.isUnique =
18,944✔
3938
                                uniqueConstraints.length > 0 &&
18,944✔
3939
                                !isConstraintComposite
1,864✔
3940

18,944✔
3941
                            if (dbColumn.is_identity === "YES") {
18,944!
3942
                                // Postgres 10+ Identity column
×
3943
                                tableColumn.isGenerated = true
×
3944
                                tableColumn.generationStrategy = "identity"
×
3945
                                tableColumn.generatedIdentity =
×
3946
                                    dbColumn.identity_generation
×
3947
                            } else if (
18,944✔
3948
                                dbColumn["column_default"] !== null &&
18,944✔
3949
                                dbColumn["column_default"] !== undefined
2,792✔
3950
                            ) {
18,944✔
3951
                                const serialDefaultName = `nextval('${this.buildSequenceName(
2,792✔
3952
                                    table,
2,792✔
3953
                                    dbColumn["column_name"],
2,792✔
3954
                                )}'::regclass)`
2,792✔
3955
                                const serialDefaultPath = `nextval('${this.buildSequencePath(
2,792✔
3956
                                    table,
2,792✔
3957
                                    dbColumn["column_name"],
2,792✔
3958
                                )}'::regclass)`
2,792✔
3959

2,792✔
3960
                                const defaultWithoutQuotes = dbColumn[
2,792✔
3961
                                    "column_default"
2,792✔
3962
                                ].replace(/"/g, "")
2,792✔
3963

2,792✔
3964
                                if (
2,792✔
3965
                                    defaultWithoutQuotes ===
2,792✔
3966
                                        serialDefaultName ||
2,792✔
3967
                                    defaultWithoutQuotes === serialDefaultPath
1,184✔
3968
                                ) {
2,792✔
3969
                                    tableColumn.isGenerated = true
1,832✔
3970
                                    tableColumn.generationStrategy = "increment"
1,832✔
3971
                                } else if (
2,792✔
3972
                                    dbColumn["column_default"] ===
960✔
3973
                                        "gen_random_uuid()" ||
960✔
3974
                                    /^uuid_generate_v\d\(\)/.test(
936✔
3975
                                        dbColumn["column_default"],
936✔
3976
                                    )
960✔
3977
                                ) {
960✔
3978
                                    if (tableColumn.type === "uuid") {
120✔
3979
                                        tableColumn.isGenerated = true
112✔
3980
                                        tableColumn.generationStrategy = "uuid"
112✔
3981
                                    } else {
120✔
3982
                                        tableColumn.default =
8✔
3983
                                            dbColumn["column_default"]
8✔
3984
                                    }
8✔
3985
                                } else if (
960✔
3986
                                    dbColumn["column_default"] === "now()" ||
840✔
3987
                                    dbColumn["column_default"].indexOf(
776✔
3988
                                        "'now'::text",
776✔
3989
                                    ) !== -1
776✔
3990
                                ) {
840✔
3991
                                    tableColumn.default =
208✔
3992
                                        dbColumn["column_default"]
208✔
3993
                                } else {
840✔
3994
                                    tableColumn.default = dbColumn[
632✔
3995
                                        "column_default"
632✔
3996
                                    ].replace(/::[\w\s.[\]\-"]+/g, "")
632✔
3997
                                    tableColumn.default =
632✔
3998
                                        tableColumn.default.replace(
632✔
3999
                                            /^(-?\d+)$/,
632✔
4000
                                            "'$1'",
632✔
4001
                                        )
632✔
4002
                                }
632✔
4003
                            }
2,792✔
4004

18,944✔
4005
                            if (
18,944✔
4006
                                dbColumn["is_generated"] === "ALWAYS" &&
18,944✔
4007
                                dbColumn["generation_expression"]
168✔
4008
                            ) {
18,944✔
4009
                                // In postgres there is no VIRTUAL generated column type
168✔
4010
                                tableColumn.generatedType = "STORED"
168✔
4011
                                // We cannot relay on information_schema.columns.generation_expression, because it is formatted different.
168✔
4012
                                const asExpressionQuery =
168✔
4013
                                    this.selectTypeormMetadataSql({
168✔
4014
                                        database: currentDatabase,
168✔
4015
                                        schema: dbTable["table_schema"],
168✔
4016
                                        table: dbTable["table_name"],
168✔
4017
                                        type: MetadataTableType.GENERATED_COLUMN,
168✔
4018
                                        name: tableColumn.name,
168✔
4019
                                    })
168✔
4020

168✔
4021
                                const results = await this.query(
168✔
4022
                                    asExpressionQuery.query,
168✔
4023
                                    asExpressionQuery.parameters,
168✔
4024
                                )
168✔
4025
                                if (results[0] && results[0].value) {
168✔
4026
                                    tableColumn.asExpression = results[0].value
168✔
4027
                                } else {
168!
4028
                                    tableColumn.asExpression = ""
×
4029
                                }
×
4030
                            }
168✔
4031

18,944✔
4032
                            tableColumn.comment = dbColumn["description"]
18,944✔
4033
                                ? dbColumn["description"]
18,944✔
4034
                                : undefined
18,944✔
4035
                            if (dbColumn["character_set_name"])
18,944✔
4036
                                tableColumn.charset =
18,944!
4037
                                    dbColumn["character_set_name"]
×
4038
                            if (dbColumn["collation_name"])
18,944✔
4039
                                tableColumn.collation =
18,944✔
4040
                                    dbColumn["collation_name"]
3,392✔
4041
                            return tableColumn
18,944✔
4042
                        }),
3,824✔
4043
                )
3,824✔
4044

3,824✔
4045
                // find unique constraints of table, group them by constraint name and build TableUnique.
3,824✔
4046
                const tableUniqueConstraints = OrmUtils.uniq(
3,824✔
4047
                    dbConstraints.filter((dbConstraint) => {
3,824✔
4048
                        return (
212,570✔
4049
                            dbConstraint["table_name"] ===
212,570✔
4050
                                dbTable["table_name"] &&
212,570✔
4051
                            dbConstraint["table_schema"] ===
7,130✔
4052
                                dbTable["table_schema"] &&
212,570✔
4053
                            dbConstraint["constraint_type"] === "UNIQUE"
7,042✔
4054
                        )
212,570✔
4055
                    }),
3,824✔
4056
                    (dbConstraint) => dbConstraint["constraint_name"],
3,824✔
4057
                )
3,824✔
4058

3,824✔
4059
                table.uniques = tableUniqueConstraints.map((constraint) => {
3,824✔
4060
                    const uniques = dbConstraints.filter(
1,262✔
4061
                        (dbC) =>
1,262✔
4062
                            dbC["constraint_name"] ===
50,118✔
4063
                            constraint["constraint_name"],
1,262✔
4064
                    )
1,262✔
4065
                    return new TableUnique({
1,262✔
4066
                        name: constraint["constraint_name"],
1,262✔
4067
                        columnNames: uniques.map((u) => u["column_name"]),
1,262✔
4068
                        deferrable: constraint["deferrable"]
1,262✔
4069
                            ? constraint["deferred"]
1,262!
4070
                            : undefined,
1,262✔
4071
                    })
1,262✔
4072
                })
3,824✔
4073

3,824✔
4074
                // find check constraints of table, group them by constraint name and build TableCheck.
3,824✔
4075
                const tableCheckConstraints = OrmUtils.uniq(
3,824✔
4076
                    dbConstraints.filter((dbConstraint) => {
3,824✔
4077
                        return (
212,570✔
4078
                            dbConstraint["table_name"] ===
212,570✔
4079
                                dbTable["table_name"] &&
212,570✔
4080
                            dbConstraint["table_schema"] ===
7,130✔
4081
                                dbTable["table_schema"] &&
212,570✔
4082
                            dbConstraint["constraint_type"] === "CHECK"
7,042✔
4083
                        )
212,570✔
4084
                    }),
3,824✔
4085
                    (dbConstraint) => dbConstraint["constraint_name"],
3,824✔
4086
                )
3,824✔
4087

3,824✔
4088
                table.checks = tableCheckConstraints.map((constraint) => {
3,824✔
4089
                    const checks = dbConstraints.filter(
420✔
4090
                        (dbC) =>
420✔
4091
                            dbC["constraint_name"] ===
4,446✔
4092
                            constraint["constraint_name"],
420✔
4093
                    )
420✔
4094
                    return new TableCheck({
420✔
4095
                        name: constraint["constraint_name"],
420✔
4096
                        columnNames: checks.map((c) => c["column_name"]),
420✔
4097
                        expression: constraint["expression"].replace(
420✔
4098
                            /^\s*CHECK\s*\((.*)\)\s*$/i,
420✔
4099
                            "$1",
420✔
4100
                        ),
420✔
4101
                    })
420✔
4102
                })
3,824✔
4103

3,824✔
4104
                // find exclusion constraints of table, group them by constraint name and build TableExclusion.
3,824✔
4105
                const tableExclusionConstraints = OrmUtils.uniq(
3,824✔
4106
                    dbConstraints.filter((dbConstraint) => {
3,824✔
4107
                        return (
212,570✔
4108
                            dbConstraint["table_name"] ===
212,570✔
4109
                                dbTable["table_name"] &&
212,570✔
4110
                            dbConstraint["table_schema"] ===
7,130✔
4111
                                dbTable["table_schema"] &&
212,570✔
4112
                            dbConstraint["constraint_type"] === "EXCLUDE"
7,042✔
4113
                        )
212,570✔
4114
                    }),
3,824✔
4115
                    (dbConstraint) => dbConstraint["constraint_name"],
3,824✔
4116
                )
3,824✔
4117

3,824✔
4118
                table.exclusions = tableExclusionConstraints.map(
3,824✔
4119
                    (constraint) => {
3,824✔
4120
                        return new TableExclusion({
324✔
4121
                            name: constraint["constraint_name"],
324✔
4122
                            expression: constraint["expression"].substring(8), // trim EXCLUDE from start of expression
324✔
4123
                        })
324✔
4124
                    },
3,824✔
4125
                )
3,824✔
4126

3,824✔
4127
                // find foreign key constraints of table, group them by constraint name and build TableForeignKey.
3,824✔
4128
                const tableForeignKeyConstraints = OrmUtils.uniq(
3,824✔
4129
                    dbForeignKeys.filter((dbForeignKey) => {
3,824✔
4130
                        return (
11,504✔
4131
                            dbForeignKey["table_name"] ===
11,504✔
4132
                                dbTable["table_name"] &&
11,504✔
4133
                            dbForeignKey["table_schema"] ===
996✔
4134
                                dbTable["table_schema"]
996✔
4135
                        )
11,504✔
4136
                    }),
3,824✔
4137
                    (dbForeignKey) => dbForeignKey["constraint_name"],
3,824✔
4138
                )
3,824✔
4139

3,824✔
4140
                table.foreignKeys = tableForeignKeyConstraints.map(
3,824✔
4141
                    (dbForeignKey) => {
3,824✔
4142
                        const foreignKeys = dbForeignKeys.filter(
948✔
4143
                            (dbFk) =>
948✔
4144
                                dbFk["constraint_name"] ===
2,660✔
4145
                                dbForeignKey["constraint_name"],
948✔
4146
                        )
948✔
4147

948✔
4148
                        // if referenced table located in currently used schema, we don't need to concat schema name to table name.
948✔
4149
                        const schema = getSchemaFromKey(
948✔
4150
                            dbForeignKey,
948✔
4151
                            "referenced_table_schema",
948✔
4152
                        )
948✔
4153
                        const referencedTableName = this.driver.buildTableName(
948✔
4154
                            dbForeignKey["referenced_table_name"],
948✔
4155
                            schema,
948✔
4156
                        )
948✔
4157

948✔
4158
                        return new TableForeignKey({
948✔
4159
                            name: dbForeignKey["constraint_name"],
948✔
4160
                            columnNames: foreignKeys.map(
948✔
4161
                                (dbFk) => dbFk["column_name"],
948✔
4162
                            ),
948✔
4163
                            referencedSchema:
948✔
4164
                                dbForeignKey["referenced_table_schema"],
948✔
4165
                            referencedTableName: referencedTableName,
948✔
4166
                            referencedColumnNames: foreignKeys.map(
948✔
4167
                                (dbFk) => dbFk["referenced_column_name"],
948✔
4168
                            ),
948✔
4169
                            onDelete: dbForeignKey["on_delete"],
948✔
4170
                            onUpdate: dbForeignKey["on_update"],
948✔
4171
                            deferrable: dbForeignKey["deferrable"]
948✔
4172
                                ? dbForeignKey["deferred"]
948✔
4173
                                : undefined,
948✔
4174
                        })
948✔
4175
                    },
3,824✔
4176
                )
3,824✔
4177

3,824✔
4178
                // find index constraints of table, group them by constraint name and build TableIndex.
3,824✔
4179
                const tableIndexConstraints = OrmUtils.uniq(
3,824✔
4180
                    dbIndices.filter((dbIndex) => {
3,824✔
4181
                        return (
31,828✔
4182
                            dbIndex["table_name"] === dbTable["table_name"] &&
31,828✔
4183
                            dbIndex["table_schema"] === dbTable["table_schema"]
876✔
4184
                        )
31,828✔
4185
                    }),
3,824✔
4186
                    (dbIndex) => dbIndex["constraint_name"],
3,824✔
4187
                )
3,824✔
4188

3,824✔
4189
                table.indices = tableIndexConstraints.map((constraint) => {
3,824✔
4190
                    const indices = dbIndices.filter((index) => {
736✔
4191
                        return (
4,176✔
4192
                            index["table_schema"] ===
4,176✔
4193
                                constraint["table_schema"] &&
4,176✔
4194
                            index["table_name"] === constraint["table_name"] &&
4,176✔
4195
                            index["constraint_name"] ===
1,628✔
4196
                                constraint["constraint_name"]
1,628✔
4197
                        )
4,176✔
4198
                    })
736✔
4199
                    return new TableIndex(<TableIndexOptions>{
736✔
4200
                        table: table,
736✔
4201
                        name: constraint["constraint_name"],
736✔
4202
                        columnNames: indices.map((i) => i["column_name"]),
736✔
4203
                        isUnique: constraint["is_unique"] === "TRUE",
736✔
4204
                        where: constraint["condition"],
736✔
4205
                        isSpatial: constraint["index_type"] === "gist",
736✔
4206
                        type: constraint["index_type"],
736✔
4207
                        isFulltext: false,
736✔
4208
                    })
736✔
4209
                })
3,824✔
4210

3,824✔
4211
                return table
3,824✔
4212
            }),
2,072✔
4213
        )
2,072✔
4214
    }
2,072✔
4215

26✔
4216
    /**
26✔
4217
     * Builds create table sql.
26✔
4218
     * @param table
26✔
4219
     * @param createForeignKeys
26✔
4220
     */
26✔
4221
    protected createTableSql(table: Table, createForeignKeys?: boolean): Query {
26✔
4222
        const columnDefinitions = table.columns
24,384✔
4223
            .map((column) => this.buildCreateColumnSql(table, column))
24,384✔
4224
            .join(", ")
24,384✔
4225
        let sql = `CREATE TABLE ${this.escapePath(table)} (${columnDefinitions}`
24,384✔
4226

24,384✔
4227
        table.columns
24,384✔
4228
            .filter((column) => column.isUnique)
24,384✔
4229
            .forEach((column) => {
24,384✔
4230
                const isUniqueExist = table.uniques.some(
3,260✔
4231
                    (unique) =>
3,260✔
4232
                        unique.columnNames.length === 1 &&
4,304✔
4233
                        unique.columnNames[0] === column.name,
3,260✔
4234
                )
3,260✔
4235
                if (!isUniqueExist)
3,260✔
4236
                    table.uniques.push(
3,260✔
4237
                        new TableUnique({
16✔
4238
                            name: this.connection.namingStrategy.uniqueConstraintName(
16✔
4239
                                table,
16✔
4240
                                [column.name],
16✔
4241
                            ),
16✔
4242
                            columnNames: [column.name],
16✔
4243
                        }),
16✔
4244
                    )
16✔
4245
            })
24,384✔
4246

24,384✔
4247
        if (table.uniques.length > 0) {
24,384✔
4248
            const uniquesSql = table.uniques
2,836✔
4249
                .map((unique) => {
2,836✔
4250
                    const uniqueName = unique.name
4,112✔
4251
                        ? unique.name
4,112✔
4252
                        : this.connection.namingStrategy.uniqueConstraintName(
4,112✔
4253
                              table,
8✔
4254
                              unique.columnNames,
8✔
4255
                          )
4,112✔
4256
                    const columnNames = unique.columnNames
4,112✔
4257
                        .map((columnName) => `"${columnName}"`)
4,112✔
4258
                        .join(", ")
4,112✔
4259
                    let constraint = `CONSTRAINT "${uniqueName}" UNIQUE (${columnNames})`
4,112✔
4260
                    if (unique.deferrable)
4,112✔
4261
                        constraint += ` DEFERRABLE ${unique.deferrable}`
4,112✔
4262
                    return constraint
4,112✔
4263
                })
2,836✔
4264
                .join(", ")
2,836✔
4265

2,836✔
4266
            sql += `, ${uniquesSql}`
2,836✔
4267
        }
2,836✔
4268

24,384✔
4269
        if (table.checks.length > 0) {
24,384✔
4270
            const checksSql = table.checks
320✔
4271
                .map((check) => {
320✔
4272
                    const checkName = check.name
324✔
4273
                        ? check.name
324✔
4274
                        : this.connection.namingStrategy.checkConstraintName(
324✔
4275
                              table,
4✔
4276
                              check.expression!,
4✔
4277
                          )
324✔
4278
                    return `CONSTRAINT "${checkName}" CHECK (${check.expression})`
324✔
4279
                })
320✔
4280
                .join(", ")
320✔
4281

320✔
4282
            sql += `, ${checksSql}`
320✔
4283
        }
320✔
4284

24,384✔
4285
        if (table.exclusions.length > 0) {
24,384✔
4286
            const exclusionsSql = table.exclusions
364✔
4287
                .map((exclusion) => {
364✔
4288
                    const exclusionName = exclusion.name
364✔
4289
                        ? exclusion.name
364✔
4290
                        : this.connection.namingStrategy.exclusionConstraintName(
364!
4291
                              table,
×
4292
                              exclusion.expression!,
×
4293
                          )
364✔
4294
                    let constraint = `CONSTRAINT "${exclusionName}" EXCLUDE ${exclusion.expression}`
364✔
4295
                    if (exclusion.deferrable)
364✔
4296
                        constraint += ` DEFERRABLE ${exclusion.deferrable}`
364✔
4297
                    return constraint
364✔
4298
                })
364✔
4299
                .join(", ")
364✔
4300

364✔
4301
            sql += `, ${exclusionsSql}`
364✔
4302
        }
364✔
4303

24,384✔
4304
        if (table.foreignKeys.length > 0 && createForeignKeys) {
24,384✔
4305
            const foreignKeysSql = table.foreignKeys
16✔
4306
                .map((fk) => {
16✔
4307
                    const columnNames = fk.columnNames
20✔
4308
                        .map((columnName) => `"${columnName}"`)
20✔
4309
                        .join(", ")
20✔
4310
                    if (!fk.name)
20✔
4311
                        fk.name = this.connection.namingStrategy.foreignKeyName(
20✔
4312
                            table,
12✔
4313
                            fk.columnNames,
12✔
4314
                            this.getTablePath(fk),
12✔
4315
                            fk.referencedColumnNames,
12✔
4316
                        )
12✔
4317

20✔
4318
                    const referencedColumnNames = fk.referencedColumnNames
20✔
4319
                        .map((columnName) => `"${columnName}"`)
20✔
4320
                        .join(", ")
20✔
4321

20✔
4322
                    let constraint = `CONSTRAINT "${
20✔
4323
                        fk.name
20✔
4324
                    }" FOREIGN KEY (${columnNames}) REFERENCES ${this.escapePath(
20✔
4325
                        this.getTablePath(fk),
20✔
4326
                    )} (${referencedColumnNames})`
20✔
4327
                    if (fk.onDelete) constraint += ` ON DELETE ${fk.onDelete}`
20✔
4328
                    if (fk.onUpdate) constraint += ` ON UPDATE ${fk.onUpdate}`
20✔
4329
                    if (fk.deferrable)
20✔
4330
                        constraint += ` DEFERRABLE ${fk.deferrable}`
20!
4331

20✔
4332
                    return constraint
20✔
4333
                })
16✔
4334
                .join(", ")
16✔
4335

16✔
4336
            sql += `, ${foreignKeysSql}`
16✔
4337
        }
16✔
4338

24,384✔
4339
        const primaryColumns = table.columns.filter(
24,384✔
4340
            (column) => column.isPrimary,
24,384✔
4341
        )
24,384✔
4342
        if (primaryColumns.length > 0) {
24,384✔
4343
            const primaryKeyName = primaryColumns[0].primaryKeyConstraintName
24,268✔
4344
                ? primaryColumns[0].primaryKeyConstraintName
24,268✔
4345
                : this.connection.namingStrategy.primaryKeyName(
24,268✔
4346
                      table,
24,204✔
4347
                      primaryColumns.map((column) => column.name),
24,204✔
4348
                  )
24,268✔
4349

24,268✔
4350
            const columnNames = primaryColumns
24,268✔
4351
                .map((column) => `"${column.name}"`)
24,268✔
4352
                .join(", ")
24,268✔
4353
            sql += `, CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNames})`
24,268✔
4354
        }
24,268✔
4355

24,384✔
4356
        sql += `)`
24,384✔
4357

24,384✔
4358
        table.columns
24,384✔
4359
            .filter((it) => it.comment)
24,384✔
4360
            .forEach(
24,384✔
4361
                (it) =>
24,384✔
4362
                    (sql += `; COMMENT ON COLUMN ${this.escapePath(table)}."${
248✔
4363
                        it.name
248✔
4364
                    }" IS ${this.escapeComment(it.comment)}`),
24,384✔
4365
            )
24,384✔
4366

24,384✔
4367
        return new Query(sql)
24,384✔
4368
    }
24,384✔
4369

26✔
4370
    /**
26✔
4371
     * Loads Postgres version.
26✔
4372
     */
26✔
4373
    async getVersion(): Promise<string> {
26✔
4374
        // we use `SELECT version()` instead of `SHOW server_version` or `SHOW server_version_num`
2,776✔
4375
        // to maintain compatability with Amazon Redshift.
2,776✔
4376
        //
2,776✔
4377
        // see:
2,776✔
4378
        //  - https://github.com/typeorm/typeorm/pull/9319
2,776✔
4379
        //  - https://docs.aws.amazon.com/redshift/latest/dg/c_unsupported-postgresql-functions.html
2,776✔
4380
        const result: [{ version: string }] =
2,776✔
4381
            await this.query(`SELECT version()`)
2,776✔
4382

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

26✔
4389
    /**
26✔
4390
     * Builds drop table sql.
26✔
4391
     * @param tableOrPath
26✔
4392
     */
26✔
4393
    protected dropTableSql(tableOrPath: Table | string): Query {
26✔
4394
        return new Query(`DROP TABLE ${this.escapePath(tableOrPath)}`)
24,384✔
4395
    }
24,384✔
4396

26✔
4397
    protected createViewSql(view: View): Query {
26✔
4398
        const materializedClause = view.materialized ? "MATERIALIZED " : ""
164✔
4399
        const viewName = this.escapePath(view)
164✔
4400

164✔
4401
        if (typeof view.expression === "string") {
164✔
4402
            return new Query(
116✔
4403
                `CREATE ${materializedClause}VIEW ${viewName} AS ${view.expression}`,
116✔
4404
            )
116✔
4405
        } else {
164✔
4406
            return new Query(
48✔
4407
                `CREATE ${materializedClause}VIEW ${viewName} AS ${view
48✔
4408
                    .expression(this.connection)
48✔
4409
                    .getQuery()}`,
48✔
4410
            )
48✔
4411
        }
48✔
4412
    }
164✔
4413

26✔
4414
    protected async insertViewDefinitionSql(view: View): Promise<Query> {
26✔
4415
        const currentSchema = await this.getCurrentSchema()
160✔
4416

160✔
4417
        let { schema, tableName: name } = this.driver.parseTableName(view)
160✔
4418

160✔
4419
        if (!schema) {
160!
4420
            schema = currentSchema
×
4421
        }
×
4422

160✔
4423
        const type = view.materialized
160✔
4424
            ? MetadataTableType.MATERIALIZED_VIEW
160✔
4425
            : MetadataTableType.VIEW
160✔
4426
        const expression =
160✔
4427
            typeof view.expression === "string"
160✔
4428
                ? view.expression.trim()
160✔
4429
                : view.expression(this.connection).getQuery()
160✔
4430
        return this.insertTypeormMetadataSql({
160✔
4431
            type,
160✔
4432
            schema,
160✔
4433
            name,
160✔
4434
            value: expression,
160✔
4435
        })
160✔
4436
    }
160✔
4437

26✔
4438
    /**
26✔
4439
     * Builds drop view sql.
26✔
4440
     * @param view
26✔
4441
     */
26✔
4442
    protected dropViewSql(view: View): Query {
26✔
4443
        const materializedClause = view.materialized ? "MATERIALIZED " : ""
164✔
4444
        return new Query(
164✔
4445
            `DROP ${materializedClause}VIEW ${this.escapePath(view)}`,
164✔
4446
        )
164✔
4447
    }
164✔
4448

26✔
4449
    /**
26✔
4450
     * Builds remove view sql.
26✔
4451
     * @param view
26✔
4452
     */
26✔
4453
    protected async deleteViewDefinitionSql(view: View): Promise<Query> {
26✔
4454
        const currentSchema = await this.getCurrentSchema()
160✔
4455

160✔
4456
        let { schema, tableName: name } = this.driver.parseTableName(view)
160✔
4457

160✔
4458
        if (!schema) {
160!
4459
            schema = currentSchema
×
4460
        }
×
4461

160✔
4462
        const type = view.materialized
160✔
4463
            ? MetadataTableType.MATERIALIZED_VIEW
160✔
4464
            : MetadataTableType.VIEW
160✔
4465
        return this.deleteTypeormMetadataSql({ type, schema, name })
160✔
4466
    }
160✔
4467

26✔
4468
    /**
26✔
4469
     * Drops ENUM type from given schemas.
26✔
4470
     * @param schemaNames
26✔
4471
     */
26✔
4472
    protected async dropEnumTypes(schemaNames: string): Promise<void> {
26✔
4473
        const selectDropsQuery =
7,588✔
4474
            `SELECT 'DROP TYPE IF EXISTS "' || n.nspname || '"."' || t.typname || '" CASCADE;' as "query" FROM "pg_type" "t" ` +
7,588✔
4475
            `INNER JOIN "pg_enum" "e" ON "e"."enumtypid" = "t"."oid" ` +
7,588✔
4476
            `INNER JOIN "pg_namespace" "n" ON "n"."oid" = "t"."typnamespace" ` +
7,588✔
4477
            `WHERE "n"."nspname" IN (${schemaNames}) GROUP BY "n"."nspname", "t"."typname"`
7,588✔
4478
        const dropQueries: ObjectLiteral[] = await this.query(selectDropsQuery)
7,588✔
4479
        await Promise.all(dropQueries.map((q) => this.query(q["query"])))
7,588✔
4480
    }
7,588✔
4481

26✔
4482
    /**
26✔
4483
     * Checks if enum with the given name exist in the database.
26✔
4484
     * @param table
26✔
4485
     * @param column
26✔
4486
     */
26✔
4487
    protected async hasEnumType(
26✔
4488
        table: Table,
1,040✔
4489
        column: TableColumn,
1,040✔
4490
    ): Promise<boolean> {
1,040✔
4491
        let { schema } = this.driver.parseTableName(table)
1,040✔
4492

1,040✔
4493
        if (!schema) {
1,040!
4494
            schema = await this.getCurrentSchema()
×
4495
        }
×
4496

1,040✔
4497
        const enumName = this.buildEnumName(table, column, false, true)
1,040✔
4498
        const sql =
1,040✔
4499
            `SELECT "n"."nspname", "t"."typname" FROM "pg_type" "t" ` +
1,040✔
4500
            `INNER JOIN "pg_namespace" "n" ON "n"."oid" = "t"."typnamespace" ` +
1,040✔
4501
            `WHERE "n"."nspname" = '${schema}' AND "t"."typname" = '${enumName}'`
1,040✔
4502
        const result = await this.query(sql)
1,040✔
4503
        return result.length ? true : false
1,040✔
4504
    }
1,040✔
4505

26✔
4506
    /**
26✔
4507
     * Builds create ENUM type sql.
26✔
4508
     * @param table
26✔
4509
     * @param column
26✔
4510
     * @param enumName
26✔
4511
     */
26✔
4512
    protected createEnumTypeSql(
26✔
4513
        table: Table,
1,124✔
4514
        column: TableColumn,
1,124✔
4515
        enumName?: string,
1,124✔
4516
    ): Query {
1,124✔
4517
        if (!enumName) enumName = this.buildEnumName(table, column)
1,124✔
4518
        const enumValues = column
1,124✔
4519
            .enum!.map((value) => `'${value.replaceAll("'", "''")}'`)
1,124✔
4520
            .join(", ")
1,124✔
4521
        return new Query(`CREATE TYPE ${enumName} AS ENUM(${enumValues})`)
1,124✔
4522
    }
1,124✔
4523

26✔
4524
    /**
26✔
4525
     * Builds create ENUM type sql.
26✔
4526
     * @param table
26✔
4527
     * @param column
26✔
4528
     * @param enumName
26✔
4529
     */
26✔
4530
    protected dropEnumTypeSql(
26✔
4531
        table: Table,
1,124✔
4532
        column: TableColumn,
1,124✔
4533
        enumName?: string,
1,124✔
4534
    ): Query {
1,124✔
4535
        if (!enumName) enumName = this.buildEnumName(table, column)
1,124✔
4536
        return new Query(`DROP TYPE ${enumName}`)
1,124✔
4537
    }
1,124✔
4538

26✔
4539
    /**
26✔
4540
     * Builds the SQL `USING <index_type>` clause based on the index type, prioritizing `isSpatial` as `GiST`.
26✔
4541
     */
26✔
4542

26✔
4543
    private buildIndexTypeClause(index: TableIndex) {
26✔
4544
        const type = index.isSpatial ? "gist" : index.type
8,500✔
4545

8,500✔
4546
        if (typeof type !== "string") return null
8,500✔
4547

180✔
4548
        return `USING ${type}`
180✔
4549
    }
180✔
4550

26✔
4551
    /**
26✔
4552
     * Builds create index sql.
26✔
4553
     * @param table
26✔
4554
     * @param index
26✔
4555
     */
26✔
4556
    protected createIndexSql(table: Table, index: TableIndex): Query {
26✔
4557
        const indexTypeClause = this.buildIndexTypeClause(index)
8,472✔
4558

8,472✔
4559
        const columns = index.columnNames
8,472✔
4560
            .map((columnName) => `"${columnName}"`)
8,472✔
4561
            .join(", ")
8,472✔
4562
        return new Query(
8,472✔
4563
            `CREATE ${index.isUnique ? "UNIQUE " : ""}INDEX${
8,472✔
4564
                index.isConcurrent ? " CONCURRENTLY" : ""
8,472✔
4565
            } "${index.name}" ON ${this.escapePath(table)} ${
8,472✔
4566
                indexTypeClause ?? ""
8,472✔
4567
            } (${columns}) ${index.where ? "WHERE " + index.where : ""}`,
8,472✔
4568
        )
8,472✔
4569
    }
8,472✔
4570

26✔
4571
    /**
26✔
4572
     * Builds create view index sql.
26✔
4573
     * @param view
26✔
4574
     * @param index
26✔
4575
     */
26✔
4576
    protected createViewIndexSql(view: View, index: TableIndex): Query {
26✔
4577
        const indexTypeClause = this.buildIndexTypeClause(index)
28✔
4578

28✔
4579
        const columns = index.columnNames
28✔
4580
            .map((columnName) => `"${columnName}"`)
28✔
4581
            .join(", ")
28✔
4582
        return new Query(
28✔
4583
            `CREATE ${index.isUnique ? "UNIQUE " : ""}INDEX "${
28✔
4584
                index.name
28✔
4585
            }" ON ${this.escapePath(view)} ${
28✔
4586
                indexTypeClause ?? ""
28✔
4587
            } (${columns}) ${index.where ? "WHERE " + index.where : ""}`,
28!
4588
        )
28✔
4589
    }
28✔
4590

26✔
4591
    /**
26✔
4592
     * Builds drop index sql.
26✔
4593
     * @param table
26✔
4594
     * @param indexOrName
26✔
4595
     */
26✔
4596
    protected dropIndexSql(
26✔
4597
        table: Table | View,
8,500✔
4598
        indexOrName: TableIndex | string,
8,500✔
4599
    ): Query {
8,500✔
4600
        const indexName = InstanceChecker.isTableIndex(indexOrName)
8,500✔
4601
            ? indexOrName.name
8,500✔
4602
            : indexOrName
8,500!
4603
        const concurrent = InstanceChecker.isTableIndex(indexOrName)
8,500✔
4604
            ? indexOrName.isConcurrent
8,500✔
4605
            : false
8,500!
4606
        const { schema } = this.driver.parseTableName(table)
8,500✔
4607
        return schema
8,500✔
4608
            ? new Query(
8,500✔
4609
                  `DROP INDEX ${
8,500✔
4610
                      concurrent ? "CONCURRENTLY " : ""
8,500✔
4611
                  }"${schema}"."${indexName}"`,
8,500✔
4612
              )
8,500✔
4613
            : new Query(
8,500!
4614
                  `DROP INDEX ${
×
4615
                      concurrent ? "CONCURRENTLY " : ""
×
4616
                  }"${indexName}"`,
×
4617
              )
8,500✔
4618
    }
8,500✔
4619

26✔
4620
    /**
26✔
4621
     * Builds create primary key sql.
26✔
4622
     * @param table
26✔
4623
     * @param columnNames
26✔
4624
     * @param constraintName
26✔
4625
     */
26✔
4626
    protected createPrimaryKeySql(
26✔
4627
        table: Table,
20✔
4628
        columnNames: string[],
20✔
4629
        constraintName?: string,
20✔
4630
    ): Query {
20✔
4631
        const primaryKeyName = constraintName
20✔
4632
            ? constraintName
20!
4633
            : this.connection.namingStrategy.primaryKeyName(table, columnNames)
20✔
4634

20✔
4635
        const columnNamesString = columnNames
20✔
4636
            .map((columnName) => `"${columnName}"`)
20✔
4637
            .join(", ")
20✔
4638

20✔
4639
        return new Query(
20✔
4640
            `ALTER TABLE ${this.escapePath(
20✔
4641
                table,
20✔
4642
            )} ADD CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNamesString})`,
20✔
4643
        )
20✔
4644
    }
20✔
4645

26✔
4646
    /**
26✔
4647
     * Builds drop primary key sql.
26✔
4648
     * @param table
26✔
4649
     */
26✔
4650
    protected dropPrimaryKeySql(table: Table): Query {
26✔
4651
        if (!table.primaryColumns.length)
20✔
4652
            throw new TypeORMError(`Table ${table} has no primary keys.`)
20!
4653

20✔
4654
        const columnNames = table.primaryColumns.map((column) => column.name)
20✔
4655
        const constraintName = table.primaryColumns[0].primaryKeyConstraintName
20✔
4656
        const primaryKeyName = constraintName
20✔
4657
            ? constraintName
20!
4658
            : this.connection.namingStrategy.primaryKeyName(table, columnNames)
20✔
4659

20✔
4660
        return new Query(
20✔
4661
            `ALTER TABLE ${this.escapePath(
20✔
4662
                table,
20✔
4663
            )} DROP CONSTRAINT "${primaryKeyName}"`,
20✔
4664
        )
20✔
4665
    }
20✔
4666

26✔
4667
    /**
26✔
4668
     * Builds create unique constraint sql.
26✔
4669
     * @param table
26✔
4670
     * @param uniqueConstraint
26✔
4671
     */
26✔
4672
    protected createUniqueConstraintSql(
26✔
4673
        table: Table,
92✔
4674
        uniqueConstraint: TableUnique,
92✔
4675
    ): Query {
92✔
4676
        const columnNames = uniqueConstraint.columnNames
92✔
4677
            .map((column) => `"` + column + `"`)
92✔
4678
            .join(", ")
92✔
4679
        let sql = `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
92✔
4680
            uniqueConstraint.name
92✔
4681
        }" UNIQUE (${columnNames})`
92✔
4682
        if (uniqueConstraint.deferrable)
92✔
4683
            sql += ` DEFERRABLE ${uniqueConstraint.deferrable}`
92!
4684
        return new Query(sql)
92✔
4685
    }
92✔
4686

26✔
4687
    /**
26✔
4688
     * Builds drop unique constraint sql.
26✔
4689
     * @param table
26✔
4690
     * @param uniqueOrName
26✔
4691
     */
26✔
4692
    protected dropUniqueConstraintSql(
26✔
4693
        table: Table,
92✔
4694
        uniqueOrName: TableUnique | string,
92✔
4695
    ): Query {
92✔
4696
        const uniqueName = InstanceChecker.isTableUnique(uniqueOrName)
92✔
4697
            ? uniqueOrName.name
92✔
4698
            : uniqueOrName
92!
4699
        return new Query(
92✔
4700
            `ALTER TABLE ${this.escapePath(
92✔
4701
                table,
92✔
4702
            )} DROP CONSTRAINT "${uniqueName}"`,
92✔
4703
        )
92✔
4704
    }
92✔
4705

26✔
4706
    /**
26✔
4707
     * Builds create check constraint sql.
26✔
4708
     * @param table
26✔
4709
     * @param checkConstraint
26✔
4710
     */
26✔
4711
    protected createCheckConstraintSql(
26✔
4712
        table: Table,
40✔
4713
        checkConstraint: TableCheck,
40✔
4714
    ): Query {
40✔
4715
        return new Query(
40✔
4716
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
40✔
4717
                checkConstraint.name
40✔
4718
            }" CHECK (${checkConstraint.expression})`,
40✔
4719
        )
40✔
4720
    }
40✔
4721

26✔
4722
    /**
26✔
4723
     * Builds drop check constraint sql.
26✔
4724
     * @param table
26✔
4725
     * @param checkOrName
26✔
4726
     */
26✔
4727
    protected dropCheckConstraintSql(
26✔
4728
        table: Table,
40✔
4729
        checkOrName: TableCheck | string,
40✔
4730
    ): Query {
40✔
4731
        const checkName = InstanceChecker.isTableCheck(checkOrName)
40✔
4732
            ? checkOrName.name
40✔
4733
            : checkOrName
40!
4734
        return new Query(
40✔
4735
            `ALTER TABLE ${this.escapePath(
40✔
4736
                table,
40✔
4737
            )} DROP CONSTRAINT "${checkName}"`,
40✔
4738
        )
40✔
4739
    }
40✔
4740

26✔
4741
    /**
26✔
4742
     * Builds create exclusion constraint sql.
26✔
4743
     * @param table
26✔
4744
     * @param exclusionConstraint
26✔
4745
     */
26✔
4746
    protected createExclusionConstraintSql(
26✔
4747
        table: Table,
28✔
4748
        exclusionConstraint: TableExclusion,
28✔
4749
    ): Query {
28✔
4750
        let sql = `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
28✔
4751
            exclusionConstraint.name
28✔
4752
        }" EXCLUDE ${exclusionConstraint.expression}`
28✔
4753

28✔
4754
        if (exclusionConstraint.deferrable)
28✔
4755
            sql += ` DEFERRABLE ${exclusionConstraint.deferrable}`
28!
4756

28✔
4757
        return new Query(sql)
28✔
4758
    }
28✔
4759

26✔
4760
    /**
26✔
4761
     * Builds drop exclusion constraint sql.
26✔
4762
     * @param table
26✔
4763
     * @param exclusionOrName
26✔
4764
     */
26✔
4765
    protected dropExclusionConstraintSql(
26✔
4766
        table: Table,
28✔
4767
        exclusionOrName: TableExclusion | string,
28✔
4768
    ): Query {
28✔
4769
        const exclusionName = InstanceChecker.isTableExclusion(exclusionOrName)
28✔
4770
            ? exclusionOrName.name
28✔
4771
            : exclusionOrName
28!
4772
        return new Query(
28✔
4773
            `ALTER TABLE ${this.escapePath(
28✔
4774
                table,
28✔
4775
            )} DROP CONSTRAINT "${exclusionName}"`,
28✔
4776
        )
28✔
4777
    }
28✔
4778

26✔
4779
    /**
26✔
4780
     * Builds create foreign key sql.
26✔
4781
     * @param table
26✔
4782
     * @param foreignKey
26✔
4783
     */
26✔
4784
    protected createForeignKeySql(
26✔
4785
        table: Table,
15,776✔
4786
        foreignKey: TableForeignKey,
15,776✔
4787
    ): Query {
15,776✔
4788
        const columnNames = foreignKey.columnNames
15,776✔
4789
            .map((column) => `"` + column + `"`)
15,776✔
4790
            .join(", ")
15,776✔
4791
        const referencedColumnNames = foreignKey.referencedColumnNames
15,776✔
4792
            .map((column) => `"` + column + `"`)
15,776✔
4793
            .join(",")
15,776✔
4794
        let sql =
15,776✔
4795
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
15,776✔
4796
                foreignKey.name
15,776✔
4797
            }" FOREIGN KEY (${columnNames}) ` +
15,776✔
4798
            `REFERENCES ${this.escapePath(
15,776✔
4799
                this.getTablePath(foreignKey),
15,776✔
4800
            )}(${referencedColumnNames})`
15,776✔
4801
        if (foreignKey.onDelete) sql += ` ON DELETE ${foreignKey.onDelete}`
15,776✔
4802
        if (foreignKey.onUpdate) sql += ` ON UPDATE ${foreignKey.onUpdate}`
15,776✔
4803
        if (foreignKey.deferrable) sql += ` DEFERRABLE ${foreignKey.deferrable}`
15,776✔
4804

15,776✔
4805
        return new Query(sql)
15,776✔
4806
    }
15,776✔
4807

26✔
4808
    /**
26✔
4809
     * Builds drop foreign key sql.
26✔
4810
     * @param table
26✔
4811
     * @param foreignKeyOrName
26✔
4812
     */
26✔
4813
    protected dropForeignKeySql(
26✔
4814
        table: Table,
15,796✔
4815
        foreignKeyOrName: TableForeignKey | string,
15,796✔
4816
    ): Query {
15,796✔
4817
        const foreignKeyName = InstanceChecker.isTableForeignKey(
15,796✔
4818
            foreignKeyOrName,
15,796✔
4819
        )
15,796✔
4820
            ? foreignKeyOrName.name
15,796✔
4821
            : foreignKeyOrName
15,796!
4822
        return new Query(
15,796✔
4823
            `ALTER TABLE ${this.escapePath(
15,796✔
4824
                table,
15,796✔
4825
            )} DROP CONSTRAINT "${foreignKeyName}"`,
15,796✔
4826
        )
15,796✔
4827
    }
15,796✔
4828

26✔
4829
    /**
26✔
4830
     * Builds sequence name from given table and column.
26✔
4831
     * @param table
26✔
4832
     * @param columnOrName
26✔
4833
     */
26✔
4834
    protected buildSequenceName(
26✔
4835
        table: Table,
5,888✔
4836
        columnOrName: TableColumn | string,
5,888✔
4837
    ): string {
5,888✔
4838
        const { tableName } = this.driver.parseTableName(table)
5,888✔
4839

5,888✔
4840
        const columnName = InstanceChecker.isTableColumn(columnOrName)
5,888✔
4841
            ? columnOrName.name
5,888✔
4842
            : columnOrName
5,888✔
4843

5,888✔
4844
        let seqName = `${tableName}_${columnName}_seq`
5,888✔
4845

5,888✔
4846
        if (seqName.length > this.connection.driver.maxAliasLength!) {
5,888✔
4847
            // note doesn't yet handle corner cases where .length differs from number of UTF-8 bytes
32✔
4848
            seqName = `${tableName.substring(0, 29)}_${columnName.substring(
32✔
4849
                0,
32✔
4850
                Math.max(29, 63 - table.name.length - 5),
32✔
4851
            )}_seq`
32✔
4852
        }
32✔
4853

5,888✔
4854
        return seqName
5,888✔
4855
    }
5,888✔
4856

26✔
4857
    protected buildSequencePath(
26✔
4858
        table: Table,
2,992✔
4859
        columnOrName: TableColumn | string,
2,992✔
4860
    ): string {
2,992✔
4861
        const { schema } = this.driver.parseTableName(table)
2,992✔
4862

2,992✔
4863
        return schema
2,992✔
4864
            ? `${schema}.${this.buildSequenceName(table, columnOrName)}`
2,992✔
4865
            : this.buildSequenceName(table, columnOrName)
2,992!
4866
    }
2,992✔
4867

26✔
4868
    /**
26✔
4869
     * Builds ENUM type name from given table and column.
26✔
4870
     * @param table
26✔
4871
     * @param column
26✔
4872
     * @param withSchema
26✔
4873
     * @param disableEscape
26✔
4874
     * @param toOld
26✔
4875
     */
26✔
4876
    protected buildEnumName(
26✔
4877
        table: Table,
4,818✔
4878
        column: TableColumn,
4,818✔
4879
        withSchema: boolean = true,
4,818✔
4880
        disableEscape?: boolean,
4,818✔
4881
        toOld?: boolean,
4,818✔
4882
    ): string {
4,818✔
4883
        const { schema, tableName } = this.driver.parseTableName(table)
4,818✔
4884
        let enumName = column.enumName
4,818✔
4885
            ? column.enumName
4,818✔
4886
            : `${tableName}_${column.name.toLowerCase()}_enum`
4,818✔
4887
        if (schema && withSchema) enumName = `${schema}.${enumName}`
4,818✔
4888
        if (toOld) enumName = enumName + "_old"
4,818✔
4889
        return enumName
4,818✔
4890
            .split(".")
4,818✔
4891
            .map((i) => {
4,818✔
4892
                return disableEscape ? i : `"${i}"`
7,146✔
4893
            })
4,818✔
4894
            .join(".")
4,818✔
4895
    }
4,818✔
4896

26✔
4897
    protected async getUserDefinedTypeName(table: Table, column: TableColumn) {
26✔
4898
        let { schema, tableName: name } = this.driver.parseTableName(table)
1,334✔
4899

1,334✔
4900
        if (!schema) {
1,334!
4901
            schema = await this.getCurrentSchema()
×
4902
        }
×
4903

1,334✔
4904
        const result = await this.query(
1,334✔
4905
            `SELECT "udt_schema", "udt_name" ` +
1,334✔
4906
                `FROM "information_schema"."columns" WHERE "table_schema" = '${schema}' AND "table_name" = '${name}' AND "column_name"='${column.name}'`,
1,334✔
4907
        )
1,334✔
4908

1,334✔
4909
        // docs: https://www.postgresql.org/docs/current/xtypes.html
1,334✔
4910
        // When you define a new base type, PostgreSQL automatically provides support for arrays of that type.
1,334✔
4911
        // The array type typically has the same name as the base type with the underscore character (_) prepended.
1,334✔
4912
        // ----
1,334✔
4913
        // so, we must remove this underscore character from enum type name
1,334✔
4914
        let udtName = result[0]["udt_name"]
1,334✔
4915
        if (udtName.indexOf("_") === 0) {
1,334✔
4916
            udtName = udtName.substr(1, udtName.length)
596✔
4917
        }
596✔
4918
        return {
1,334✔
4919
            schema: result[0]["udt_schema"],
1,334✔
4920
            name: udtName,
1,334✔
4921
        }
1,334✔
4922
    }
1,334✔
4923

26✔
4924
    /**
26✔
4925
     * Escapes a given comment so it's safe to include in a query.
26✔
4926
     * @param comment
26✔
4927
     */
26✔
4928
    protected escapeComment(comment?: string) {
26✔
4929
        if (!comment || comment.length === 0) {
3,108✔
4930
            return "NULL"
2,604✔
4931
        }
2,604✔
4932

504✔
4933
        comment = comment.replace(/'/g, "''").replace(/\u0000/g, "") // Null bytes aren't allowed in comments
504✔
4934

504✔
4935
        return `'${comment}'`
504✔
4936
    }
504✔
4937

26✔
4938
    /**
26✔
4939
     * Escapes given table or view path.
26✔
4940
     * @param target
26✔
4941
     */
26✔
4942
    protected escapePath(target: Table | View | string): string {
26✔
4943
        const { schema, tableName } = this.driver.parseTableName(target)
108,360✔
4944

108,360✔
4945
        if (schema && schema !== this.driver.searchSchema) {
108,360✔
4946
            return `"${schema}"."${tableName}"`
1,024✔
4947
        }
1,024✔
4948

107,336✔
4949
        return `"${tableName}"`
107,336✔
4950
    }
107,336✔
4951

26✔
4952
    /**
26✔
4953
     * Get the table name with table schema
26✔
4954
     * Note: Without ' or "
26✔
4955
     * @param target
26✔
4956
     */
26✔
4957
    protected async getTableNameWithSchema(target: Table | string) {
26✔
4958
        const tableName = InstanceChecker.isTable(target) ? target.name : target
132!
4959
        if (tableName.indexOf(".") === -1) {
132✔
4960
            const schemaResult = await this.query(`SELECT current_schema()`)
132✔
4961
            const schema = schemaResult[0]["current_schema"]
132✔
4962
            return `${schema}.${tableName}`
132✔
4963
        } else {
132!
4964
            return `${tableName.split(".")[0]}.${tableName.split(".")[1]}`
×
4965
        }
×
4966
    }
132✔
4967

26✔
4968
    /**
26✔
4969
     * Builds a query for create column.
26✔
4970
     * @param table
26✔
4971
     * @param column
26✔
4972
     */
26✔
4973
    protected buildCreateColumnSql(table: Table, column: TableColumn) {
26✔
4974
        let c = '"' + column.name + '"'
83,092✔
4975
        if (
83,092✔
4976
            column.isGenerated === true &&
83,092✔
4977
            column.generationStrategy !== "uuid"
15,252✔
4978
        ) {
83,092✔
4979
            if (column.generationStrategy === "identity") {
14,496✔
4980
                // Postgres 10+ Identity generated column
20✔
4981
                const generatedIdentityOrDefault =
20✔
4982
                    column.generatedIdentity || "BY DEFAULT"
20✔
4983
                c += ` ${column.type} GENERATED ${generatedIdentityOrDefault} AS IDENTITY`
20✔
4984
            } else {
14,496✔
4985
                // classic SERIAL primary column
14,476✔
4986
                if (
14,476✔
4987
                    column.type === "integer" ||
14,476✔
4988
                    column.type === "int" ||
14,476✔
4989
                    column.type === "int4"
40✔
4990
                )
14,476✔
4991
                    c += " SERIAL"
14,476✔
4992
                if (column.type === "smallint" || column.type === "int2")
14,476✔
4993
                    c += " SMALLSERIAL"
14,476✔
4994
                if (column.type === "bigint" || column.type === "int8")
14,476✔
4995
                    c += " BIGSERIAL"
14,476✔
4996
            }
14,476✔
4997
        }
14,496✔
4998
        if (column.type === "enum" || column.type === "simple-enum") {
83,092✔
4999
            c += " " + this.buildEnumName(table, column)
1,060✔
5000
            if (column.isArray) c += " array"
1,060✔
5001
        } else if (!column.isGenerated || column.type === "uuid") {
83,092✔
5002
            c += " " + this.connection.driver.createFullType(column)
67,536✔
5003
        }
67,536✔
5004

83,092✔
5005
        // Postgres only supports the stored generated column type
83,092✔
5006
        if (column.generatedType === "STORED" && column.asExpression) {
83,092✔
5007
            c += ` GENERATED ALWAYS AS (${column.asExpression}) STORED`
132✔
5008
        }
132✔
5009

83,092✔
5010
        if (column.charset) c += ' CHARACTER SET "' + column.charset + '"'
83,092!
5011
        if (column.collation) c += ' COLLATE "' + column.collation + '"'
83,092✔
5012
        if (column.isNullable !== true) c += " NOT NULL"
83,092✔
5013
        if (column.default !== undefined && column.default !== null)
83,092✔
5014
            c += " DEFAULT " + column.default
83,092✔
5015
        if (
83,092✔
5016
            column.isGenerated &&
83,092✔
5017
            column.generationStrategy === "uuid" &&
83,092✔
5018
            !column.default
756✔
5019
        )
83,092✔
5020
            c += ` DEFAULT ${this.driver.uuidGenerator}`
83,092✔
5021

83,092✔
5022
        return c
83,092✔
5023
    }
83,092✔
5024

26✔
5025
    /**
26✔
5026
     * Checks if the PostgreSQL server has support for partitioned tables
26✔
5027
     */
26✔
5028
    protected async hasSupportForPartitionedTables() {
26✔
5029
        const result = await this.query(
2,072✔
5030
            `SELECT TRUE FROM information_schema.columns WHERE table_name = 'pg_class' and column_name = 'relispartition'`,
2,072✔
5031
        )
2,072✔
5032
        return result.length ? true : false
2,072!
5033
    }
2,072✔
5034

26✔
5035
    /**
26✔
5036
     * Change table comment.
26✔
5037
     * @param tableOrName
26✔
5038
     * @param newComment
26✔
5039
     */
26✔
5040
    async changeTableComment(
26✔
5041
        tableOrName: Table | string,
1,312✔
5042
        newComment?: string,
1,312✔
5043
    ): Promise<void> {
1,312✔
5044
        const upQueries: Query[] = []
1,312✔
5045
        const downQueries: Query[] = []
1,312✔
5046

1,312✔
5047
        const table = InstanceChecker.isTable(tableOrName)
1,312✔
5048
            ? tableOrName
1,312✔
5049
            : await this.getCachedTable(tableOrName)
1,312!
5050

×
NEW
5051
        const escapedNewComment = this.escapeComment(newComment)
×
NEW
5052
        const escapedComment = this.escapeComment(table.comment)
×
UNCOV
5053

×
5054
        if (escapedNewComment === escapedComment) {
1,312✔
5055
            return
1,292✔
5056
        }
1,292✔
5057

20✔
5058
        const newTable = table.clone()
20✔
5059
        newTable.comment = newComment
20✔
5060

20✔
5061
        upQueries.push(
20✔
5062
            new Query(
20✔
5063
                `COMMENT ON TABLE ${this.escapePath(
20✔
5064
                    newTable,
20✔
5065
                )} IS ${escapedNewComment}`,
20✔
5066
            ),
20✔
5067
        )
20✔
5068

20✔
5069
        downQueries.push(
20✔
5070
            new Query(
20✔
5071
                `COMMENT ON TABLE ${this.escapePath(table)} IS ${escapedComment}`,
20✔
5072
            ),
20✔
5073
        )
20✔
5074

20✔
5075
        await this.executeQueries(upQueries, downQueries)
20✔
5076

20✔
5077
        table.comment = newTable.comment
20✔
5078
        this.replaceCachedTable(table, newTable)
20✔
5079
    }
20✔
5080
}
26✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc