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

typeorm / typeorm / 19549987525

20 Nov 2025 08:11PM UTC coverage: 80.769% (+4.3%) from 76.433%
19549987525

push

github

web-flow
ci: run tests on commits to master and next (#11783)

Co-authored-by: Oleg "OSA413" Sokolov <OSA413@users.noreply.github.com>

26500 of 32174 branches covered (82.36%)

Branch coverage included in aggregate %.

91252 of 113615 relevant lines covered (80.32%)

88980.79 hits per line

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

91.49
/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 { DriverUtils } from "../DriverUtils"
26✔
24
import { Query } from "../Query"
26✔
25
import { ColumnType } from "../types/ColumnTypes"
26✔
26
import { IsolationLevel } from "../types/IsolationLevel"
26✔
27
import { MetadataTableType } from "../types/MetadataTableType"
26✔
28
import { ReplicationMode } from "../types/ReplicationMode"
26✔
29
import { PostgresDriver } from "./PostgresDriver"
26✔
30

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

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

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

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

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

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

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

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

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

55,272✔
85
        if (this.databaseConnectionPromise)
55,272✔
86
            return this.databaseConnectionPromise
307,608✔
87

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

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

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

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

55,004✔
127
                    return this.databaseConnection
55,004✔
128
                })
55,008✔
129
        }
55,008✔
130

55,020✔
131
        return this.databaseConnectionPromise
55,020✔
132
    }
55,020✔
133

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

57,692✔
143
        this.isReleased = true
57,692✔
144
        if (this.releaseCallback) {
57,692✔
145
            this.releaseCallback(err)
55,016✔
146
            this.releaseCallback = undefined
55,016✔
147
        }
55,016✔
148

57,692✔
149
        const index = this.driver.connectedQueryRunners.indexOf(this)
57,692✔
150

57,692✔
151
        if (index !== -1) {
57,692✔
152
            this.driver.connectedQueryRunners.splice(index, 1)
55,016✔
153
        }
55,016✔
154
    }
57,692✔
155

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

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

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

41,572✔
188
        await this.broadcaster.broadcast("AfterTransactionStart")
41,572✔
189
    }
41,572✔
190

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

41,420✔
198
        await this.broadcaster.broadcast("BeforeTransactionCommit")
41,420✔
199

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

41,416✔
210
        await this.broadcaster.broadcast("AfterTransactionCommit")
41,416✔
211
    }
41,416✔
212

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

156✔
220
        await this.broadcaster.broadcast("BeforeTransactionRollback")
156✔
221

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

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

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

307,580✔
245
        const databaseConnection = await this.connect()
307,580✔
246

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

307,576✔
250
        const broadcasterResult = new BroadcasterResult()
307,576✔
251

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

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

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

307,504✔
282
            const result = new QueryResult()
307,504✔
283
            if (raw) {
307,504✔
284
                if (raw.hasOwnProperty("rows")) {
307,504✔
285
                    result.records = raw.rows
307,356✔
286
                }
307,356✔
287

307,504✔
288
                if (raw.hasOwnProperty("rowCount")) {
307,504✔
289
                    result.affected = raw.rowCount
307,356✔
290
                }
307,356✔
291

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

307,504✔
302
                if (!useStructuredResult) {
307,504✔
303
                    return result.raw
241,392✔
304
                }
241,392✔
305
            }
307,504✔
306

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

72✔
325
            throw new QueryFailedError(query, parameters, err)
72✔
326
        } finally {
307,580!
327
            await broadcasterResult.wait()
307,576✔
328
        }
307,576✔
329
    }
307,580✔
330

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

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

8✔
351
        return stream
8✔
352
    }
8✔
353

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

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

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

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

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

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

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

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

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

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

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

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

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

8✔
449
            if (databaseAlreadyExists) return Promise.resolve()
8!
450
        }
8✔
451

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

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

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

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

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

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

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

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

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

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

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

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

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

22,520✔
575
        upQueries.push(this.createTableSql(table, createForeignKeys))
22,520✔
576
        downQueries.push(this.dropTableSql(table))
22,520✔
577

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

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

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

22,520✔
616
        await this.executeQueries(upQueries, downQueries)
22,520✔
617
    }
22,520✔
618

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

60✔
743
        newTable.name = schemaName
60✔
744
            ? `${schemaName}.${newTableName}`
76✔
745
            : newTableName
76!
746

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

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

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

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

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

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

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

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

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

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

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

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

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

12✔
862
            // replace constraint name
12✔
863
            unique.name = newUniqueName
12✔
864
        })
76✔
865

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

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

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

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

36✔
895
            // replace constraint name
36✔
896
            index.name = newIndexName
36✔
897
        })
76✔
898

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

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

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

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

4✔
941
            // replace constraint name
4✔
942
            foreignKey.name = newForeignKeyName
4✔
943
        })
76✔
944

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

152✔
1160
        await this.executeQueries(upQueries, downQueries)
152✔
1161

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

16✔
1418
                    // replace constraint name
16✔
1419
                    unique.name = newUniqueName
16✔
1420
                })
100✔
1421

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

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

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

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

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

28✔
1459
                    // replace constraint name
28✔
1460
                    index.name = newIndexName
28✔
1461
                })
100✔
1462

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

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

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

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

4✔
1512
                        // replace constraint name
4✔
1513
                        foreignKey.name = newForeignKeyName
4✔
1514
                    })
100✔
1515

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

388✔
2186
            // update column collation
388✔
2187
            if (newColumn.collation !== oldColumn.collation) {
388✔
2188
                upQueries.push(
8✔
2189
                    new Query(
8✔
2190
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
8✔
2191
                            newColumn.name
8✔
2192
                        }" TYPE ${newColumn.type} COLLATE "${
8✔
2193
                            newColumn.collation
8✔
2194
                        }"`,
8✔
2195
                    ),
8✔
2196
                )
8✔
2197

8✔
2198
                const oldCollation = oldColumn.collation
8✔
2199
                    ? `"${oldColumn.collation}"`
8✔
2200
                    : `pg_catalog."default"` // if there's no old collation, use default
8!
2201

8✔
2202
                downQueries.push(
8✔
2203
                    new Query(
8✔
2204
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
8✔
2205
                            newColumn.name
8✔
2206
                        }" TYPE ${newColumn.type} COLLATE ${oldCollation}`,
8✔
2207
                    ),
8✔
2208
                )
8✔
2209
            }
8✔
2210

388✔
2211
            if (newColumn.generatedType !== oldColumn.generatedType) {
388✔
2212
                // Convert generated column data to normal column
8✔
2213
                if (
8✔
2214
                    !newColumn.generatedType ||
8!
2215
                    newColumn.generatedType === "VIRTUAL"
×
2216
                ) {
8✔
2217
                    // We can copy the generated data to the new column
8✔
2218
                    const tableNameWithSchema = (
8✔
2219
                        await this.getTableNameWithSchema(table.name)
8✔
2220
                    ).split(".")
8✔
2221
                    const tableName = tableNameWithSchema[1]
8✔
2222
                    const schema = tableNameWithSchema[0]
8✔
2223

8✔
2224
                    upQueries.push(
8✔
2225
                        new Query(
8✔
2226
                            `ALTER TABLE ${this.escapePath(
8✔
2227
                                table,
8✔
2228
                            )} RENAME COLUMN "${oldColumn.name}" TO "TEMP_OLD_${
8✔
2229
                                oldColumn.name
8✔
2230
                            }"`,
8✔
2231
                        ),
8✔
2232
                    )
8✔
2233
                    upQueries.push(
8✔
2234
                        new Query(
8✔
2235
                            `ALTER TABLE ${this.escapePath(
8✔
2236
                                table,
8✔
2237
                            )} ADD ${this.buildCreateColumnSql(
8✔
2238
                                table,
8✔
2239
                                newColumn,
8✔
2240
                            )}`,
8✔
2241
                        ),
8✔
2242
                    )
8✔
2243
                    upQueries.push(
8✔
2244
                        new Query(
8✔
2245
                            `UPDATE ${this.escapePath(table)} SET "${
8✔
2246
                                newColumn.name
8✔
2247
                            }" = "TEMP_OLD_${oldColumn.name}"`,
8✔
2248
                        ),
8✔
2249
                    )
8✔
2250
                    upQueries.push(
8✔
2251
                        new Query(
8✔
2252
                            `ALTER TABLE ${this.escapePath(
8✔
2253
                                table,
8✔
2254
                            )} DROP COLUMN "TEMP_OLD_${oldColumn.name}"`,
8✔
2255
                        ),
8✔
2256
                    )
8✔
2257
                    upQueries.push(
8✔
2258
                        this.deleteTypeormMetadataSql({
8✔
2259
                            database: this.driver.database,
8✔
2260
                            schema,
8✔
2261
                            table: tableName,
8✔
2262
                            type: MetadataTableType.GENERATED_COLUMN,
8✔
2263
                            name: oldColumn.name,
8✔
2264
                        }),
8✔
2265
                    )
8✔
2266
                    // However, we can't copy it back on downgrade. It needs to regenerate.
8✔
2267
                    downQueries.push(
8✔
2268
                        this.insertTypeormMetadataSql({
8✔
2269
                            database: this.driver.database,
8✔
2270
                            schema,
8✔
2271
                            table: tableName,
8✔
2272
                            type: MetadataTableType.GENERATED_COLUMN,
8✔
2273
                            name: oldColumn.name,
8✔
2274
                            value: oldColumn.asExpression,
8✔
2275
                        }),
8✔
2276
                    )
8✔
2277
                    downQueries.push(
8✔
2278
                        new Query(
8✔
2279
                            `ALTER TABLE ${this.escapePath(
8✔
2280
                                table,
8✔
2281
                            )} ADD ${this.buildCreateColumnSql(
8✔
2282
                                table,
8✔
2283
                                oldColumn,
8✔
2284
                            )}`,
8✔
2285
                        ),
8✔
2286
                    )
8✔
2287
                    downQueries.push(
8✔
2288
                        new Query(
8✔
2289
                            `ALTER TABLE ${this.escapePath(
8✔
2290
                                table,
8✔
2291
                            )} DROP COLUMN "${newColumn.name}"`,
8✔
2292
                        ),
8✔
2293
                    )
8✔
2294
                    // downQueries.push(
8✔
2295
                    //     this.deleteTypeormMetadataSql({
8✔
2296
                    //         database: this.driver.database,
8✔
2297
                    //         schema,
8✔
2298
                    //         table: tableName,
8✔
2299
                    //         type: MetadataTableType.GENERATED_COLUMN,
8✔
2300
                    //         name: newColumn.name,
8✔
2301
                    //     }),
8✔
2302
                    // )
8✔
2303
                }
8✔
2304
            }
8✔
2305
        }
388✔
2306

488✔
2307
        await this.executeQueries(upQueries, downQueries)
488✔
2308
        this.replaceCachedTable(table, clonedTable)
488✔
2309
    }
488✔
2310

26✔
2311
    /**
26✔
2312
     * Changes a column in the table.
26✔
2313
     */
26✔
2314
    async changeColumns(
26✔
2315
        tableOrName: Table | string,
232✔
2316
        changedColumns: { newColumn: TableColumn; oldColumn: TableColumn }[],
232✔
2317
    ): Promise<void> {
232✔
2318
        for (const { oldColumn, newColumn } of changedColumns) {
232✔
2319
            await this.changeColumn(tableOrName, oldColumn, newColumn)
308✔
2320
        }
308✔
2321
    }
232✔
2322

26✔
2323
    /**
26✔
2324
     * Drops column in the table.
26✔
2325
     */
26✔
2326
    async dropColumn(
26✔
2327
        tableOrName: Table | string,
184✔
2328
        columnOrName: TableColumn | string,
184✔
2329
    ): Promise<void> {
184✔
2330
        const table = InstanceChecker.isTable(tableOrName)
184✔
2331
            ? tableOrName
184✔
2332
            : await this.getCachedTable(tableOrName)
184✔
2333
        const column = InstanceChecker.isTableColumn(columnOrName)
12✔
2334
            ? columnOrName
184✔
2335
            : table.findColumnByName(columnOrName)
184✔
2336
        if (!column)
184✔
2337
            throw new TypeORMError(
184✔
2338
                `Column "${columnOrName}" was not found in table "${table.name}"`,
4✔
2339
            )
4✔
2340

180✔
2341
        const clonedTable = table.clone()
180✔
2342
        const upQueries: Query[] = []
180✔
2343
        const downQueries: Query[] = []
180✔
2344

180✔
2345
        // drop primary key constraint
180✔
2346
        if (column.isPrimary) {
184✔
2347
            const pkName = column.primaryKeyConstraintName
16✔
2348
                ? column.primaryKeyConstraintName
16!
2349
                : this.connection.namingStrategy.primaryKeyName(
16✔
2350
                      clonedTable,
16✔
2351
                      clonedTable.primaryColumns.map((column) => column.name),
16✔
2352
                  )
16✔
2353

16✔
2354
            const columnNames = clonedTable.primaryColumns
16✔
2355
                .map((primaryColumn) => `"${primaryColumn.name}"`)
16✔
2356
                .join(", ")
16✔
2357

16✔
2358
            upQueries.push(
16✔
2359
                new Query(
16✔
2360
                    `ALTER TABLE ${this.escapePath(
16✔
2361
                        clonedTable,
16✔
2362
                    )} DROP CONSTRAINT "${pkName}"`,
16✔
2363
                ),
16✔
2364
            )
16✔
2365
            downQueries.push(
16✔
2366
                new Query(
16✔
2367
                    `ALTER TABLE ${this.escapePath(
16✔
2368
                        clonedTable,
16✔
2369
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
16✔
2370
                ),
16✔
2371
            )
16✔
2372

16✔
2373
            // update column in table
16✔
2374
            const tableColumn = clonedTable.findColumnByName(column.name)
16✔
2375
            tableColumn!.isPrimary = false
16✔
2376

16✔
2377
            // if primary key have multiple columns, we must recreate it without dropped column
16✔
2378
            if (clonedTable.primaryColumns.length > 0) {
16!
2379
                const pkName = clonedTable.primaryColumns[0]
×
2380
                    .primaryKeyConstraintName
×
2381
                    ? clonedTable.primaryColumns[0].primaryKeyConstraintName
×
2382
                    : this.connection.namingStrategy.primaryKeyName(
×
2383
                          clonedTable,
×
2384
                          clonedTable.primaryColumns.map(
×
2385
                              (column) => column.name,
×
2386
                          ),
×
2387
                      )
×
2388

×
2389
                const columnNames = clonedTable.primaryColumns
×
2390
                    .map((primaryColumn) => `"${primaryColumn.name}"`)
×
2391
                    .join(", ")
×
2392

×
2393
                upQueries.push(
×
2394
                    new Query(
×
2395
                        `ALTER TABLE ${this.escapePath(
×
2396
                            clonedTable,
×
2397
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
×
2398
                    ),
×
2399
                )
×
2400
                downQueries.push(
×
2401
                    new Query(
×
2402
                        `ALTER TABLE ${this.escapePath(
×
2403
                            clonedTable,
×
2404
                        )} DROP CONSTRAINT "${pkName}"`,
×
2405
                    ),
×
2406
                )
×
2407
            }
×
2408
        }
16✔
2409

180✔
2410
        // drop column index
180✔
2411
        const columnIndex = clonedTable.indices.find(
180✔
2412
            (index) =>
180✔
2413
                index.columnNames.length === 1 &&
12✔
2414
                index.columnNames[0] === column.name,
180✔
2415
        )
180✔
2416
        if (columnIndex) {
184!
2417
            clonedTable.indices.splice(
×
2418
                clonedTable.indices.indexOf(columnIndex),
×
2419
                1,
×
2420
            )
×
2421
            upQueries.push(this.dropIndexSql(table, columnIndex))
×
2422
            downQueries.push(this.createIndexSql(table, columnIndex))
×
2423
        }
×
2424

180✔
2425
        // drop column check
180✔
2426
        const columnCheck = clonedTable.checks.find(
180✔
2427
            (check) =>
180✔
2428
                !!check.columnNames &&
56✔
2429
                check.columnNames.length === 1 &&
56✔
2430
                check.columnNames[0] === column.name,
180✔
2431
        )
180✔
2432
        if (columnCheck) {
184✔
2433
            clonedTable.checks.splice(
8✔
2434
                clonedTable.checks.indexOf(columnCheck),
8✔
2435
                1,
8✔
2436
            )
8✔
2437
            upQueries.push(this.dropCheckConstraintSql(table, columnCheck))
8✔
2438
            downQueries.push(this.createCheckConstraintSql(table, columnCheck))
8✔
2439
        }
8✔
2440

180✔
2441
        // drop column unique
180✔
2442
        const columnUnique = clonedTable.uniques.find(
180✔
2443
            (unique) =>
180✔
2444
                unique.columnNames.length === 1 &&
96✔
2445
                unique.columnNames[0] === column.name,
180✔
2446
        )
180✔
2447
        if (columnUnique) {
184✔
2448
            clonedTable.uniques.splice(
12✔
2449
                clonedTable.uniques.indexOf(columnUnique),
12✔
2450
                1,
12✔
2451
            )
12✔
2452
            upQueries.push(this.dropUniqueConstraintSql(table, columnUnique))
12✔
2453
            downQueries.push(
12✔
2454
                this.createUniqueConstraintSql(table, columnUnique),
12✔
2455
            )
12✔
2456
        }
12✔
2457

180✔
2458
        upQueries.push(
180✔
2459
            new Query(
180✔
2460
                `ALTER TABLE ${this.escapePath(table)} DROP COLUMN "${
180✔
2461
                    column.name
180✔
2462
                }"`,
180✔
2463
            ),
180✔
2464
        )
180✔
2465
        downQueries.push(
180✔
2466
            new Query(
180✔
2467
                `ALTER TABLE ${this.escapePath(
180✔
2468
                    table,
180✔
2469
                )} ADD ${this.buildCreateColumnSql(table, column)}`,
180✔
2470
            ),
180✔
2471
        )
180✔
2472

180✔
2473
        // drop enum type
180✔
2474
        if (column.type === "enum" || column.type === "simple-enum") {
184✔
2475
            const hasEnum = await this.hasEnumType(table, column)
20✔
2476
            if (hasEnum) {
20✔
2477
                const enumType = await this.getUserDefinedTypeName(
20✔
2478
                    table,
20✔
2479
                    column,
20✔
2480
                )
20✔
2481
                const escapedEnumName = `"${enumType.schema}"."${enumType.name}"`
20✔
2482
                upQueries.push(
20✔
2483
                    this.dropEnumTypeSql(table, column, escapedEnumName),
20✔
2484
                )
20✔
2485
                downQueries.push(
20✔
2486
                    this.createEnumTypeSql(table, column, escapedEnumName),
20✔
2487
                )
20✔
2488
            }
20✔
2489
        }
20✔
2490

180✔
2491
        if (column.generatedType === "STORED") {
184✔
2492
            const tableNameWithSchema = (
12✔
2493
                await this.getTableNameWithSchema(table.name)
12✔
2494
            ).split(".")
12✔
2495
            const tableName = tableNameWithSchema[1]
12✔
2496
            const schema = tableNameWithSchema[0]
12✔
2497
            const deleteQuery = this.deleteTypeormMetadataSql({
12✔
2498
                database: this.driver.database,
12✔
2499
                schema,
12✔
2500
                table: tableName,
12✔
2501
                type: MetadataTableType.GENERATED_COLUMN,
12✔
2502
                name: column.name,
12✔
2503
            })
12✔
2504
            const insertQuery = this.insertTypeormMetadataSql({
12✔
2505
                database: this.driver.database,
12✔
2506
                schema,
12✔
2507
                table: tableName,
12✔
2508
                type: MetadataTableType.GENERATED_COLUMN,
12✔
2509
                name: column.name,
12✔
2510
                value: column.asExpression,
12✔
2511
            })
12✔
2512

12✔
2513
            upQueries.push(deleteQuery)
12✔
2514
            downQueries.push(insertQuery)
12✔
2515
        }
12✔
2516

180✔
2517
        await this.executeQueries(upQueries, downQueries)
180✔
2518

180✔
2519
        clonedTable.removeColumn(column)
180✔
2520
        this.replaceCachedTable(table, clonedTable)
180✔
2521
    }
180✔
2522

26✔
2523
    /**
26✔
2524
     * Drops the columns in the table.
26✔
2525
     */
26✔
2526
    async dropColumns(
26✔
2527
        tableOrName: Table | string,
28✔
2528
        columns: TableColumn[] | string[],
28✔
2529
    ): Promise<void> {
28✔
2530
        for (const column of [...columns]) {
28✔
2531
            await this.dropColumn(tableOrName, column)
60✔
2532
        }
60✔
2533
    }
28✔
2534

26✔
2535
    /**
26✔
2536
     * Creates a new primary key.
26✔
2537
     */
26✔
2538
    async createPrimaryKey(
26✔
2539
        tableOrName: Table | string,
8✔
2540
        columnNames: string[],
8✔
2541
        constraintName?: string,
8✔
2542
    ): Promise<void> {
8✔
2543
        const table = InstanceChecker.isTable(tableOrName)
8✔
2544
            ? tableOrName
8!
2545
            : await this.getCachedTable(tableOrName)
8✔
2546
        const clonedTable = table.clone()
8✔
2547

8✔
2548
        const up = this.createPrimaryKeySql(table, columnNames, constraintName)
8✔
2549

8✔
2550
        // mark columns as primary, because dropPrimaryKeySql build constraint name from table primary column names.
8✔
2551
        clonedTable.columns.forEach((column) => {
8✔
2552
            if (columnNames.find((columnName) => columnName === column.name))
20✔
2553
                column.isPrimary = true
20✔
2554
        })
8✔
2555
        const down = this.dropPrimaryKeySql(clonedTable)
8✔
2556

8✔
2557
        await this.executeQueries(up, down)
8✔
2558
        this.replaceCachedTable(table, clonedTable)
8✔
2559
    }
8✔
2560

26✔
2561
    /**
26✔
2562
     * Updates composite primary keys.
26✔
2563
     */
26✔
2564
    async updatePrimaryKeys(
26✔
2565
        tableOrName: Table | string,
12✔
2566
        columns: TableColumn[],
12✔
2567
    ): Promise<void> {
12✔
2568
        const table = InstanceChecker.isTable(tableOrName)
12✔
2569
            ? tableOrName
12✔
2570
            : await this.getCachedTable(tableOrName)
12!
2571
        const clonedTable = table.clone()
×
2572
        const columnNames = columns.map((column) => column.name)
✔
2573
        const upQueries: Query[] = []
×
2574
        const downQueries: Query[] = []
×
2575

×
2576
        // if table already have primary columns, we must drop them.
×
2577
        const primaryColumns = clonedTable.primaryColumns
×
2578
        if (primaryColumns.length > 0) {
12✔
2579
            const pkName = primaryColumns[0].primaryKeyConstraintName
12✔
2580
                ? primaryColumns[0].primaryKeyConstraintName
12!
2581
                : this.connection.namingStrategy.primaryKeyName(
12✔
2582
                      clonedTable,
12✔
2583
                      primaryColumns.map((column) => column.name),
12✔
2584
                  )
12✔
2585

12✔
2586
            const columnNamesString = primaryColumns
12✔
2587
                .map((column) => `"${column.name}"`)
12✔
2588
                .join(", ")
12✔
2589

12✔
2590
            upQueries.push(
12✔
2591
                new Query(
12✔
2592
                    `ALTER TABLE ${this.escapePath(
12✔
2593
                        table,
12✔
2594
                    )} DROP CONSTRAINT "${pkName}"`,
12✔
2595
                ),
12✔
2596
            )
12✔
2597
            downQueries.push(
12✔
2598
                new Query(
12✔
2599
                    `ALTER TABLE ${this.escapePath(
12✔
2600
                        table,
12✔
2601
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
12✔
2602
                ),
12✔
2603
            )
12✔
2604
        }
12✔
2605

12✔
2606
        // update columns in table.
12✔
2607
        clonedTable.columns
12✔
2608
            .filter((column) => columnNames.indexOf(column.name) !== -1)
12✔
2609
            .forEach((column) => (column.isPrimary = true))
12✔
2610

12✔
2611
        const pkName = primaryColumns[0]?.primaryKeyConstraintName
12✔
2612
            ? primaryColumns[0].primaryKeyConstraintName
12!
2613
            : this.connection.namingStrategy.primaryKeyName(
12✔
2614
                  clonedTable,
12✔
2615
                  columnNames,
12✔
2616
              )
12✔
2617

12✔
2618
        const columnNamesString = columnNames
12✔
2619
            .map((columnName) => `"${columnName}"`)
12✔
2620
            .join(", ")
12✔
2621

12✔
2622
        upQueries.push(
12✔
2623
            new Query(
12✔
2624
                `ALTER TABLE ${this.escapePath(
12✔
2625
                    table,
12✔
2626
                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
12✔
2627
            ),
12✔
2628
        )
12✔
2629
        downQueries.push(
12✔
2630
            new Query(
12✔
2631
                `ALTER TABLE ${this.escapePath(
12✔
2632
                    table,
12✔
2633
                )} DROP CONSTRAINT "${pkName}"`,
12✔
2634
            ),
12✔
2635
        )
12✔
2636

12✔
2637
        await this.executeQueries(upQueries, downQueries)
12✔
2638
        this.replaceCachedTable(table, clonedTable)
12✔
2639
    }
12✔
2640

26✔
2641
    /**
26✔
2642
     * Drops a primary key.
26✔
2643
     */
26✔
2644
    async dropPrimaryKey(
26✔
2645
        tableOrName: Table | string,
12✔
2646
        constraintName?: string,
12✔
2647
    ): Promise<void> {
12✔
2648
        const table = InstanceChecker.isTable(tableOrName)
12✔
2649
            ? tableOrName
12✔
2650
            : await this.getCachedTable(tableOrName)
12!
2651
        const up = this.dropPrimaryKeySql(table)
×
2652
        const down = this.createPrimaryKeySql(
×
2653
            table,
×
2654
            table.primaryColumns.map((column) => column.name),
✔
2655
            constraintName,
×
2656
        )
×
2657
        await this.executeQueries(up, down)
×
2658
        table.primaryColumns.forEach((column) => {
12✔
2659
            column.isPrimary = false
12✔
2660
        })
12✔
2661
    }
12✔
2662

26✔
2663
    /**
26✔
2664
     * Creates new unique constraint.
26✔
2665
     */
26✔
2666
    async createUniqueConstraint(
26✔
2667
        tableOrName: Table | string,
40✔
2668
        uniqueConstraint: TableUnique,
40✔
2669
    ): Promise<void> {
40✔
2670
        const table = InstanceChecker.isTable(tableOrName)
40✔
2671
            ? tableOrName
40✔
2672
            : await this.getCachedTable(tableOrName)
40✔
2673

24✔
2674
        // new unique constraint may be passed without name. In this case we generate unique name manually.
24✔
2675
        if (!uniqueConstraint.name)
24✔
2676
            uniqueConstraint.name =
40✔
2677
                this.connection.namingStrategy.uniqueConstraintName(
8✔
2678
                    table,
8✔
2679
                    uniqueConstraint.columnNames,
8✔
2680
                )
8✔
2681

40✔
2682
        const up = this.createUniqueConstraintSql(table, uniqueConstraint)
40✔
2683
        const down = this.dropUniqueConstraintSql(table, uniqueConstraint)
40✔
2684
        await this.executeQueries(up, down)
40✔
2685
        table.addUniqueConstraint(uniqueConstraint)
40✔
2686
    }
40✔
2687

26✔
2688
    /**
26✔
2689
     * Creates new unique constraints.
26✔
2690
     */
26✔
2691
    async createUniqueConstraints(
26✔
2692
        tableOrName: Table | string,
20✔
2693
        uniqueConstraints: TableUnique[],
20✔
2694
    ): Promise<void> {
20✔
2695
        for (const uniqueConstraint of uniqueConstraints) {
20✔
2696
            await this.createUniqueConstraint(tableOrName, uniqueConstraint)
32✔
2697
        }
32✔
2698
    }
20✔
2699

26✔
2700
    /**
26✔
2701
     * Drops unique constraint.
26✔
2702
     */
26✔
2703
    async dropUniqueConstraint(
26✔
2704
        tableOrName: Table | string,
40✔
2705
        uniqueOrName: TableUnique | string,
40✔
2706
    ): Promise<void> {
40✔
2707
        const table = InstanceChecker.isTable(tableOrName)
40✔
2708
            ? tableOrName
40✔
2709
            : await this.getCachedTable(tableOrName)
40✔
2710
        const uniqueConstraint = InstanceChecker.isTableUnique(uniqueOrName)
16✔
2711
            ? uniqueOrName
40✔
2712
            : table.uniques.find((u) => u.name === uniqueOrName)
40!
2713
        if (!uniqueConstraint)
40✔
2714
            throw new TypeORMError(
40!
2715
                `Supplied unique constraint was not found in table ${table.name}`,
×
2716
            )
×
2717

40✔
2718
        const up = this.dropUniqueConstraintSql(table, uniqueConstraint)
40✔
2719
        const down = this.createUniqueConstraintSql(table, uniqueConstraint)
40✔
2720
        await this.executeQueries(up, down)
40✔
2721
        table.removeUniqueConstraint(uniqueConstraint)
40✔
2722
    }
40✔
2723

26✔
2724
    /**
26✔
2725
     * Drops unique constraints.
26✔
2726
     */
26✔
2727
    async dropUniqueConstraints(
26✔
2728
        tableOrName: Table | string,
24✔
2729
        uniqueConstraints: TableUnique[],
24✔
2730
    ): Promise<void> {
24✔
2731
        for (const uniqueConstraint of [...uniqueConstraints]) {
24✔
2732
            await this.dropUniqueConstraint(tableOrName, uniqueConstraint)
36✔
2733
        }
36✔
2734
    }
24✔
2735

26✔
2736
    /**
26✔
2737
     * Creates new check constraint.
26✔
2738
     */
26✔
2739
    async createCheckConstraint(
26✔
2740
        tableOrName: Table | string,
20✔
2741
        checkConstraint: TableCheck,
20✔
2742
    ): Promise<void> {
20✔
2743
        const table = InstanceChecker.isTable(tableOrName)
20✔
2744
            ? tableOrName
20✔
2745
            : await this.getCachedTable(tableOrName)
20✔
2746

12✔
2747
        // new unique constraint may be passed without name. In this case we generate unique name manually.
12✔
2748
        if (!checkConstraint.name)
12✔
2749
            checkConstraint.name =
12✔
2750
                this.connection.namingStrategy.checkConstraintName(
12✔
2751
                    table,
12✔
2752
                    checkConstraint.expression!,
12✔
2753
                )
12✔
2754

20✔
2755
        const up = this.createCheckConstraintSql(table, checkConstraint)
20✔
2756
        const down = this.dropCheckConstraintSql(table, checkConstraint)
20✔
2757
        await this.executeQueries(up, down)
20✔
2758
        table.addCheckConstraint(checkConstraint)
20✔
2759
    }
20✔
2760

26✔
2761
    /**
26✔
2762
     * Creates new check constraints.
26✔
2763
     */
26✔
2764
    async createCheckConstraints(
26✔
2765
        tableOrName: Table | string,
8✔
2766
        checkConstraints: TableCheck[],
8✔
2767
    ): Promise<void> {
8✔
2768
        const promises = checkConstraints.map((checkConstraint) =>
8✔
2769
            this.createCheckConstraint(tableOrName, checkConstraint),
8✔
2770
        )
8✔
2771
        await Promise.all(promises)
8✔
2772
    }
8✔
2773

26✔
2774
    /**
26✔
2775
     * Drops check constraint.
26✔
2776
     */
26✔
2777
    async dropCheckConstraint(
26✔
2778
        tableOrName: Table | string,
12✔
2779
        checkOrName: TableCheck | string,
12✔
2780
    ): Promise<void> {
12✔
2781
        const table = InstanceChecker.isTable(tableOrName)
12✔
2782
            ? tableOrName
12✔
2783
            : await this.getCachedTable(tableOrName)
12!
2784
        const checkConstraint = InstanceChecker.isTableCheck(checkOrName)
×
2785
            ? checkOrName
12✔
2786
            : table.checks.find((c) => c.name === checkOrName)
12!
2787
        if (!checkConstraint)
12✔
2788
            throw new TypeORMError(
12!
2789
                `Supplied check constraint was not found in table ${table.name}`,
×
2790
            )
×
2791

12✔
2792
        const up = this.dropCheckConstraintSql(table, checkConstraint)
12✔
2793
        const down = this.createCheckConstraintSql(table, checkConstraint)
12✔
2794
        await this.executeQueries(up, down)
12✔
2795
        table.removeCheckConstraint(checkConstraint)
12✔
2796
    }
12✔
2797

26✔
2798
    /**
26✔
2799
     * Drops check constraints.
26✔
2800
     */
26✔
2801
    async dropCheckConstraints(
26✔
2802
        tableOrName: Table | string,
8✔
2803
        checkConstraints: TableCheck[],
8✔
2804
    ): Promise<void> {
8✔
2805
        const promises = checkConstraints.map((checkConstraint) =>
8✔
2806
            this.dropCheckConstraint(tableOrName, checkConstraint),
8✔
2807
        )
8✔
2808
        await Promise.all(promises)
8✔
2809
    }
8✔
2810

26✔
2811
    /**
26✔
2812
     * Creates new exclusion constraint.
26✔
2813
     */
26✔
2814
    async createExclusionConstraint(
26✔
2815
        tableOrName: Table | string,
16✔
2816
        exclusionConstraint: TableExclusion,
16✔
2817
    ): Promise<void> {
16✔
2818
        const table = InstanceChecker.isTable(tableOrName)
16✔
2819
            ? tableOrName
16✔
2820
            : await this.getCachedTable(tableOrName)
16✔
2821

8✔
2822
        // new unique constraint may be passed without name. In this case we generate unique name manually.
8✔
2823
        if (!exclusionConstraint.name)
8✔
2824
            exclusionConstraint.name =
8✔
2825
                this.connection.namingStrategy.exclusionConstraintName(
8✔
2826
                    table,
8✔
2827
                    exclusionConstraint.expression!,
8✔
2828
                )
8✔
2829

16✔
2830
        const up = this.createExclusionConstraintSql(table, exclusionConstraint)
16✔
2831
        const down = this.dropExclusionConstraintSql(table, exclusionConstraint)
16✔
2832
        await this.executeQueries(up, down)
16✔
2833
        table.addExclusionConstraint(exclusionConstraint)
16✔
2834
    }
16✔
2835

26✔
2836
    /**
26✔
2837
     * Creates new exclusion constraints.
26✔
2838
     */
26✔
2839
    async createExclusionConstraints(
26✔
2840
        tableOrName: Table | string,
12✔
2841
        exclusionConstraints: TableExclusion[],
12✔
2842
    ): Promise<void> {
12✔
2843
        const promises = exclusionConstraints.map((exclusionConstraint) =>
12✔
2844
            this.createExclusionConstraint(tableOrName, exclusionConstraint),
12✔
2845
        )
12✔
2846
        await Promise.all(promises)
12✔
2847
    }
12✔
2848

26✔
2849
    /**
26✔
2850
     * Drops exclusion constraint.
26✔
2851
     */
26✔
2852
    async dropExclusionConstraint(
26✔
2853
        tableOrName: Table | string,
12✔
2854
        exclusionOrName: TableExclusion | string,
12✔
2855
    ): Promise<void> {
12✔
2856
        const table = InstanceChecker.isTable(tableOrName)
12✔
2857
            ? tableOrName
12✔
2858
            : await this.getCachedTable(tableOrName)
12!
2859
        const exclusionConstraint = InstanceChecker.isTableExclusion(
×
2860
            exclusionOrName,
×
2861
        )
×
2862
            ? exclusionOrName
12✔
2863
            : table.exclusions.find((c) => c.name === exclusionOrName)
12!
2864
        if (!exclusionConstraint)
12✔
2865
            throw new TypeORMError(
12!
2866
                `Supplied exclusion constraint was not found in table ${table.name}`,
×
2867
            )
×
2868

12✔
2869
        const up = this.dropExclusionConstraintSql(table, exclusionConstraint)
12✔
2870
        const down = this.createExclusionConstraintSql(
12✔
2871
            table,
12✔
2872
            exclusionConstraint,
12✔
2873
        )
12✔
2874
        await this.executeQueries(up, down)
12✔
2875
        table.removeExclusionConstraint(exclusionConstraint)
12✔
2876
    }
12✔
2877

26✔
2878
    /**
26✔
2879
     * Drops exclusion constraints.
26✔
2880
     */
26✔
2881
    async dropExclusionConstraints(
26✔
2882
        tableOrName: Table | string,
8✔
2883
        exclusionConstraints: TableExclusion[],
8✔
2884
    ): Promise<void> {
8✔
2885
        const promises = exclusionConstraints.map((exclusionConstraint) =>
8✔
2886
            this.dropExclusionConstraint(tableOrName, exclusionConstraint),
8✔
2887
        )
8✔
2888
        await Promise.all(promises)
8✔
2889
    }
8✔
2890

26✔
2891
    /**
26✔
2892
     * Creates a new foreign key.
26✔
2893
     */
26✔
2894
    async createForeignKey(
26✔
2895
        tableOrName: Table | string,
14,748✔
2896
        foreignKey: TableForeignKey,
14,748✔
2897
    ): Promise<void> {
14,748✔
2898
        const table = InstanceChecker.isTable(tableOrName)
14,748✔
2899
            ? tableOrName
14,748✔
2900
            : await this.getCachedTable(tableOrName)
14,748✔
2901

20✔
2902
        // new FK may be passed without name. In this case we generate FK name manually.
20✔
2903
        if (!foreignKey.name)
20✔
2904
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
14,748✔
2905
                table,
4✔
2906
                foreignKey.columnNames,
4✔
2907
                this.getTablePath(foreignKey),
4✔
2908
                foreignKey.referencedColumnNames,
4✔
2909
            )
4✔
2910

14,748✔
2911
        const up = this.createForeignKeySql(table, foreignKey)
14,748✔
2912
        const down = this.dropForeignKeySql(table, foreignKey)
14,748✔
2913
        await this.executeQueries(up, down)
14,748✔
2914
        table.addForeignKey(foreignKey)
14,748✔
2915
    }
14,748✔
2916

26✔
2917
    /**
26✔
2918
     * Creates a new foreign keys.
26✔
2919
     */
26✔
2920
    async createForeignKeys(
26✔
2921
        tableOrName: Table | string,
8,732✔
2922
        foreignKeys: TableForeignKey[],
8,732✔
2923
    ): Promise<void> {
8,732✔
2924
        for (const foreignKey of foreignKeys) {
8,732✔
2925
            await this.createForeignKey(tableOrName, foreignKey)
14,744✔
2926
        }
14,744✔
2927
    }
8,732✔
2928

26✔
2929
    /**
26✔
2930
     * Drops a foreign key from the table.
26✔
2931
     */
26✔
2932
    async dropForeignKey(
26✔
2933
        tableOrName: Table | string,
72✔
2934
        foreignKeyOrName: TableForeignKey | string,
72✔
2935
    ): Promise<void> {
72✔
2936
        const table = InstanceChecker.isTable(tableOrName)
72✔
2937
            ? tableOrName
72✔
2938
            : await this.getCachedTable(tableOrName)
72✔
2939
        const foreignKey = InstanceChecker.isTableForeignKey(foreignKeyOrName)
16✔
2940
            ? foreignKeyOrName
72✔
2941
            : table.foreignKeys.find((fk) => fk.name === foreignKeyOrName)
72!
2942
        if (!foreignKey)
72✔
2943
            throw new TypeORMError(
72!
2944
                `Supplied foreign key was not found in table ${table.name}`,
×
2945
            )
×
2946

72✔
2947
        if (!foreignKey.name) {
72!
2948
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
×
2949
                table,
×
2950
                foreignKey.columnNames,
×
2951
                this.getTablePath(foreignKey),
×
2952
                foreignKey.referencedColumnNames,
×
2953
            )
×
2954
        }
×
2955

72✔
2956
        const up = this.dropForeignKeySql(table, foreignKey)
72✔
2957
        const down = this.createForeignKeySql(table, foreignKey)
72✔
2958
        await this.executeQueries(up, down)
72✔
2959
        table.removeForeignKey(foreignKey)
72✔
2960
    }
72✔
2961

26✔
2962
    /**
26✔
2963
     * Drops a foreign keys from the table.
26✔
2964
     */
26✔
2965
    async dropForeignKeys(
26✔
2966
        tableOrName: Table | string,
56✔
2967
        foreignKeys: TableForeignKey[],
56✔
2968
    ): Promise<void> {
56✔
2969
        for (const foreignKey of [...foreignKeys]) {
56✔
2970
            await this.dropForeignKey(tableOrName, foreignKey)
68✔
2971
        }
68✔
2972
    }
56✔
2973

26✔
2974
    /**
26✔
2975
     * Creates a new index.
26✔
2976
     */
26✔
2977
    async createIndex(
26✔
2978
        tableOrName: Table | string,
64✔
2979
        index: TableIndex,
64✔
2980
    ): Promise<void> {
64✔
2981
        const table = InstanceChecker.isTable(tableOrName)
64✔
2982
            ? tableOrName
64✔
2983
            : await this.getCachedTable(tableOrName)
64✔
2984

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

64✔
2988
        const up = this.createIndexSql(table, index)
64✔
2989
        const down = this.dropIndexSql(table, index)
64✔
2990
        await this.executeQueries(up, down)
64✔
2991
        table.addIndex(index)
64✔
2992
    }
64✔
2993

26✔
2994
    /**
26✔
2995
     * Create a new view index.
26✔
2996
     */
26✔
2997
    async createViewIndex(
26✔
2998
        viewOrName: View | string,
16✔
2999
        index: TableIndex,
16✔
3000
    ): Promise<void> {
16✔
3001
        const view = InstanceChecker.isView(viewOrName)
16✔
3002
            ? viewOrName
16✔
3003
            : await this.getCachedView(viewOrName)
16!
3004

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

16✔
3008
        const up = this.createViewIndexSql(view, index)
16✔
3009
        const down = this.dropIndexSql(view, index)
16✔
3010
        await this.executeQueries(up, down)
16✔
3011
        view.addIndex(index)
16✔
3012
    }
16✔
3013

26✔
3014
    /**
26✔
3015
     * Creates a new indices
26✔
3016
     */
26✔
3017
    async createIndices(
26✔
3018
        tableOrName: Table | string,
36✔
3019
        indices: TableIndex[],
36✔
3020
    ): Promise<void> {
36✔
3021
        for (const index of indices) {
36✔
3022
            await this.createIndex(tableOrName, index)
48✔
3023
        }
48✔
3024
    }
36✔
3025

26✔
3026
    /**
26✔
3027
     * Creates new view indices
26✔
3028
     */
26✔
3029
    async createViewIndices(
26✔
3030
        viewOrName: View | string,
16✔
3031
        indices: TableIndex[],
16✔
3032
    ): Promise<void> {
16✔
3033
        for (const index of indices) {
16✔
3034
            await this.createViewIndex(viewOrName, index)
16✔
3035
        }
16✔
3036
    }
16✔
3037

26✔
3038
    /**
26✔
3039
     * Drops an index from the table.
26✔
3040
     */
26✔
3041
    async dropIndex(
26✔
3042
        tableOrName: Table | string,
76✔
3043
        indexOrName: TableIndex | string,
76✔
3044
    ): Promise<void> {
76✔
3045
        const table = InstanceChecker.isTable(tableOrName)
76✔
3046
            ? tableOrName
76✔
3047
            : await this.getCachedTable(tableOrName)
76✔
3048
        const index = InstanceChecker.isTableIndex(indexOrName)
20✔
3049
            ? indexOrName
76✔
3050
            : table.indices.find((i) => i.name === indexOrName)
76!
3051
        if (!index)
76✔
3052
            throw new TypeORMError(
76!
3053
                `Supplied index ${indexOrName} was not found in table ${table.name}`,
×
3054
            )
×
3055
        // old index may be passed without name. In this case we generate index name manually.
76✔
3056
        if (!index.name) index.name = this.generateIndexName(table, index)
76✔
3057

76✔
3058
        const up = this.dropIndexSql(table, index)
76✔
3059
        const down = this.createIndexSql(table, index)
76✔
3060
        await this.executeQueries(up, down)
76✔
3061
        table.removeIndex(index)
76✔
3062
    }
76✔
3063

26✔
3064
    /**
26✔
3065
     * Drops an index from a view.
26✔
3066
     */
26✔
3067
    async dropViewIndex(
26✔
3068
        viewOrName: View | string,
12✔
3069
        indexOrName: TableIndex | string,
12✔
3070
    ): Promise<void> {
12✔
3071
        const view = InstanceChecker.isView(viewOrName)
12✔
3072
            ? viewOrName
12✔
3073
            : await this.getCachedView(viewOrName)
12!
3074
        const index = InstanceChecker.isTableIndex(indexOrName)
×
3075
            ? indexOrName
12✔
3076
            : view.indices.find((i) => i.name === indexOrName)
12!
3077
        if (!index)
12✔
3078
            throw new TypeORMError(
12!
3079
                `Supplied index ${indexOrName} was not found in view ${view.name}`,
×
3080
            )
×
3081
        // old index may be passed without name. In this case we generate index name manually.
12✔
3082
        if (!index.name) index.name = this.generateIndexName(view, index)
12!
3083

12✔
3084
        const up = this.dropIndexSql(view, index)
12✔
3085
        const down = this.createViewIndexSql(view, index)
12✔
3086
        await this.executeQueries(up, down)
12✔
3087
        view.removeIndex(index)
12✔
3088
    }
12✔
3089

26✔
3090
    /**
26✔
3091
     * Drops an indices from the table.
26✔
3092
     */
26✔
3093
    async dropIndices(
26✔
3094
        tableOrName: Table | string,
8✔
3095
        indices: TableIndex[],
8✔
3096
    ): Promise<void> {
8✔
3097
        for (const index of [...indices]) {
8✔
3098
            await this.dropIndex(tableOrName, index)
24✔
3099
        }
24✔
3100
    }
8✔
3101

26✔
3102
    /**
26✔
3103
     * Clears all table contents.
26✔
3104
     * Note: this operation uses SQL's TRUNCATE query which cannot be reverted in transactions.
26✔
3105
     */
26✔
3106
    async clearTable(tableName: string): Promise<void> {
26✔
3107
        await this.query(`TRUNCATE TABLE ${this.escapePath(tableName)}`)
24✔
3108
    }
24✔
3109

26✔
3110
    /**
26✔
3111
     * Removes all tables from the currently connected database.
26✔
3112
     */
26✔
3113
    async clearDatabase(): Promise<void> {
26✔
3114
        const schemas: string[] = []
7,140✔
3115
        this.connection.entityMetadatas
7,140✔
3116
            .filter((metadata) => metadata.schema)
7,140✔
3117
            .forEach((metadata) => {
7,140✔
3118
                const isSchemaExist = !!schemas.find(
184✔
3119
                    (schema) => schema === metadata.schema,
184✔
3120
                )
184✔
3121
                if (!isSchemaExist) schemas.push(metadata.schema!)
184✔
3122
            })
7,140✔
3123
        schemas.push(this.driver.options.schema || "current_schema()")
7,140✔
3124
        const schemaNamesString = schemas
7,140✔
3125
            .map((name) => {
7,140✔
3126
                return name === "current_schema()" ? name : "'" + name + "'"
7,284✔
3127
            })
7,140✔
3128
            .join(", ")
7,140✔
3129

7,140✔
3130
        const isAnotherTransactionActive = this.isTransactionActive
7,140✔
3131
        if (!isAnotherTransactionActive) await this.startTransaction()
7,140✔
3132
        try {
7,140✔
3133
            // drop views
7,140✔
3134
            const selectViewDropsQuery =
7,140✔
3135
                `SELECT 'DROP VIEW IF EXISTS "' || schemaname || '"."' || viewname || '" CASCADE;' as "query" ` +
7,140✔
3136
                `FROM "pg_views" WHERE "schemaname" IN (${schemaNamesString}) AND "viewname" NOT IN ('geography_columns', 'geometry_columns', 'raster_columns', 'raster_overviews')`
7,140✔
3137
            const dropViewQueries: ObjectLiteral[] = await this.query(
7,140✔
3138
                selectViewDropsQuery,
7,140✔
3139
            )
7,140✔
3140
            await Promise.all(
7,140✔
3141
                dropViewQueries.map((q) => this.query(q["query"])),
7,140✔
3142
            )
7,140✔
3143

7,140✔
3144
            // drop materialized views
7,140✔
3145
            // Note: materialized views introduced in Postgres 9.3
7,140✔
3146
            if (DriverUtils.isReleaseVersionOrGreater(this.driver, "9.3")) {
7,140✔
3147
                const selectMatViewDropsQuery =
7,140✔
3148
                    `SELECT 'DROP MATERIALIZED VIEW IF EXISTS "' || schemaname || '"."' || matviewname || '" CASCADE;' as "query" ` +
7,140✔
3149
                    `FROM "pg_matviews" WHERE "schemaname" IN (${schemaNamesString})`
7,140✔
3150
                const dropMatViewQueries: ObjectLiteral[] = await this.query(
7,140✔
3151
                    selectMatViewDropsQuery,
7,140✔
3152
                )
7,140✔
3153
                await Promise.all(
7,140✔
3154
                    dropMatViewQueries.map((q) => this.query(q["query"])),
7,140✔
3155
                )
7,140✔
3156
            }
7,140✔
3157

7,140✔
3158
            // ignore spatial_ref_sys; it's a special table supporting PostGIS
7,140✔
3159
            // TODO generalize this as this.driver.ignoreTables
7,140✔
3160

7,140✔
3161
            // drop tables
7,140✔
3162
            const selectTableDropsQuery = `SELECT 'DROP TABLE IF EXISTS "' || schemaname || '"."' || tablename || '" CASCADE;' as "query" FROM "pg_tables" WHERE "schemaname" IN (${schemaNamesString}) AND "tablename" NOT IN ('spatial_ref_sys')`
7,140✔
3163
            const dropTableQueries: ObjectLiteral[] = await this.query(
7,140✔
3164
                selectTableDropsQuery,
7,140✔
3165
            )
7,140✔
3166
            await Promise.all(
7,140✔
3167
                dropTableQueries.map((q) => this.query(q["query"])),
7,140✔
3168
            )
7,140✔
3169

7,140✔
3170
            // drop enum types
7,140✔
3171
            await this.dropEnumTypes(schemaNamesString)
7,140✔
3172

7,140✔
3173
            if (!isAnotherTransactionActive) {
7,140✔
3174
                await this.commitTransaction()
7,136✔
3175
            }
7,136✔
3176
        } catch (error) {
7,140!
3177
            try {
×
3178
                // we throw original error even if rollback thrown an error
×
3179
                if (!isAnotherTransactionActive) {
×
3180
                    await this.rollbackTransaction()
×
3181
                }
×
3182
            } catch {
×
3183
                // no-op
×
3184
            }
×
3185
            throw error
×
3186
        }
×
3187
    }
7,140✔
3188

26✔
3189
    // -------------------------------------------------------------------------
26✔
3190
    // Protected Methods
26✔
3191
    // -------------------------------------------------------------------------
26✔
3192

26✔
3193
    protected async loadViews(viewNames?: string[]): Promise<View[]> {
26✔
3194
        const hasTable = await this.hasTable(this.getTypeormMetadataTableName())
7,704✔
3195

7,704✔
3196
        if (!hasTable) return []
7,704✔
3197

196✔
3198
        if (!viewNames) {
7,704!
3199
            viewNames = []
×
3200
        }
×
3201

196✔
3202
        const currentDatabase = await this.getCurrentDatabase()
196✔
3203
        const currentSchema = await this.getCurrentSchema()
196✔
3204
        const viewsCondition =
196✔
3205
            viewNames.length === 0
196✔
3206
                ? "1=1"
7,704✔
3207
                : viewNames
7,704✔
3208
                      .map((tableName) => this.driver.parseTableName(tableName))
164✔
3209
                      .map(({ schema, tableName }) => {
164✔
3210
                          if (!schema) {
248!
3211
                              schema =
×
3212
                                  this.driver.options.schema || currentSchema
×
3213
                          }
×
3214

248✔
3215
                          return `("t"."schema" = '${schema}' AND "t"."name" = '${tableName}')`
248✔
3216
                      })
164✔
3217
                      .join(" OR ")
164✔
3218

7,704✔
3219
        const constraintsCondition =
7,704✔
3220
            viewNames.length === 0
7,704✔
3221
                ? "1=1"
7,704✔
3222
                : viewNames
7,704✔
3223
                      .map((tableName) => this.driver.parseTableName(tableName))
164✔
3224
                      .map(({ schema, tableName }) => {
164✔
3225
                          if (!schema) {
248!
3226
                              schema =
×
3227
                                  this.driver.options.schema || currentSchema
×
3228
                          }
×
3229

248✔
3230
                          return `("ns"."nspname" = '${schema}' AND "t"."relname" = '${tableName}')`
248✔
3231
                      })
164✔
3232
                      .join(" OR ")
164✔
3233

7,704✔
3234
        const indicesSql =
7,704✔
3235
            `SELECT "ns"."nspname" AS "table_schema", "t"."relname" AS "table_name", "i"."relname" AS "constraint_name", "a"."attname" AS "column_name", ` +
7,704✔
3236
            `CASE "ix"."indisunique" WHEN 't' THEN 'TRUE' ELSE'FALSE' END AS "is_unique", pg_get_expr("ix"."indpred", "ix"."indrelid") AS "condition", ` +
7,704✔
3237
            `"types"."typname" AS "type_name" ` +
7,704✔
3238
            `FROM "pg_class" "t" ` +
7,704✔
3239
            `INNER JOIN "pg_index" "ix" ON "ix"."indrelid" = "t"."oid" ` +
7,704✔
3240
            `INNER JOIN "pg_attribute" "a" ON "a"."attrelid" = "t"."oid"  AND "a"."attnum" = ANY ("ix"."indkey") ` +
7,704✔
3241
            `INNER JOIN "pg_namespace" "ns" ON "ns"."oid" = "t"."relnamespace" ` +
7,704✔
3242
            `INNER JOIN "pg_class" "i" ON "i"."oid" = "ix"."indexrelid" ` +
7,704✔
3243
            `INNER JOIN "pg_type" "types" ON "types"."oid" = "a"."atttypid" ` +
7,704✔
3244
            `LEFT JOIN "pg_constraint" "cnst" ON "cnst"."conname" = "i"."relname" ` +
7,704✔
3245
            `WHERE "t"."relkind" IN ('m') AND "cnst"."contype" IS NULL AND (${constraintsCondition})`
7,704✔
3246

7,704✔
3247
        const query =
7,704✔
3248
            `SELECT "t".* FROM ${this.escapePath(
7,704✔
3249
                this.getTypeormMetadataTableName(),
7,704✔
3250
            )} "t" ` +
7,704✔
3251
            `INNER JOIN "pg_catalog"."pg_class" "c" ON "c"."relname" = "t"."name" ` +
7,704✔
3252
            `INNER JOIN "pg_namespace" "n" ON "n"."oid" = "c"."relnamespace" AND "n"."nspname" = "t"."schema" ` +
7,704✔
3253
            `WHERE "t"."type" IN ('${MetadataTableType.VIEW}', '${
7,704✔
3254
                MetadataTableType.MATERIALIZED_VIEW
7,704✔
3255
            }') ${viewsCondition ? `AND (${viewsCondition})` : ""}`
7,704!
3256

7,704✔
3257
        const dbViews = await this.query(query)
7,704✔
3258
        const dbIndices: ObjectLiteral[] = await this.query(indicesSql)
196✔
3259
        return dbViews.map((dbView: any) => {
196✔
3260
            // find index constraints of table, group them by constraint name and build TableIndex.
100✔
3261
            const tableIndexConstraints = OrmUtils.uniq(
100✔
3262
                dbIndices.filter((dbIndex) => {
100✔
3263
                    return (
28✔
3264
                        dbIndex["table_name"] === dbView["name"] &&
28✔
3265
                        dbIndex["table_schema"] === dbView["schema"]
28✔
3266
                    )
28✔
3267
                }),
100✔
3268
                (dbIndex) => dbIndex["constraint_name"],
100✔
3269
            )
100✔
3270
            const view = new View()
100✔
3271
            const schema =
100✔
3272
                dbView["schema"] === currentSchema &&
100✔
3273
                !this.driver.options.schema
100✔
3274
                    ? undefined
100✔
3275
                    : dbView["schema"]
100!
3276
            view.database = currentDatabase
100✔
3277
            view.schema = dbView["schema"]
100✔
3278
            view.name = this.driver.buildTableName(dbView["name"], schema)
100✔
3279
            view.expression = dbView["value"]
100✔
3280
            view.materialized =
100✔
3281
                dbView["type"] === MetadataTableType.MATERIALIZED_VIEW
100✔
3282
            view.indices = tableIndexConstraints.map((constraint) => {
100✔
3283
                const indices = dbIndices.filter((index) => {
28✔
3284
                    return (
28✔
3285
                        index["table_schema"] === constraint["table_schema"] &&
28✔
3286
                        index["table_name"] === constraint["table_name"] &&
28✔
3287
                        index["constraint_name"] ===
28✔
3288
                            constraint["constraint_name"]
28✔
3289
                    )
28✔
3290
                })
28✔
3291
                return new TableIndex(<TableIndexOptions>{
28✔
3292
                    view: view,
28✔
3293
                    name: constraint["constraint_name"],
28✔
3294
                    columnNames: indices.map((i) => i["column_name"]),
28✔
3295
                    isUnique: constraint["is_unique"] === "TRUE",
28✔
3296
                    where: constraint["condition"],
28✔
3297
                    isFulltext: false,
28✔
3298
                })
28✔
3299
            })
100✔
3300
            return view
100✔
3301
        })
196✔
3302
    }
196✔
3303

26✔
3304
    /**
26✔
3305
     * Loads all tables (with given names) from the database and creates a Table from them.
26✔
3306
     */
26✔
3307
    protected async loadTables(tableNames?: string[]): Promise<Table[]> {
26✔
3308
        // if no tables given then no need to proceed
9,308✔
3309
        if (tableNames && tableNames.length === 0) {
9,308✔
3310
            return []
128✔
3311
        }
128✔
3312

9,180✔
3313
        const currentSchema = await this.getCurrentSchema()
9,180✔
3314
        const currentDatabase = await this.getCurrentDatabase()
9,180✔
3315

9,180✔
3316
        const dbTables: {
9,180✔
3317
            table_schema: string
9,180✔
3318
            table_name: string
9,180✔
3319
            table_comment: string
9,180✔
3320
        }[] = []
9,180✔
3321

9,180✔
3322
        if (!tableNames) {
9,308!
3323
            const tablesSql = `SELECT "table_schema", "table_name", obj_description(('"' || "table_schema" || '"."' || "table_name" || '"')::regclass, 'pg_class') AS table_comment FROM "information_schema"."tables"`
×
3324
            dbTables.push(...(await this.query(tablesSql)))
×
3325
        } else {
9,308✔
3326
            const tablesCondition = tableNames
9,180✔
3327
                .map((tableName) => this.driver.parseTableName(tableName))
9,180✔
3328
                .map(({ schema, tableName }) => {
9,180✔
3329
                    return `("table_schema" = '${
25,120✔
3330
                        schema || currentSchema
25,120!
3331
                    }' AND "table_name" = '${tableName}')`
25,120✔
3332
                })
9,180✔
3333
                .join(" OR ")
9,180✔
3334

9,180✔
3335
            const tablesSql =
9,180✔
3336
                `SELECT "table_schema", "table_name", obj_description(('"' || "table_schema" || '"."' || "table_name" || '"')::regclass, 'pg_class') AS table_comment FROM "information_schema"."tables" WHERE ` +
9,180✔
3337
                tablesCondition
9,180✔
3338
            dbTables.push(...(await this.query(tablesSql)))
9,180✔
3339
        }
9,180✔
3340

9,180✔
3341
        // if tables were not found in the db, no need to proceed
9,180✔
3342
        if (dbTables.length === 0) {
9,308✔
3343
            return []
7,140✔
3344
        }
7,140✔
3345

2,040✔
3346
        /**
2,040✔
3347
         * Uses standard SQL information_schema.columns table and postgres-specific
2,040✔
3348
         * pg_catalog.pg_attribute table to get column information.
2,040✔
3349
         * @see https://stackoverflow.com/a/19541865
2,040✔
3350
         */
2,040✔
3351
        const columnsCondition = dbTables
2,040✔
3352
            .map(({ table_schema, table_name }) => {
2,040✔
3353
                return `("table_schema" = '${table_schema}' AND "table_name" = '${table_name}')`
2,860✔
3354
            })
2,040✔
3355
            .join(" OR ")
2,040✔
3356
        const columnsSql =
2,040✔
3357
            `SELECT columns.*, pg_catalog.col_description(('"' || table_catalog || '"."' || table_schema || '"."' || table_name || '"')::regclass::oid, ordinal_position) AS description, ` +
2,040✔
3358
            `('"' || "udt_schema" || '"."' || "udt_name" || '"')::"regtype"::text AS "regtype", pg_catalog.format_type("col_attr"."atttypid", "col_attr"."atttypmod") AS "format_type" ` +
2,040✔
3359
            `FROM "information_schema"."columns" ` +
2,040✔
3360
            `LEFT JOIN "pg_catalog"."pg_attribute" AS "col_attr" ON "col_attr"."attname" = "columns"."column_name" ` +
2,040✔
3361
            `AND "col_attr"."attrelid" = ( ` +
2,040✔
3362
            `SELECT "cls"."oid" FROM "pg_catalog"."pg_class" AS "cls" ` +
2,040✔
3363
            `LEFT JOIN "pg_catalog"."pg_namespace" AS "ns" ON "ns"."oid" = "cls"."relnamespace" ` +
2,040✔
3364
            `WHERE "cls"."relname" = "columns"."table_name" ` +
2,040✔
3365
            `AND "ns"."nspname" = "columns"."table_schema" ` +
2,040✔
3366
            `) ` +
2,040✔
3367
            `WHERE ` +
2,040✔
3368
            columnsCondition
2,040✔
3369

2,040✔
3370
        const constraintsCondition = dbTables
2,040✔
3371
            .map(({ table_schema, table_name }) => {
2,040✔
3372
                return `("ns"."nspname" = '${table_schema}' AND "t"."relname" = '${table_name}')`
2,860✔
3373
            })
2,040✔
3374
            .join(" OR ")
2,040✔
3375

2,040✔
3376
        const constraintsSql =
2,040✔
3377
            `SELECT "ns"."nspname" AS "table_schema", "t"."relname" AS "table_name", "cnst"."conname" AS "constraint_name", ` +
2,040✔
3378
            `pg_get_constraintdef("cnst"."oid") AS "expression", ` +
2,040✔
3379
            `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,040✔
3380
            `FROM "pg_constraint" "cnst" ` +
2,040✔
3381
            `INNER JOIN "pg_class" "t" ON "t"."oid" = "cnst"."conrelid" ` +
2,040✔
3382
            `INNER JOIN "pg_namespace" "ns" ON "ns"."oid" = "cnst"."connamespace" ` +
2,040✔
3383
            `LEFT JOIN "pg_attribute" "a" ON "a"."attrelid" = "cnst"."conrelid" AND "a"."attnum" = ANY ("cnst"."conkey") ` +
2,040✔
3384
            `WHERE "t"."relkind" IN ('r', 'p') AND (${constraintsCondition})`
2,040✔
3385

2,040✔
3386
        const indicesSql =
2,040✔
3387
            `SELECT "ns"."nspname" AS "table_schema", "t"."relname" AS "table_name", "i"."relname" AS "constraint_name", "a"."attname" AS "column_name", ` +
2,040✔
3388
            `CASE "ix"."indisunique" WHEN 't' THEN 'TRUE' ELSE'FALSE' END AS "is_unique", pg_get_expr("ix"."indpred", "ix"."indrelid") AS "condition", ` +
2,040✔
3389
            `"types"."typname" AS "type_name", "am"."amname" AS "index_type" ` +
2,040✔
3390
            `FROM "pg_class" "t" ` +
2,040✔
3391
            `INNER JOIN "pg_index" "ix" ON "ix"."indrelid" = "t"."oid" ` +
2,040✔
3392
            `INNER JOIN "pg_attribute" "a" ON "a"."attrelid" = "t"."oid"  AND "a"."attnum" = ANY ("ix"."indkey") ` +
2,040✔
3393
            `INNER JOIN "pg_namespace" "ns" ON "ns"."oid" = "t"."relnamespace" ` +
2,040✔
3394
            `INNER JOIN "pg_class" "i" ON "i"."oid" = "ix"."indexrelid" ` +
2,040✔
3395
            `INNER JOIN "pg_type" "types" ON "types"."oid" = "a"."atttypid" ` +
2,040✔
3396
            `INNER JOIN "pg_am" "am" ON "i"."relam" = "am"."oid" ` +
2,040✔
3397
            `LEFT JOIN "pg_constraint" "cnst" ON "cnst"."conname" = "i"."relname" ` +
2,040✔
3398
            `WHERE "t"."relkind" IN ('r', 'p') AND "cnst"."contype" IS NULL AND (${constraintsCondition})`
2,040✔
3399

2,040✔
3400
        const foreignKeysCondition = dbTables
2,040✔
3401
            .map(({ table_schema, table_name }) => {
2,040✔
3402
                return `("ns"."nspname" = '${table_schema}' AND "cl"."relname" = '${table_name}')`
2,860✔
3403
            })
2,040✔
3404
            .join(" OR ")
2,040✔
3405

2,040✔
3406
        const hasRelispartitionColumn =
2,040✔
3407
            await this.hasSupportForPartitionedTables()
2,040✔
3408
        const isPartitionCondition = hasRelispartitionColumn
2,040✔
3409
            ? ` AND "cl"."relispartition" = 'f'`
2,040✔
3410
            : ""
9,308!
3411

9,308✔
3412
        const foreignKeysSql =
9,308✔
3413
            `SELECT "con"."conname" AS "constraint_name", "con"."nspname" AS "table_schema", "con"."relname" AS "table_name", "att2"."attname" AS "column_name", ` +
9,308✔
3414
            `"ns"."nspname" AS "referenced_table_schema", "cl"."relname" AS "referenced_table_name", "att"."attname" AS "referenced_column_name", "con"."confdeltype" AS "on_delete", ` +
9,308✔
3415
            `"con"."confupdtype" AS "on_update", "con"."condeferrable" AS "deferrable", "con"."condeferred" AS "deferred" ` +
9,308✔
3416
            `FROM ( ` +
9,308✔
3417
            `SELECT UNNEST ("con1"."conkey") AS "parent", UNNEST ("con1"."confkey") AS "child", "con1"."confrelid", "con1"."conrelid", "con1"."conname", "con1"."contype", "ns"."nspname", ` +
9,308✔
3418
            `"cl"."relname", "con1"."condeferrable", ` +
9,308✔
3419
            `CASE WHEN "con1"."condeferred" THEN 'INITIALLY DEFERRED' ELSE 'INITIALLY IMMEDIATE' END as condeferred, ` +
9,308✔
3420
            `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,308✔
3421
            `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,308✔
3422
            `FROM "pg_class" "cl" ` +
9,308✔
3423
            `INNER JOIN "pg_namespace" "ns" ON "cl"."relnamespace" = "ns"."oid" ` +
9,308✔
3424
            `INNER JOIN "pg_constraint" "con1" ON "con1"."conrelid" = "cl"."oid" ` +
9,308✔
3425
            `WHERE "con1"."contype" = 'f' AND (${foreignKeysCondition}) ` +
9,308✔
3426
            `) "con" ` +
9,308✔
3427
            `INNER JOIN "pg_attribute" "att" ON "att"."attrelid" = "con"."confrelid" AND "att"."attnum" = "con"."child" ` +
9,308✔
3428
            `INNER JOIN "pg_class" "cl" ON "cl"."oid" = "con"."confrelid" ${isPartitionCondition}` +
9,308✔
3429
            `INNER JOIN "pg_namespace" "ns" ON "cl"."relnamespace" = "ns"."oid" ` +
9,308✔
3430
            `INNER JOIN "pg_attribute" "att2" ON "att2"."attrelid" = "con"."conrelid" AND "att2"."attnum" = "con"."parent"`
9,308✔
3431

9,308✔
3432
        const [
9,308✔
3433
            dbColumns,
9,308✔
3434
            dbConstraints,
9,308✔
3435
            dbIndices,
9,308✔
3436
            dbForeignKeys,
9,308✔
3437
        ]: ObjectLiteral[][] = await Promise.all([
9,308✔
3438
            this.query(columnsSql),
9,308✔
3439
            this.query(constraintsSql),
9,308✔
3440
            this.query(indicesSql),
9,308✔
3441
            this.query(foreignKeysSql),
9,308✔
3442
        ])
9,308✔
3443

2,040✔
3444
        // create tables for loaded tables
2,040✔
3445
        return Promise.all(
2,040✔
3446
            dbTables.map(async (dbTable) => {
2,040✔
3447
                const table = new Table()
2,860✔
3448

2,860✔
3449
                const getSchemaFromKey = (dbObject: any, key: string) => {
2,860✔
3450
                    return dbObject[key] === currentSchema &&
3,756✔
3451
                        (!this.driver.options.schema ||
3,472✔
3452
                            this.driver.options.schema === currentSchema)
3,472✔
3453
                        ? undefined
3,756✔
3454
                        : dbObject[key]
3,756✔
3455
                }
3,756✔
3456
                // We do not need to join schema name, when database is by default.
2,860✔
3457
                const schema = getSchemaFromKey(dbTable, "table_schema")
2,860✔
3458
                table.database = currentDatabase
2,860✔
3459
                table.schema = dbTable["table_schema"]
2,860✔
3460
                table.comment = dbTable["table_comment"]
2,860✔
3461
                table.name = this.driver.buildTableName(
2,860✔
3462
                    dbTable["table_name"],
2,860✔
3463
                    schema,
2,860✔
3464
                )
2,860✔
3465

2,860✔
3466
                // create columns from the loaded columns
2,860✔
3467
                table.columns = await Promise.all(
2,860✔
3468
                    dbColumns
2,860✔
3469
                        .filter(
2,860✔
3470
                            (dbColumn) =>
2,860✔
3471
                                dbColumn["table_name"] ===
27,464✔
3472
                                    dbTable["table_name"] &&
27,464✔
3473
                                dbColumn["table_schema"] ===
10,456✔
3474
                                    dbTable["table_schema"],
2,860✔
3475
                        )
2,860✔
3476
                        .map(async (dbColumn) => {
2,860✔
3477
                            const columnConstraints = dbConstraints.filter(
10,456✔
3478
                                (dbConstraint) => {
10,456✔
3479
                                    return (
60,444✔
3480
                                        dbConstraint["table_name"] ===
60,444✔
3481
                                            dbColumn["table_name"] &&
60,444✔
3482
                                        dbConstraint["table_schema"] ===
25,532✔
3483
                                            dbColumn["table_schema"] &&
60,444✔
3484
                                        dbConstraint["column_name"] ===
25,532✔
3485
                                            dbColumn["column_name"]
25,532✔
3486
                                    )
60,444✔
3487
                                },
10,456✔
3488
                            )
10,456✔
3489

10,456✔
3490
                            const tableColumn = new TableColumn()
10,456✔
3491
                            tableColumn.name = dbColumn["column_name"]
10,456✔
3492
                            tableColumn.type = dbColumn["regtype"].toLowerCase()
10,456✔
3493

10,456✔
3494
                            if (
10,456✔
3495
                                tableColumn.type === "vector" ||
10,456✔
3496
                                tableColumn.type === "halfvec"
10,440✔
3497
                            ) {
10,456✔
3498
                                const lengthMatch = dbColumn[
32✔
3499
                                    "format_type"
32✔
3500
                                ].match(/^(?:vector|halfvec)\((\d+)\)$/)
32✔
3501
                                if (lengthMatch && lengthMatch[1]) {
32✔
3502
                                    tableColumn.length = lengthMatch[1]
16✔
3503
                                }
16✔
3504
                            }
32✔
3505

10,456✔
3506
                            if (
10,456✔
3507
                                tableColumn.type === "numeric" ||
10,456✔
3508
                                tableColumn.type === "numeric[]" ||
10,456✔
3509
                                tableColumn.type === "decimal" ||
10,456✔
3510
                                tableColumn.type === "float"
10,412✔
3511
                            ) {
10,456✔
3512
                                let numericPrecision =
44✔
3513
                                    dbColumn["numeric_precision"]
44✔
3514
                                let numericScale = dbColumn["numeric_scale"]
44✔
3515
                                if (dbColumn["data_type"] === "ARRAY") {
44✔
3516
                                    const numericSize = dbColumn[
4✔
3517
                                        "format_type"
4✔
3518
                                    ].match(
4✔
3519
                                        /^numeric\(([0-9]+),([0-9]+)\)\[\]$/,
4✔
3520
                                    )
4✔
3521
                                    if (numericSize) {
4✔
3522
                                        numericPrecision = +numericSize[1]
4✔
3523
                                        numericScale = +numericSize[2]
4✔
3524
                                    }
4✔
3525
                                }
4✔
3526
                                // If one of these properties was set, and another was not, Postgres sets '0' in to unspecified property
44✔
3527
                                // we set 'undefined' in to unspecified property to avoid changing column on sync
44✔
3528
                                if (
44✔
3529
                                    numericPrecision !== null &&
44✔
3530
                                    !this.isDefaultColumnPrecision(
28✔
3531
                                        table,
28✔
3532
                                        tableColumn,
28✔
3533
                                        numericPrecision,
28✔
3534
                                    )
44✔
3535
                                ) {
44✔
3536
                                    tableColumn.precision = numericPrecision
28✔
3537
                                } else if (
44✔
3538
                                    numericScale !== null &&
16!
3539
                                    !this.isDefaultColumnScale(
×
3540
                                        table,
×
3541
                                        tableColumn,
×
3542
                                        numericScale,
×
3543
                                    )
16✔
3544
                                ) {
16!
3545
                                    tableColumn.precision = undefined
×
3546
                                }
×
3547
                                if (
44✔
3548
                                    numericScale !== null &&
44✔
3549
                                    !this.isDefaultColumnScale(
28✔
3550
                                        table,
28✔
3551
                                        tableColumn,
28✔
3552
                                        numericScale,
28✔
3553
                                    )
44✔
3554
                                ) {
44✔
3555
                                    tableColumn.scale = numericScale
28✔
3556
                                } else if (
44✔
3557
                                    numericPrecision !== null &&
16!
3558
                                    !this.isDefaultColumnPrecision(
×
3559
                                        table,
×
3560
                                        tableColumn,
×
3561
                                        numericPrecision,
×
3562
                                    )
16✔
3563
                                ) {
16!
3564
                                    tableColumn.scale = undefined
×
3565
                                }
×
3566
                            }
44✔
3567

10,456✔
3568
                            if (
10,456✔
3569
                                tableColumn.type === "interval" ||
10,456✔
3570
                                tableColumn.type === "time without time zone" ||
10,456✔
3571
                                tableColumn.type === "time with time zone" ||
10,456✔
3572
                                tableColumn.type ===
10,360✔
3573
                                    "timestamp without time zone" ||
10,456✔
3574
                                tableColumn.type === "timestamp with time zone"
10,228✔
3575
                            ) {
10,456✔
3576
                                tableColumn.precision =
248✔
3577
                                    !this.isDefaultColumnPrecision(
248✔
3578
                                        table,
248✔
3579
                                        tableColumn,
248✔
3580
                                        dbColumn["datetime_precision"],
248✔
3581
                                    )
248✔
3582
                                        ? dbColumn["datetime_precision"]
248✔
3583
                                        : undefined
248✔
3584
                            }
248✔
3585

10,456✔
3586
                            // check if column has user-defined data type.
10,456✔
3587
                            // NOTE: if ENUM type defined with "array:true" it comes with ARRAY type instead of USER-DEFINED
10,456✔
3588
                            if (
10,456✔
3589
                                dbColumn["data_type"] === "USER-DEFINED" ||
10,456✔
3590
                                dbColumn["data_type"] === "ARRAY"
9,812✔
3591
                            ) {
10,456✔
3592
                                const { name } =
924✔
3593
                                    await this.getUserDefinedTypeName(
924✔
3594
                                        table,
924✔
3595
                                        tableColumn,
924✔
3596
                                    )
924✔
3597

924✔
3598
                                // check if `enumName` is specified by user
924✔
3599
                                const builtEnumName = this.buildEnumName(
924✔
3600
                                    table,
924✔
3601
                                    tableColumn,
924✔
3602
                                    false,
924✔
3603
                                    true,
924✔
3604
                                )
924✔
3605
                                const enumName =
924✔
3606
                                    builtEnumName !== name ? name : undefined
924✔
3607

924✔
3608
                                // check if type is ENUM
924✔
3609
                                const sql =
924✔
3610
                                    `SELECT "e"."enumlabel" AS "value" FROM "pg_enum" "e" ` +
924✔
3611
                                    `INNER JOIN "pg_type" "t" ON "t"."oid" = "e"."enumtypid" ` +
924✔
3612
                                    `INNER JOIN "pg_namespace" "n" ON "n"."oid" = "t"."typnamespace" ` +
924✔
3613
                                    `WHERE "n"."nspname" = '${
924✔
3614
                                        dbTable["table_schema"]
924✔
3615
                                    }' AND "t"."typname" = '${
924✔
3616
                                        enumName || name
924✔
3617
                                    }'`
924✔
3618
                                const results: ObjectLiteral[] =
924✔
3619
                                    await this.query(sql)
924✔
3620

924✔
3621
                                if (results.length) {
924✔
3622
                                    tableColumn.type = "enum"
772✔
3623
                                    tableColumn.enum = results.map(
772✔
3624
                                        (result) => result["value"],
772✔
3625
                                    )
772✔
3626
                                    tableColumn.enumName = enumName
772✔
3627
                                }
772✔
3628

924✔
3629
                                if (dbColumn["data_type"] === "ARRAY") {
924✔
3630
                                    tableColumn.isArray = true
280✔
3631
                                    const type = tableColumn.type.replace(
280✔
3632
                                        "[]",
280✔
3633
                                        "",
280✔
3634
                                    )
280✔
3635
                                    tableColumn.type =
280✔
3636
                                        this.connection.driver.normalizeType({
280✔
3637
                                            type: type,
280✔
3638
                                        })
280✔
3639
                                }
280✔
3640
                            }
924✔
3641

10,456✔
3642
                            if (
10,456✔
3643
                                tableColumn.type === "geometry" ||
10,456✔
3644
                                tableColumn.type === "geography"
10,416✔
3645
                            ) {
10,456✔
3646
                                const sql =
52✔
3647
                                    `SELECT * FROM (` +
52✔
3648
                                    `SELECT "f_table_schema" "table_schema", "f_table_name" "table_name", ` +
52✔
3649
                                    `"f_${tableColumn.type}_column" "column_name", "srid", "type" ` +
52✔
3650
                                    `FROM "${tableColumn.type}_columns"` +
52✔
3651
                                    `) AS _ ` +
52✔
3652
                                    `WHERE "column_name" = '${dbColumn["column_name"]}' AND ` +
52✔
3653
                                    `"table_schema" = '${dbColumn["table_schema"]}' AND ` +
52✔
3654
                                    `"table_name" = '${dbColumn["table_name"]}'`
52✔
3655

52✔
3656
                                const results: ObjectLiteral[] =
52✔
3657
                                    await this.query(sql)
52✔
3658

52✔
3659
                                if (results.length > 0) {
52✔
3660
                                    tableColumn.spatialFeatureType =
52✔
3661
                                        results[0].type
52✔
3662
                                    tableColumn.srid = results[0].srid
52✔
3663
                                }
52✔
3664
                            }
52✔
3665

10,456✔
3666
                            // check only columns that have length property
10,456✔
3667
                            if (
10,456✔
3668
                                this.driver.withLengthColumnTypes.indexOf(
10,456✔
3669
                                    tableColumn.type as ColumnType,
10,456✔
3670
                                ) !== -1
10,456✔
3671
                            ) {
10,456✔
3672
                                let length
4,728✔
3673
                                if (tableColumn.isArray) {
4,728✔
3674
                                    const match = /\((\d+)\)/.exec(
32✔
3675
                                        dbColumn["format_type"],
32✔
3676
                                    )
32✔
3677
                                    length = match ? match[1] : undefined
32✔
3678
                                } else if (
4,728✔
3679
                                    dbColumn["character_maximum_length"]
4,696✔
3680
                                ) {
4,696✔
3681
                                    length =
560✔
3682
                                        dbColumn[
560✔
3683
                                            "character_maximum_length"
560✔
3684
                                        ].toString()
560✔
3685
                                }
560✔
3686
                                if (length) {
4,728✔
3687
                                    tableColumn.length =
564✔
3688
                                        !this.isDefaultColumnLength(
564✔
3689
                                            table,
564✔
3690
                                            tableColumn,
564✔
3691
                                            length,
564✔
3692
                                        )
564✔
3693
                                            ? length
564✔
3694
                                            : ""
564✔
3695
                                }
564✔
3696
                            }
4,728✔
3697
                            tableColumn.isNullable =
10,456✔
3698
                                dbColumn["is_nullable"] === "YES"
10,456✔
3699

10,456✔
3700
                            const primaryConstraint = columnConstraints.find(
10,456✔
3701
                                (constraint) =>
10,456✔
3702
                                    constraint["constraint_type"] === "PRIMARY",
10,456✔
3703
                            )
10,456✔
3704
                            if (primaryConstraint) {
10,456✔
3705
                                tableColumn.isPrimary = true
2,968✔
3706
                                // find another columns involved in primary key constraint
2,968✔
3707
                                const anotherPrimaryConstraints =
2,968✔
3708
                                    dbConstraints.filter(
2,968✔
3709
                                        (constraint) =>
2,968✔
3710
                                            constraint["table_name"] ===
18,008✔
3711
                                                dbColumn["table_name"] &&
18,008✔
3712
                                            constraint["table_schema"] ===
6,592✔
3713
                                                dbColumn["table_schema"] &&
18,008✔
3714
                                            constraint["column_name"] !==
6,592✔
3715
                                                dbColumn["column_name"] &&
18,008✔
3716
                                            constraint["constraint_type"] ===
3,436✔
3717
                                                "PRIMARY",
2,968✔
3718
                                    )
2,968✔
3719

2,968✔
3720
                                // collect all column names
2,968✔
3721
                                const columnNames =
2,968✔
3722
                                    anotherPrimaryConstraints.map(
2,968✔
3723
                                        (constraint) =>
2,968✔
3724
                                            constraint["column_name"],
2,968✔
3725
                                    )
2,968✔
3726
                                columnNames.push(dbColumn["column_name"])
2,968✔
3727

2,968✔
3728
                                // build default primary key constraint name
2,968✔
3729
                                const pkName =
2,968✔
3730
                                    this.connection.namingStrategy.primaryKeyName(
2,968✔
3731
                                        table,
2,968✔
3732
                                        columnNames,
2,968✔
3733
                                    )
2,968✔
3734

2,968✔
3735
                                // if primary key has user-defined constraint name, write it in table column
2,968✔
3736
                                if (
2,968✔
3737
                                    primaryConstraint["constraint_name"] !==
2,968✔
3738
                                    pkName
2,968✔
3739
                                ) {
2,968✔
3740
                                    tableColumn.primaryKeyConstraintName =
112✔
3741
                                        primaryConstraint["constraint_name"]
112✔
3742
                                }
112✔
3743
                            }
2,968✔
3744

10,456✔
3745
                            const uniqueConstraints = columnConstraints.filter(
10,456✔
3746
                                (constraint) =>
10,456✔
3747
                                    constraint["constraint_type"] === "UNIQUE",
10,456✔
3748
                            )
10,456✔
3749
                            const isConstraintComposite =
10,456✔
3750
                                uniqueConstraints.every((uniqueConstraint) => {
10,456✔
3751
                                    return dbConstraints.some(
1,486✔
3752
                                        (dbConstraint) =>
1,486✔
3753
                                            dbConstraint["constraint_type"] ===
9,547✔
3754
                                                "UNIQUE" &&
9,547✔
3755
                                            dbConstraint["constraint_name"] ===
3,700✔
3756
                                                uniqueConstraint[
3,700✔
3757
                                                    "constraint_name"
3,700✔
3758
                                                ] &&
9,547✔
3759
                                            dbConstraint["column_name"] !==
1,913✔
3760
                                                dbColumn["column_name"],
1,486✔
3761
                                    )
1,486✔
3762
                                })
10,456✔
3763
                            tableColumn.isUnique =
10,456✔
3764
                                uniqueConstraints.length > 0 &&
10,456✔
3765
                                !isConstraintComposite
1,476✔
3766

10,456✔
3767
                            if (dbColumn.is_identity === "YES") {
10,456!
3768
                                // Postgres 10+ Identity column
×
3769
                                tableColumn.isGenerated = true
×
3770
                                tableColumn.generationStrategy = "identity"
×
3771
                                tableColumn.generatedIdentity =
×
3772
                                    dbColumn.identity_generation
×
3773
                            } else if (
10,456✔
3774
                                dbColumn["column_default"] !== null &&
10,456✔
3775
                                dbColumn["column_default"] !== undefined
2,684✔
3776
                            ) {
10,456✔
3777
                                const serialDefaultName = `nextval('${this.buildSequenceName(
2,684✔
3778
                                    table,
2,684✔
3779
                                    dbColumn["column_name"],
2,684✔
3780
                                )}'::regclass)`
2,684✔
3781
                                const serialDefaultPath = `nextval('${this.buildSequencePath(
2,684✔
3782
                                    table,
2,684✔
3783
                                    dbColumn["column_name"],
2,684✔
3784
                                )}'::regclass)`
2,684✔
3785

2,684✔
3786
                                const defaultWithoutQuotes = dbColumn[
2,684✔
3787
                                    "column_default"
2,684✔
3788
                                ].replace(/"/g, "")
2,684✔
3789

2,684✔
3790
                                if (
2,684✔
3791
                                    defaultWithoutQuotes ===
2,684✔
3792
                                        serialDefaultName ||
2,684✔
3793
                                    defaultWithoutQuotes === serialDefaultPath
1,132✔
3794
                                ) {
2,684✔
3795
                                    tableColumn.isGenerated = true
1,732✔
3796
                                    tableColumn.generationStrategy = "increment"
1,732✔
3797
                                } else if (
2,684✔
3798
                                    dbColumn["column_default"] ===
952✔
3799
                                        "gen_random_uuid()" ||
952✔
3800
                                    /^uuid_generate_v\d\(\)/.test(
928✔
3801
                                        dbColumn["column_default"],
928✔
3802
                                    )
952✔
3803
                                ) {
952✔
3804
                                    if (tableColumn.type === "uuid") {
120✔
3805
                                        tableColumn.isGenerated = true
112✔
3806
                                        tableColumn.generationStrategy = "uuid"
112✔
3807
                                    } else {
120✔
3808
                                        tableColumn.default =
8✔
3809
                                            dbColumn["column_default"]
8✔
3810
                                    }
8✔
3811
                                } else if (
952✔
3812
                                    dbColumn["column_default"] === "now()" ||
832✔
3813
                                    dbColumn["column_default"].indexOf(
768✔
3814
                                        "'now'::text",
768✔
3815
                                    ) !== -1
768✔
3816
                                ) {
832✔
3817
                                    tableColumn.default =
208✔
3818
                                        dbColumn["column_default"]
208✔
3819
                                } else {
832✔
3820
                                    tableColumn.default = dbColumn[
624✔
3821
                                        "column_default"
624✔
3822
                                    ].replace(/::[\w\s.[\]\-"]+/g, "")
624✔
3823
                                    tableColumn.default =
624✔
3824
                                        tableColumn.default.replace(
624✔
3825
                                            /^(-?\d+)$/,
624✔
3826
                                            "'$1'",
624✔
3827
                                        )
624✔
3828
                                }
624✔
3829
                            }
2,684✔
3830

10,456✔
3831
                            if (
10,456✔
3832
                                dbColumn["is_generated"] === "ALWAYS" &&
10,456✔
3833
                                dbColumn["generation_expression"]
168✔
3834
                            ) {
10,456✔
3835
                                // In postgres there is no VIRTUAL generated column type
168✔
3836
                                tableColumn.generatedType = "STORED"
168✔
3837
                                // We cannot relay on information_schema.columns.generation_expression, because it is formatted different.
168✔
3838
                                const asExpressionQuery =
168✔
3839
                                    this.selectTypeormMetadataSql({
168✔
3840
                                        database: currentDatabase,
168✔
3841
                                        schema: dbTable["table_schema"],
168✔
3842
                                        table: dbTable["table_name"],
168✔
3843
                                        type: MetadataTableType.GENERATED_COLUMN,
168✔
3844
                                        name: tableColumn.name,
168✔
3845
                                    })
168✔
3846

168✔
3847
                                const results = await this.query(
168✔
3848
                                    asExpressionQuery.query,
168✔
3849
                                    asExpressionQuery.parameters,
168✔
3850
                                )
168✔
3851
                                if (results[0] && results[0].value) {
168✔
3852
                                    tableColumn.asExpression = results[0].value
168✔
3853
                                } else {
168!
3854
                                    tableColumn.asExpression = ""
×
3855
                                }
×
3856
                            }
168✔
3857

10,456✔
3858
                            tableColumn.comment = dbColumn["description"]
10,456✔
3859
                                ? dbColumn["description"]
10,456✔
3860
                                : undefined
10,456✔
3861
                            if (dbColumn["character_set_name"])
10,456✔
3862
                                tableColumn.charset =
10,456!
3863
                                    dbColumn["character_set_name"]
×
3864
                            if (dbColumn["collation_name"])
10,456✔
3865
                                tableColumn.collation =
10,456✔
3866
                                    dbColumn["collation_name"]
20✔
3867
                            return tableColumn
10,456✔
3868
                        }),
2,860✔
3869
                )
2,860✔
3870

2,860✔
3871
                // find unique constraints of table, group them by constraint name and build TableUnique.
2,860✔
3872
                const tableUniqueConstraints = OrmUtils.uniq(
2,860✔
3873
                    dbConstraints.filter((dbConstraint) => {
2,860✔
3874
                        return (
17,460✔
3875
                            dbConstraint["table_name"] ===
17,460✔
3876
                                dbTable["table_name"] &&
17,460✔
3877
                            dbConstraint["table_schema"] ===
6,124✔
3878
                                dbTable["table_schema"] &&
17,460✔
3879
                            dbConstraint["constraint_type"] === "UNIQUE"
6,124✔
3880
                        )
17,460✔
3881
                    }),
2,860✔
3882
                    (dbConstraint) => dbConstraint["constraint_name"],
2,860✔
3883
                )
2,860✔
3884

2,860✔
3885
                table.uniques = tableUniqueConstraints.map((constraint) => {
2,860✔
3886
                    const uniques = dbConstraints.filter(
1,060✔
3887
                        (dbC) =>
1,060✔
3888
                            dbC["constraint_name"] ===
7,936✔
3889
                            constraint["constraint_name"],
1,060✔
3890
                    )
1,060✔
3891
                    return new TableUnique({
1,060✔
3892
                        name: constraint["constraint_name"],
1,060✔
3893
                        columnNames: uniques.map((u) => u["column_name"]),
1,060✔
3894
                        deferrable: constraint["deferrable"]
1,060✔
3895
                            ? constraint["deferred"]
1,060!
3896
                            : undefined,
1,060✔
3897
                    })
1,060✔
3898
                })
2,860✔
3899

2,860✔
3900
                // find check constraints of table, group them by constraint name and build TableCheck.
2,860✔
3901
                const tableCheckConstraints = OrmUtils.uniq(
2,860✔
3902
                    dbConstraints.filter((dbConstraint) => {
2,860✔
3903
                        return (
17,460✔
3904
                            dbConstraint["table_name"] ===
17,460✔
3905
                                dbTable["table_name"] &&
17,460✔
3906
                            dbConstraint["table_schema"] ===
6,124✔
3907
                                dbTable["table_schema"] &&
17,460✔
3908
                            dbConstraint["constraint_type"] === "CHECK"
6,124✔
3909
                        )
17,460✔
3910
                    }),
2,860✔
3911
                    (dbConstraint) => dbConstraint["constraint_name"],
2,860✔
3912
                )
2,860✔
3913

2,860✔
3914
                table.checks = tableCheckConstraints.map((constraint) => {
2,860✔
3915
                    const checks = dbConstraints.filter(
416✔
3916
                        (dbC) =>
416✔
3917
                            dbC["constraint_name"] ===
3,584✔
3918
                            constraint["constraint_name"],
416✔
3919
                    )
416✔
3920
                    return new TableCheck({
416✔
3921
                        name: constraint["constraint_name"],
416✔
3922
                        columnNames: checks.map((c) => c["column_name"]),
416✔
3923
                        expression: constraint["expression"].replace(
416✔
3924
                            /^\s*CHECK\s*\((.*)\)\s*$/i,
416✔
3925
                            "$1",
416✔
3926
                        ),
416✔
3927
                    })
416✔
3928
                })
2,860✔
3929

2,860✔
3930
                // find exclusion constraints of table, group them by constraint name and build TableExclusion.
2,860✔
3931
                const tableExclusionConstraints = OrmUtils.uniq(
2,860✔
3932
                    dbConstraints.filter((dbConstraint) => {
2,860✔
3933
                        return (
17,460✔
3934
                            dbConstraint["table_name"] ===
17,460✔
3935
                                dbTable["table_name"] &&
17,460✔
3936
                            dbConstraint["table_schema"] ===
6,124✔
3937
                                dbTable["table_schema"] &&
17,460✔
3938
                            dbConstraint["constraint_type"] === "EXCLUDE"
6,124✔
3939
                        )
17,460✔
3940
                    }),
2,860✔
3941
                    (dbConstraint) => dbConstraint["constraint_name"],
2,860✔
3942
                )
2,860✔
3943

2,860✔
3944
                table.exclusions = tableExclusionConstraints.map(
2,860✔
3945
                    (constraint) => {
2,860✔
3946
                        return new TableExclusion({
324✔
3947
                            name: constraint["constraint_name"],
324✔
3948
                            expression: constraint["expression"].substring(8), // trim EXCLUDE from start of expression
324✔
3949
                        })
324✔
3950
                    },
2,860✔
3951
                )
2,860✔
3952

2,860✔
3953
                // find foreign key constraints of table, group them by constraint name and build TableForeignKey.
2,860✔
3954
                const tableForeignKeyConstraints = OrmUtils.uniq(
2,860✔
3955
                    dbForeignKeys.filter((dbForeignKey) => {
2,860✔
3956
                        return (
3,160✔
3957
                            dbForeignKey["table_name"] ===
3,160✔
3958
                                dbTable["table_name"] &&
3,160✔
3959
                            dbForeignKey["table_schema"] ===
912✔
3960
                                dbTable["table_schema"]
912✔
3961
                        )
3,160✔
3962
                    }),
2,860✔
3963
                    (dbForeignKey) => dbForeignKey["constraint_name"],
2,860✔
3964
                )
2,860✔
3965

2,860✔
3966
                table.foreignKeys = tableForeignKeyConstraints.map(
2,860✔
3967
                    (dbForeignKey) => {
2,860✔
3968
                        const foreignKeys = dbForeignKeys.filter(
896✔
3969
                            (dbFk) =>
896✔
3970
                                dbFk["constraint_name"] ===
2,280✔
3971
                                dbForeignKey["constraint_name"],
896✔
3972
                        )
896✔
3973

896✔
3974
                        // if referenced table located in currently used schema, we don't need to concat schema name to table name.
896✔
3975
                        const schema = getSchemaFromKey(
896✔
3976
                            dbForeignKey,
896✔
3977
                            "referenced_table_schema",
896✔
3978
                        )
896✔
3979
                        const referencedTableName = this.driver.buildTableName(
896✔
3980
                            dbForeignKey["referenced_table_name"],
896✔
3981
                            schema,
896✔
3982
                        )
896✔
3983

896✔
3984
                        return new TableForeignKey({
896✔
3985
                            name: dbForeignKey["constraint_name"],
896✔
3986
                            columnNames: foreignKeys.map(
896✔
3987
                                (dbFk) => dbFk["column_name"],
896✔
3988
                            ),
896✔
3989
                            referencedSchema:
896✔
3990
                                dbForeignKey["referenced_table_schema"],
896✔
3991
                            referencedTableName: referencedTableName,
896✔
3992
                            referencedColumnNames: foreignKeys.map(
896✔
3993
                                (dbFk) => dbFk["referenced_column_name"],
896✔
3994
                            ),
896✔
3995
                            onDelete: dbForeignKey["on_delete"],
896✔
3996
                            onUpdate: dbForeignKey["on_update"],
896✔
3997
                            deferrable: dbForeignKey["deferrable"]
896✔
3998
                                ? dbForeignKey["deferred"]
896!
3999
                                : undefined,
896✔
4000
                        })
896✔
4001
                    },
2,860✔
4002
                )
2,860✔
4003

2,860✔
4004
                // find index constraints of table, group them by constraint name and build TableIndex.
2,860✔
4005
                const tableIndexConstraints = OrmUtils.uniq(
2,860✔
4006
                    dbIndices.filter((dbIndex) => {
2,860✔
4007
                        return (
1,560✔
4008
                            dbIndex["table_name"] === dbTable["table_name"] &&
1,560✔
4009
                            dbIndex["table_schema"] === dbTable["table_schema"]
720✔
4010
                        )
1,560✔
4011
                    }),
2,860✔
4012
                    (dbIndex) => dbIndex["constraint_name"],
2,860✔
4013
                )
2,860✔
4014

2,860✔
4015
                table.indices = tableIndexConstraints.map((constraint) => {
2,860✔
4016
                    const indices = dbIndices.filter((index) => {
652✔
4017
                        return (
1,400✔
4018
                            index["table_schema"] ===
1,400✔
4019
                                constraint["table_schema"] &&
1,400✔
4020
                            index["table_name"] === constraint["table_name"] &&
1,400✔
4021
                            index["constraint_name"] ===
1,384✔
4022
                                constraint["constraint_name"]
1,384✔
4023
                        )
1,400✔
4024
                    })
652✔
4025
                    return new TableIndex(<TableIndexOptions>{
652✔
4026
                        table: table,
652✔
4027
                        name: constraint["constraint_name"],
652✔
4028
                        columnNames: indices.map((i) => i["column_name"]),
652✔
4029
                        isUnique: constraint["is_unique"] === "TRUE",
652✔
4030
                        where: constraint["condition"],
652✔
4031
                        isSpatial: constraint["index_type"] === "gist",
652✔
4032
                        isFulltext: false,
652✔
4033
                    })
652✔
4034
                })
2,860✔
4035

2,860✔
4036
                return table
2,860✔
4037
            }),
2,040✔
4038
        )
2,040✔
4039
    }
2,040✔
4040

26✔
4041
    /**
26✔
4042
     * Builds create table sql.
26✔
4043
     */
26✔
4044
    protected createTableSql(table: Table, createForeignKeys?: boolean): Query {
26✔
4045
        const columnDefinitions = table.columns
22,584✔
4046
            .map((column) => this.buildCreateColumnSql(table, column))
22,584✔
4047
            .join(", ")
22,584✔
4048
        let sql = `CREATE TABLE ${this.escapePath(table)} (${columnDefinitions}`
22,584✔
4049

22,584✔
4050
        table.columns
22,584✔
4051
            .filter((column) => column.isUnique)
22,584✔
4052
            .forEach((column) => {
22,584✔
4053
                const isUniqueExist = table.uniques.some(
3,116✔
4054
                    (unique) =>
3,116✔
4055
                        unique.columnNames.length === 1 &&
4,148✔
4056
                        unique.columnNames[0] === column.name,
3,116✔
4057
                )
3,116✔
4058
                if (!isUniqueExist)
3,116✔
4059
                    table.uniques.push(
3,116✔
4060
                        new TableUnique({
16✔
4061
                            name: this.connection.namingStrategy.uniqueConstraintName(
16✔
4062
                                table,
16✔
4063
                                [column.name],
16✔
4064
                            ),
16✔
4065
                            columnNames: [column.name],
16✔
4066
                        }),
16✔
4067
                    )
16✔
4068
            })
22,584✔
4069

22,584✔
4070
        if (table.uniques.length > 0) {
22,584✔
4071
            const uniquesSql = table.uniques
2,692✔
4072
                .map((unique) => {
2,692✔
4073
                    const uniqueName = unique.name
3,956✔
4074
                        ? unique.name
3,956✔
4075
                        : this.connection.namingStrategy.uniqueConstraintName(
3,956✔
4076
                              table,
8✔
4077
                              unique.columnNames,
8✔
4078
                          )
3,956✔
4079
                    const columnNames = unique.columnNames
3,956✔
4080
                        .map((columnName) => `"${columnName}"`)
3,956✔
4081
                        .join(", ")
3,956✔
4082
                    let constraint = `CONSTRAINT "${uniqueName}" UNIQUE (${columnNames})`
3,956✔
4083
                    if (unique.deferrable)
3,956✔
4084
                        constraint += ` DEFERRABLE ${unique.deferrable}`
3,956✔
4085
                    return constraint
3,956✔
4086
                })
2,692✔
4087
                .join(", ")
2,692✔
4088

2,692✔
4089
            sql += `, ${uniquesSql}`
2,692✔
4090
        }
2,692✔
4091

22,584✔
4092
        if (table.checks.length > 0) {
22,584✔
4093
            const checksSql = table.checks
320✔
4094
                .map((check) => {
320✔
4095
                    const checkName = check.name
324✔
4096
                        ? check.name
324✔
4097
                        : this.connection.namingStrategy.checkConstraintName(
324✔
4098
                              table,
4✔
4099
                              check.expression!,
4✔
4100
                          )
324✔
4101
                    return `CONSTRAINT "${checkName}" CHECK (${check.expression})`
324✔
4102
                })
320✔
4103
                .join(", ")
320✔
4104

320✔
4105
            sql += `, ${checksSql}`
320✔
4106
        }
320✔
4107

22,584✔
4108
        if (table.exclusions.length > 0) {
22,584✔
4109
            const exclusionsSql = table.exclusions
316✔
4110
                .map((exclusion) => {
316✔
4111
                    const exclusionName = exclusion.name
316✔
4112
                        ? exclusion.name
316✔
4113
                        : this.connection.namingStrategy.exclusionConstraintName(
316!
4114
                              table,
×
4115
                              exclusion.expression!,
×
4116
                          )
316✔
4117
                    return `CONSTRAINT "${exclusionName}" EXCLUDE ${exclusion.expression}`
316✔
4118
                })
316✔
4119
                .join(", ")
316✔
4120

316✔
4121
            sql += `, ${exclusionsSql}`
316✔
4122
        }
316✔
4123

22,584✔
4124
        if (table.foreignKeys.length > 0 && createForeignKeys) {
22,584✔
4125
            const foreignKeysSql = table.foreignKeys
16✔
4126
                .map((fk) => {
16✔
4127
                    const columnNames = fk.columnNames
20✔
4128
                        .map((columnName) => `"${columnName}"`)
20✔
4129
                        .join(", ")
20✔
4130
                    if (!fk.name)
20✔
4131
                        fk.name = this.connection.namingStrategy.foreignKeyName(
20✔
4132
                            table,
12✔
4133
                            fk.columnNames,
12✔
4134
                            this.getTablePath(fk),
12✔
4135
                            fk.referencedColumnNames,
12✔
4136
                        )
12✔
4137

20✔
4138
                    const referencedColumnNames = fk.referencedColumnNames
20✔
4139
                        .map((columnName) => `"${columnName}"`)
20✔
4140
                        .join(", ")
20✔
4141

20✔
4142
                    let constraint = `CONSTRAINT "${
20✔
4143
                        fk.name
20✔
4144
                    }" FOREIGN KEY (${columnNames}) REFERENCES ${this.escapePath(
20✔
4145
                        this.getTablePath(fk),
20✔
4146
                    )} (${referencedColumnNames})`
20✔
4147
                    if (fk.onDelete) constraint += ` ON DELETE ${fk.onDelete}`
20✔
4148
                    if (fk.onUpdate) constraint += ` ON UPDATE ${fk.onUpdate}`
20✔
4149
                    if (fk.deferrable)
20✔
4150
                        constraint += ` DEFERRABLE ${fk.deferrable}`
20!
4151

20✔
4152
                    return constraint
20✔
4153
                })
16✔
4154
                .join(", ")
16✔
4155

16✔
4156
            sql += `, ${foreignKeysSql}`
16✔
4157
        }
16✔
4158

22,584✔
4159
        const primaryColumns = table.columns.filter(
22,584✔
4160
            (column) => column.isPrimary,
22,584✔
4161
        )
22,584✔
4162
        if (primaryColumns.length > 0) {
22,584✔
4163
            const primaryKeyName = primaryColumns[0].primaryKeyConstraintName
22,464✔
4164
                ? primaryColumns[0].primaryKeyConstraintName
22,464✔
4165
                : this.connection.namingStrategy.primaryKeyName(
22,464✔
4166
                      table,
22,400✔
4167
                      primaryColumns.map((column) => column.name),
22,400✔
4168
                  )
22,464✔
4169

22,464✔
4170
            const columnNames = primaryColumns
22,464✔
4171
                .map((column) => `"${column.name}"`)
22,464✔
4172
                .join(", ")
22,464✔
4173
            sql += `, CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNames})`
22,464✔
4174
        }
22,464✔
4175

22,584✔
4176
        sql += `)`
22,584✔
4177

22,584✔
4178
        table.columns
22,584✔
4179
            .filter((it) => it.comment)
22,584✔
4180
            .forEach(
22,584✔
4181
                (it) =>
22,584✔
4182
                    (sql += `; COMMENT ON COLUMN ${this.escapePath(table)}."${
248✔
4183
                        it.name
248✔
4184
                    }" IS ${this.escapeComment(it.comment)}`),
22,584✔
4185
            )
22,584✔
4186

22,584✔
4187
        return new Query(sql)
22,584✔
4188
    }
22,584✔
4189

26✔
4190
    /**
26✔
4191
     * Loads Postgres version.
26✔
4192
     */
26✔
4193
    async getVersion(): Promise<string> {
26✔
4194
        // we use `SELECT version()` instead of `SHOW server_version` or `SHOW server_version_num`
2,680✔
4195
        // to maintain compatability with Amazon Redshift.
2,680✔
4196
        //
2,680✔
4197
        // see:
2,680✔
4198
        //  - https://github.com/typeorm/typeorm/pull/9319
2,680✔
4199
        //  - https://docs.aws.amazon.com/redshift/latest/dg/c_unsupported-postgresql-functions.html
2,680✔
4200
        const result: [{ version: string }] = await this.query(
2,680✔
4201
            `SELECT version()`,
2,680✔
4202
        )
2,680✔
4203

2,680✔
4204
        // Examples:
2,680✔
4205
        // 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,680✔
4206
        // 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,680✔
4207
        return result[0].version.replace(/^PostgreSQL ([\d.]+).*$/, "$1")
2,680✔
4208
    }
2,680✔
4209

26✔
4210
    /**
26✔
4211
     * Builds drop table sql.
26✔
4212
     */
26✔
4213
    protected dropTableSql(tableOrPath: Table | string): Query {
26✔
4214
        return new Query(`DROP TABLE ${this.escapePath(tableOrPath)}`)
22,584✔
4215
    }
22,584✔
4216

26✔
4217
    protected createViewSql(view: View): Query {
26✔
4218
        const materializedClause = view.materialized ? "MATERIALIZED " : ""
200✔
4219
        const viewName = this.escapePath(view)
200✔
4220

200✔
4221
        if (typeof view.expression === "string") {
200✔
4222
            return new Query(
152✔
4223
                `CREATE ${materializedClause}VIEW ${viewName} AS ${view.expression}`,
152✔
4224
            )
152✔
4225
        } else {
200✔
4226
            return new Query(
48✔
4227
                `CREATE ${materializedClause}VIEW ${viewName} AS ${view
48✔
4228
                    .expression(this.connection)
48✔
4229
                    .getQuery()}`,
48✔
4230
            )
48✔
4231
        }
48✔
4232
    }
200✔
4233

26✔
4234
    protected async insertViewDefinitionSql(view: View): Promise<Query> {
26✔
4235
        const currentSchema = await this.getCurrentSchema()
196✔
4236

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

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

196✔
4243
        const type = view.materialized
196✔
4244
            ? MetadataTableType.MATERIALIZED_VIEW
196✔
4245
            : MetadataTableType.VIEW
196✔
4246
        const expression =
196✔
4247
            typeof view.expression === "string"
196✔
4248
                ? view.expression.trim()
196✔
4249
                : view.expression(this.connection).getQuery()
196✔
4250
        return this.insertTypeormMetadataSql({
196✔
4251
            type,
196✔
4252
            schema,
196✔
4253
            name,
196✔
4254
            value: expression,
196✔
4255
        })
196✔
4256
    }
196✔
4257

26✔
4258
    /**
26✔
4259
     * Builds drop view sql.
26✔
4260
     */
26✔
4261
    protected dropViewSql(view: View): Query {
26✔
4262
        const materializedClause = view.materialized ? "MATERIALIZED " : ""
200✔
4263
        return new Query(
200✔
4264
            `DROP ${materializedClause}VIEW ${this.escapePath(view)}`,
200✔
4265
        )
200✔
4266
    }
200✔
4267

26✔
4268
    /**
26✔
4269
     * Builds remove view sql.
26✔
4270
     */
26✔
4271
    protected async deleteViewDefinitionSql(view: View): Promise<Query> {
26✔
4272
        const currentSchema = await this.getCurrentSchema()
196✔
4273

196✔
4274
        let { schema, tableName: name } = this.driver.parseTableName(view)
196✔
4275

196✔
4276
        if (!schema) {
196!
4277
            schema = currentSchema
×
4278
        }
×
4279

196✔
4280
        const type = view.materialized
196✔
4281
            ? MetadataTableType.MATERIALIZED_VIEW
196✔
4282
            : MetadataTableType.VIEW
196✔
4283
        return this.deleteTypeormMetadataSql({ type, schema, name })
196✔
4284
    }
196✔
4285

26✔
4286
    /**
26✔
4287
     * Drops ENUM type from given schemas.
26✔
4288
     */
26✔
4289
    protected async dropEnumTypes(schemaNames: string): Promise<void> {
26✔
4290
        const selectDropsQuery =
7,140✔
4291
            `SELECT 'DROP TYPE IF EXISTS "' || n.nspname || '"."' || t.typname || '" CASCADE;' as "query" FROM "pg_type" "t" ` +
7,140✔
4292
            `INNER JOIN "pg_enum" "e" ON "e"."enumtypid" = "t"."oid" ` +
7,140✔
4293
            `INNER JOIN "pg_namespace" "n" ON "n"."oid" = "t"."typnamespace" ` +
7,140✔
4294
            `WHERE "n"."nspname" IN (${schemaNames}) GROUP BY "n"."nspname", "t"."typname"`
7,140✔
4295
        const dropQueries: ObjectLiteral[] = await this.query(selectDropsQuery)
7,140✔
4296
        await Promise.all(dropQueries.map((q) => this.query(q["query"])))
7,140✔
4297
    }
7,140✔
4298

26✔
4299
    /**
26✔
4300
     * Checks if enum with the given name exist in the database.
26✔
4301
     */
26✔
4302
    protected async hasEnumType(
26✔
4303
        table: Table,
1,040✔
4304
        column: TableColumn,
1,040✔
4305
    ): Promise<boolean> {
1,040✔
4306
        let { schema } = this.driver.parseTableName(table)
1,040✔
4307

1,040✔
4308
        if (!schema) {
1,040!
4309
            schema = await this.getCurrentSchema()
×
4310
        }
×
4311

1,040✔
4312
        const enumName = this.buildEnumName(table, column, false, true)
1,040✔
4313
        const sql =
1,040✔
4314
            `SELECT "n"."nspname", "t"."typname" FROM "pg_type" "t" ` +
1,040✔
4315
            `INNER JOIN "pg_namespace" "n" ON "n"."oid" = "t"."typnamespace" ` +
1,040✔
4316
            `WHERE "n"."nspname" = '${schema}' AND "t"."typname" = '${enumName}'`
1,040✔
4317
        const result = await this.query(sql)
1,040✔
4318
        return result.length ? true : false
1,040✔
4319
    }
1,040✔
4320

26✔
4321
    /**
26✔
4322
     * Builds create ENUM type sql.
26✔
4323
     */
26✔
4324
    protected createEnumTypeSql(
26✔
4325
        table: Table,
1,100✔
4326
        column: TableColumn,
1,100✔
4327
        enumName?: string,
1,100✔
4328
    ): Query {
1,100✔
4329
        if (!enumName) enumName = this.buildEnumName(table, column)
1,100✔
4330
        const enumValues = column
1,100✔
4331
            .enum!.map((value) => `'${value.replaceAll("'", "''")}'`)
1,100✔
4332
            .join(", ")
1,100✔
4333
        return new Query(`CREATE TYPE ${enumName} AS ENUM(${enumValues})`)
1,100✔
4334
    }
1,100✔
4335

26✔
4336
    /**
26✔
4337
     * Builds create ENUM type sql.
26✔
4338
     */
26✔
4339
    protected dropEnumTypeSql(
26✔
4340
        table: Table,
1,100✔
4341
        column: TableColumn,
1,100✔
4342
        enumName?: string,
1,100✔
4343
    ): Query {
1,100✔
4344
        if (!enumName) enumName = this.buildEnumName(table, column)
1,100✔
4345
        return new Query(`DROP TYPE ${enumName}`)
1,100✔
4346
    }
1,100✔
4347

26✔
4348
    /**
26✔
4349
     * Builds create index sql.
26✔
4350
     */
26✔
4351
    protected createIndexSql(table: Table, index: TableIndex): Query {
26✔
4352
        const columns = index.columnNames
8,064✔
4353
            .map((columnName) => `"${columnName}"`)
8,064✔
4354
            .join(", ")
8,064✔
4355
        return new Query(
8,064✔
4356
            `CREATE ${index.isUnique ? "UNIQUE " : ""}INDEX${
8,064✔
4357
                index.isConcurrent ? " CONCURRENTLY" : ""
8,064✔
4358
            } "${index.name}" ON ${this.escapePath(table)} ${
8,064✔
4359
                index.isSpatial ? "USING GiST " : ""
8,064✔
4360
            }(${columns}) ${index.where ? "WHERE " + index.where : ""}`,
8,064✔
4361
        )
8,064✔
4362
    }
8,064✔
4363

26✔
4364
    /**
26✔
4365
     * Builds create view index sql.
26✔
4366
     */
26✔
4367
    protected createViewIndexSql(view: View, index: TableIndex): Query {
26✔
4368
        const columns = index.columnNames
28✔
4369
            .map((columnName) => `"${columnName}"`)
28✔
4370
            .join(", ")
28✔
4371
        return new Query(
28✔
4372
            `CREATE ${index.isUnique ? "UNIQUE " : ""}INDEX "${
28✔
4373
                index.name
28✔
4374
            }" ON ${this.escapePath(view)} (${columns}) ${
28✔
4375
                index.where ? "WHERE " + index.where : ""
28!
4376
            }`,
28✔
4377
        )
28✔
4378
    }
28✔
4379

26✔
4380
    /**
26✔
4381
     * Builds drop index sql.
26✔
4382
     */
26✔
4383
    protected dropIndexSql(
26✔
4384
        table: Table | View,
8,092✔
4385
        indexOrName: TableIndex | string,
8,092✔
4386
    ): Query {
8,092✔
4387
        const indexName = InstanceChecker.isTableIndex(indexOrName)
8,092✔
4388
            ? indexOrName.name
8,092✔
4389
            : indexOrName
8,092!
4390
        const concurrent = InstanceChecker.isTableIndex(indexOrName)
8,092✔
4391
            ? indexOrName.isConcurrent
8,092✔
4392
            : false
8,092!
4393
        const { schema } = this.driver.parseTableName(table)
8,092✔
4394
        return schema
8,092✔
4395
            ? new Query(
8,092✔
4396
                  `DROP INDEX ${
8,092✔
4397
                      concurrent ? "CONCURRENTLY " : ""
8,092✔
4398
                  }"${schema}"."${indexName}"`,
8,092✔
4399
              )
8,092✔
4400
            : new Query(
8,092!
4401
                  `DROP INDEX ${
×
4402
                      concurrent ? "CONCURRENTLY " : ""
×
4403
                  }"${indexName}"`,
×
4404
              )
8,092✔
4405
    }
8,092✔
4406

26✔
4407
    /**
26✔
4408
     * Builds create primary key sql.
26✔
4409
     */
26✔
4410
    protected createPrimaryKeySql(
26✔
4411
        table: Table,
20✔
4412
        columnNames: string[],
20✔
4413
        constraintName?: string,
20✔
4414
    ): Query {
20✔
4415
        const primaryKeyName = constraintName
20✔
4416
            ? constraintName
20!
4417
            : this.connection.namingStrategy.primaryKeyName(table, columnNames)
20✔
4418

20✔
4419
        const columnNamesString = columnNames
20✔
4420
            .map((columnName) => `"${columnName}"`)
20✔
4421
            .join(", ")
20✔
4422

20✔
4423
        return new Query(
20✔
4424
            `ALTER TABLE ${this.escapePath(
20✔
4425
                table,
20✔
4426
            )} ADD CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNamesString})`,
20✔
4427
        )
20✔
4428
    }
20✔
4429

26✔
4430
    /**
26✔
4431
     * Builds drop primary key sql.
26✔
4432
     */
26✔
4433
    protected dropPrimaryKeySql(table: Table): Query {
26✔
4434
        if (!table.primaryColumns.length)
20✔
4435
            throw new TypeORMError(`Table ${table} has no primary keys.`)
20!
4436

20✔
4437
        const columnNames = table.primaryColumns.map((column) => column.name)
20✔
4438
        const constraintName = table.primaryColumns[0].primaryKeyConstraintName
20✔
4439
        const primaryKeyName = constraintName
20✔
4440
            ? constraintName
20!
4441
            : this.connection.namingStrategy.primaryKeyName(table, columnNames)
20✔
4442

20✔
4443
        return new Query(
20✔
4444
            `ALTER TABLE ${this.escapePath(
20✔
4445
                table,
20✔
4446
            )} DROP CONSTRAINT "${primaryKeyName}"`,
20✔
4447
        )
20✔
4448
    }
20✔
4449

26✔
4450
    /**
26✔
4451
     * Builds create unique constraint sql.
26✔
4452
     */
26✔
4453
    protected createUniqueConstraintSql(
26✔
4454
        table: Table,
92✔
4455
        uniqueConstraint: TableUnique,
92✔
4456
    ): Query {
92✔
4457
        const columnNames = uniqueConstraint.columnNames
92✔
4458
            .map((column) => `"` + column + `"`)
92✔
4459
            .join(", ")
92✔
4460
        let sql = `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
92✔
4461
            uniqueConstraint.name
92✔
4462
        }" UNIQUE (${columnNames})`
92✔
4463
        if (uniqueConstraint.deferrable)
92✔
4464
            sql += ` DEFERRABLE ${uniqueConstraint.deferrable}`
92!
4465
        return new Query(sql)
92✔
4466
    }
92✔
4467

26✔
4468
    /**
26✔
4469
     * Builds drop unique constraint sql.
26✔
4470
     */
26✔
4471
    protected dropUniqueConstraintSql(
26✔
4472
        table: Table,
92✔
4473
        uniqueOrName: TableUnique | string,
92✔
4474
    ): Query {
92✔
4475
        const uniqueName = InstanceChecker.isTableUnique(uniqueOrName)
92✔
4476
            ? uniqueOrName.name
92✔
4477
            : uniqueOrName
92!
4478
        return new Query(
92✔
4479
            `ALTER TABLE ${this.escapePath(
92✔
4480
                table,
92✔
4481
            )} DROP CONSTRAINT "${uniqueName}"`,
92✔
4482
        )
92✔
4483
    }
92✔
4484

26✔
4485
    /**
26✔
4486
     * Builds create check constraint sql.
26✔
4487
     */
26✔
4488
    protected createCheckConstraintSql(
26✔
4489
        table: Table,
40✔
4490
        checkConstraint: TableCheck,
40✔
4491
    ): Query {
40✔
4492
        return new Query(
40✔
4493
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
40✔
4494
                checkConstraint.name
40✔
4495
            }" CHECK (${checkConstraint.expression})`,
40✔
4496
        )
40✔
4497
    }
40✔
4498

26✔
4499
    /**
26✔
4500
     * Builds drop check constraint sql.
26✔
4501
     */
26✔
4502
    protected dropCheckConstraintSql(
26✔
4503
        table: Table,
40✔
4504
        checkOrName: TableCheck | string,
40✔
4505
    ): Query {
40✔
4506
        const checkName = InstanceChecker.isTableCheck(checkOrName)
40✔
4507
            ? checkOrName.name
40✔
4508
            : checkOrName
40!
4509
        return new Query(
40✔
4510
            `ALTER TABLE ${this.escapePath(
40✔
4511
                table,
40✔
4512
            )} DROP CONSTRAINT "${checkName}"`,
40✔
4513
        )
40✔
4514
    }
40✔
4515

26✔
4516
    /**
26✔
4517
     * Builds create exclusion constraint sql.
26✔
4518
     */
26✔
4519
    protected createExclusionConstraintSql(
26✔
4520
        table: Table,
28✔
4521
        exclusionConstraint: TableExclusion,
28✔
4522
    ): Query {
28✔
4523
        return new Query(
28✔
4524
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
28✔
4525
                exclusionConstraint.name
28✔
4526
            }" EXCLUDE ${exclusionConstraint.expression}`,
28✔
4527
        )
28✔
4528
    }
28✔
4529

26✔
4530
    /**
26✔
4531
     * Builds drop exclusion constraint sql.
26✔
4532
     */
26✔
4533
    protected dropExclusionConstraintSql(
26✔
4534
        table: Table,
28✔
4535
        exclusionOrName: TableExclusion | string,
28✔
4536
    ): Query {
28✔
4537
        const exclusionName = InstanceChecker.isTableExclusion(exclusionOrName)
28✔
4538
            ? exclusionOrName.name
28✔
4539
            : exclusionOrName
28!
4540
        return new Query(
28✔
4541
            `ALTER TABLE ${this.escapePath(
28✔
4542
                table,
28✔
4543
            )} DROP CONSTRAINT "${exclusionName}"`,
28✔
4544
        )
28✔
4545
    }
28✔
4546

26✔
4547
    /**
26✔
4548
     * Builds create foreign key sql.
26✔
4549
     */
26✔
4550
    protected createForeignKeySql(
26✔
4551
        table: Table,
14,820✔
4552
        foreignKey: TableForeignKey,
14,820✔
4553
    ): Query {
14,820✔
4554
        const columnNames = foreignKey.columnNames
14,820✔
4555
            .map((column) => `"` + column + `"`)
14,820✔
4556
            .join(", ")
14,820✔
4557
        const referencedColumnNames = foreignKey.referencedColumnNames
14,820✔
4558
            .map((column) => `"` + column + `"`)
14,820✔
4559
            .join(",")
14,820✔
4560
        let sql =
14,820✔
4561
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
14,820✔
4562
                foreignKey.name
14,820✔
4563
            }" FOREIGN KEY (${columnNames}) ` +
14,820✔
4564
            `REFERENCES ${this.escapePath(
14,820✔
4565
                this.getTablePath(foreignKey),
14,820✔
4566
            )}(${referencedColumnNames})`
14,820✔
4567
        if (foreignKey.onDelete) sql += ` ON DELETE ${foreignKey.onDelete}`
14,820✔
4568
        if (foreignKey.onUpdate) sql += ` ON UPDATE ${foreignKey.onUpdate}`
14,820✔
4569
        if (foreignKey.deferrable) sql += ` DEFERRABLE ${foreignKey.deferrable}`
14,820✔
4570

14,820✔
4571
        return new Query(sql)
14,820✔
4572
    }
14,820✔
4573

26✔
4574
    /**
26✔
4575
     * Builds drop foreign key sql.
26✔
4576
     */
26✔
4577
    protected dropForeignKeySql(
26✔
4578
        table: Table,
14,840✔
4579
        foreignKeyOrName: TableForeignKey | string,
14,840✔
4580
    ): Query {
14,840✔
4581
        const foreignKeyName = InstanceChecker.isTableForeignKey(
14,840✔
4582
            foreignKeyOrName,
14,840✔
4583
        )
14,840✔
4584
            ? foreignKeyOrName.name
14,840✔
4585
            : foreignKeyOrName
14,840!
4586
        return new Query(
14,840✔
4587
            `ALTER TABLE ${this.escapePath(
14,840✔
4588
                table,
14,840✔
4589
            )} DROP CONSTRAINT "${foreignKeyName}"`,
14,840✔
4590
        )
14,840✔
4591
    }
14,840✔
4592

26✔
4593
    /**
26✔
4594
     * Builds sequence name from given table and column.
26✔
4595
     */
26✔
4596
    protected buildSequenceName(
26✔
4597
        table: Table,
5,672✔
4598
        columnOrName: TableColumn | string,
5,672✔
4599
    ): string {
5,672✔
4600
        const { tableName } = this.driver.parseTableName(table)
5,672✔
4601

5,672✔
4602
        const columnName = InstanceChecker.isTableColumn(columnOrName)
5,672✔
4603
            ? columnOrName.name
5,672✔
4604
            : columnOrName
5,672✔
4605

5,672✔
4606
        let seqName = `${tableName}_${columnName}_seq`
5,672✔
4607

5,672✔
4608
        if (seqName.length > this.connection.driver.maxAliasLength!) {
5,672✔
4609
            // note doesn't yet handle corner cases where .length differs from number of UTF-8 bytes
32✔
4610
            seqName = `${tableName.substring(0, 29)}_${columnName.substring(
32✔
4611
                0,
32✔
4612
                Math.max(29, 63 - table.name.length - 5),
32✔
4613
            )}_seq`
32✔
4614
        }
32✔
4615

5,672✔
4616
        return seqName
5,672✔
4617
    }
5,672✔
4618

26✔
4619
    protected buildSequencePath(
26✔
4620
        table: Table,
2,884✔
4621
        columnOrName: TableColumn | string,
2,884✔
4622
    ): string {
2,884✔
4623
        const { schema } = this.driver.parseTableName(table)
2,884✔
4624

2,884✔
4625
        return schema
2,884✔
4626
            ? `${schema}.${this.buildSequenceName(table, columnOrName)}`
2,884✔
4627
            : this.buildSequenceName(table, columnOrName)
2,884!
4628
    }
2,884✔
4629

26✔
4630
    /**
26✔
4631
     * Builds ENUM type name from given table and column.
26✔
4632
     */
26✔
4633
    protected buildEnumName(
26✔
4634
        table: Table,
4,372✔
4635
        column: TableColumn,
4,372✔
4636
        withSchema: boolean = true,
4,372✔
4637
        disableEscape?: boolean,
4,372✔
4638
        toOld?: boolean,
4,372✔
4639
    ): string {
4,372✔
4640
        const { schema, tableName } = this.driver.parseTableName(table)
4,372✔
4641
        let enumName = column.enumName
4,372✔
4642
            ? column.enumName
4,372✔
4643
            : `${tableName}_${column.name.toLowerCase()}_enum`
4,372✔
4644
        if (schema && withSchema) enumName = `${schema}.${enumName}`
4,372✔
4645
        if (toOld) enumName = enumName + "_old"
4,372✔
4646
        return enumName
4,372✔
4647
            .split(".")
4,372✔
4648
            .map((i) => {
4,372✔
4649
                return disableEscape ? i : `"${i}"`
6,652✔
4650
            })
4,372✔
4651
            .join(".")
4,372✔
4652
    }
4,372✔
4653

26✔
4654
    protected async getUserDefinedTypeName(table: Table, column: TableColumn) {
26✔
4655
        let { schema, tableName: name } = this.driver.parseTableName(table)
968✔
4656

968✔
4657
        if (!schema) {
968!
4658
            schema = await this.getCurrentSchema()
×
4659
        }
×
4660

968✔
4661
        const result = await this.query(
968✔
4662
            `SELECT "udt_schema", "udt_name" ` +
968✔
4663
                `FROM "information_schema"."columns" WHERE "table_schema" = '${schema}' AND "table_name" = '${name}' AND "column_name"='${column.name}'`,
968✔
4664
        )
968✔
4665

968✔
4666
        // docs: https://www.postgresql.org/docs/current/xtypes.html
968✔
4667
        // When you define a new base type, PostgreSQL automatically provides support for arrays of that type.
968✔
4668
        // The array type typically has the same name as the base type with the underscore character (_) prepended.
968✔
4669
        // ----
968✔
4670
        // so, we must remove this underscore character from enum type name
968✔
4671
        let udtName = result[0]["udt_name"]
968✔
4672
        if (udtName.indexOf("_") === 0) {
968✔
4673
            udtName = udtName.substr(1, udtName.length)
288✔
4674
        }
288✔
4675
        return {
968✔
4676
            schema: result[0]["udt_schema"],
968✔
4677
            name: udtName,
968✔
4678
        }
968✔
4679
    }
968✔
4680

26✔
4681
    /**
26✔
4682
     * Escapes a given comment so it's safe to include in a query.
26✔
4683
     */
26✔
4684
    protected escapeComment(comment?: string) {
26✔
4685
        if (!comment || comment.length === 0) {
2,808✔
4686
            return "NULL"
2,528✔
4687
        }
2,528✔
4688

280✔
4689
        comment = comment.replace(/'/g, "''").replace(/\u0000/g, "") // Null bytes aren't allowed in comments
280✔
4690

280✔
4691
        return `'${comment}'`
280✔
4692
    }
280✔
4693

26✔
4694
    /**
26✔
4695
     * Escapes given table or view path.
26✔
4696
     */
26✔
4697
    protected escapePath(target: Table | View | string): string {
26✔
4698
        const { schema, tableName } = this.driver.parseTableName(target)
101,140✔
4699

101,140✔
4700
        if (schema && schema !== this.driver.searchSchema) {
101,140✔
4701
            return `"${schema}"."${tableName}"`
1,012✔
4702
        }
1,012✔
4703

100,128✔
4704
        return `"${tableName}"`
100,128✔
4705
    }
100,128✔
4706

26✔
4707
    /**
26✔
4708
     * Get the table name with table schema
26✔
4709
     * Note: Without ' or "
26✔
4710
     */
26✔
4711
    protected async getTableNameWithSchema(target: Table | string) {
26✔
4712
        const tableName = InstanceChecker.isTable(target) ? target.name : target
132!
4713
        if (tableName.indexOf(".") === -1) {
132✔
4714
            const schemaResult = await this.query(`SELECT current_schema()`)
132✔
4715
            const schema = schemaResult[0]["current_schema"]
132✔
4716
            return `${schema}.${tableName}`
132✔
4717
        } else {
132!
4718
            return `${tableName.split(".")[0]}.${tableName.split(".")[1]}`
×
4719
        }
×
4720
    }
132✔
4721

26✔
4722
    /**
26✔
4723
     * Builds a query for create column.
26✔
4724
     */
26✔
4725
    protected buildCreateColumnSql(table: Table, column: TableColumn) {
26✔
4726
        let c = '"' + column.name + '"'
77,460✔
4727
        if (
77,460✔
4728
            column.isGenerated === true &&
77,460✔
4729
            column.generationStrategy !== "uuid"
13,824✔
4730
        ) {
77,460✔
4731
            if (column.generationStrategy === "identity") {
13,100✔
4732
                // Postgres 10+ Identity generated column
20✔
4733
                const generatedIdentityOrDefault =
20✔
4734
                    column.generatedIdentity || "BY DEFAULT"
20✔
4735
                c += ` ${column.type} GENERATED ${generatedIdentityOrDefault} AS IDENTITY`
20✔
4736
            } else {
13,100✔
4737
                // classic SERIAL primary column
13,080✔
4738
                if (
13,080✔
4739
                    column.type === "integer" ||
13,080✔
4740
                    column.type === "int" ||
13,080✔
4741
                    column.type === "int4"
40✔
4742
                )
13,080✔
4743
                    c += " SERIAL"
13,080✔
4744
                if (column.type === "smallint" || column.type === "int2")
13,080✔
4745
                    c += " SMALLSERIAL"
13,080✔
4746
                if (column.type === "bigint" || column.type === "int8")
13,080✔
4747
                    c += " BIGSERIAL"
13,080✔
4748
            }
13,080✔
4749
        }
13,100✔
4750
        if (column.type === "enum" || column.type === "simple-enum") {
77,460✔
4751
            c += " " + this.buildEnumName(table, column)
1,060✔
4752
            if (column.isArray) c += " array"
1,060✔
4753
        } else if (!column.isGenerated || column.type === "uuid") {
77,460✔
4754
            c += " " + this.connection.driver.createFullType(column)
63,300✔
4755
        }
63,300✔
4756

77,460✔
4757
        // Postgres only supports the stored generated column type
77,460✔
4758
        if (column.generatedType === "STORED" && column.asExpression) {
77,460✔
4759
            c += ` GENERATED ALWAYS AS (${column.asExpression}) STORED`
132✔
4760
        }
132✔
4761

77,460✔
4762
        if (column.charset) c += ' CHARACTER SET "' + column.charset + '"'
77,460!
4763
        if (column.collation) c += ' COLLATE "' + column.collation + '"'
77,460✔
4764
        if (column.isNullable !== true) c += " NOT NULL"
77,460✔
4765
        if (column.default !== undefined && column.default !== null)
77,460✔
4766
            c += " DEFAULT " + column.default
77,460✔
4767
        if (
77,460✔
4768
            column.isGenerated &&
77,460✔
4769
            column.generationStrategy === "uuid" &&
77,460✔
4770
            !column.default
724✔
4771
        )
77,460✔
4772
            c += ` DEFAULT ${this.driver.uuidGenerator}`
77,460✔
4773

77,460✔
4774
        return c
77,460✔
4775
    }
77,460✔
4776

26✔
4777
    /**
26✔
4778
     * Checks if the PostgreSQL server has support for partitioned tables
26✔
4779
     */
26✔
4780
    protected async hasSupportForPartitionedTables() {
26✔
4781
        const result = await this.query(
2,040✔
4782
            `SELECT TRUE FROM information_schema.columns WHERE table_name = 'pg_class' and column_name = 'relispartition'`,
2,040✔
4783
        )
2,040✔
4784
        return result.length ? true : false
2,040!
4785
    }
2,040✔
4786

26✔
4787
    /**
26✔
4788
     * Change table comment.
26✔
4789
     */
26✔
4790
    async changeTableComment(
26✔
4791
        tableOrName: Table | string,
1,260✔
4792
        newComment?: string,
1,260✔
4793
    ): Promise<void> {
1,260✔
4794
        const upQueries: Query[] = []
1,260✔
4795
        const downQueries: Query[] = []
1,260✔
4796

1,260✔
4797
        const table = InstanceChecker.isTable(tableOrName)
1,260✔
4798
            ? tableOrName
1,260✔
4799
            : await this.getCachedTable(tableOrName)
1,260!
4800

×
4801
        newComment = this.escapeComment(newComment)
×
4802
        const comment = this.escapeComment(table.comment)
×
4803

×
4804
        if (newComment === comment) {
1,260✔
4805
            return
1,252✔
4806
        }
1,252✔
4807

8✔
4808
        const newTable = table.clone()
8✔
4809

8✔
4810
        upQueries.push(
8✔
4811
            new Query(
8✔
4812
                `COMMENT ON TABLE ${this.escapePath(
8✔
4813
                    newTable,
8✔
4814
                )} IS ${newComment}`,
8✔
4815
            ),
8✔
4816
        )
8✔
4817

8✔
4818
        downQueries.push(
8✔
4819
            new Query(
8✔
4820
                `COMMENT ON TABLE ${this.escapePath(table)} IS ${comment}`,
8✔
4821
            ),
8✔
4822
        )
8✔
4823

8✔
4824
        await this.executeQueries(upQueries, downQueries)
8✔
4825

8✔
4826
        table.comment = newTable.comment
8✔
4827
        this.replaceCachedTable(table, newTable)
8✔
4828
    }
8✔
4829
}
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