• 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

81.61
/src/driver/cockroachdb/CockroachQueryRunner.ts
1
import { ObjectLiteral } from "../../common/ObjectLiteral"
26✔
2
import { TypeORMError } from "../../error"
26✔
3
import { QueryFailedError } from "../../error/QueryFailedError"
26✔
4
import { QueryRunnerAlreadyReleasedError } from "../../error/QueryRunnerAlreadyReleasedError"
26✔
5
import { TransactionNotStartedError } from "../../error/TransactionNotStartedError"
26✔
6
import { ReadStream } from "../../platform/PlatformTools"
26✔
7
import { BaseQueryRunner } from "../../query-runner/BaseQueryRunner"
26✔
8
import { QueryResult } from "../../query-runner/QueryResult"
26✔
9
import { QueryRunner } from "../../query-runner/QueryRunner"
26✔
10
import { TableIndexOptions } from "../../schema-builder/options/TableIndexOptions"
26✔
11
import { Table } from "../../schema-builder/table/Table"
26✔
12
import { TableCheck } from "../../schema-builder/table/TableCheck"
26✔
13
import { TableColumn } from "../../schema-builder/table/TableColumn"
26✔
14
import { TableExclusion } from "../../schema-builder/table/TableExclusion"
26✔
15
import { TableForeignKey } from "../../schema-builder/table/TableForeignKey"
26✔
16
import { TableIndex } from "../../schema-builder/table/TableIndex"
26✔
17
import { TableUnique } from "../../schema-builder/table/TableUnique"
26✔
18
import { View } from "../../schema-builder/view/View"
26✔
19
import { Broadcaster } from "../../subscriber/Broadcaster"
26✔
20
import { BroadcasterResult } from "../../subscriber/BroadcasterResult"
26✔
21
import { InstanceChecker } from "../../util/InstanceChecker"
26✔
22
import { OrmUtils } from "../../util/OrmUtils"
26✔
23
import { VersionUtils } from "../../util/VersionUtils"
26✔
24
import { 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 { CockroachDriver } from "./CockroachDriver"
26✔
30

26✔
31
/**
26✔
32
 * Runs queries on a single postgres database connection.
26✔
33
 */
26✔
34
export class CockroachQueryRunner
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: CockroachDriver
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
     * Stores all executed queries to be able to run them again if transaction fails.
26✔
63
     */
26✔
64
    protected queries: { query: string; parameters?: any[] }[] = []
26✔
65

26✔
66
    /**
26✔
67
     * Indicates if running queries must be stored
26✔
68
     */
26✔
69
    protected storeQueries: boolean = false
26✔
70

26✔
71
    /**
26✔
72
     * Current number of transaction retries in case of 40001 error.
26✔
73
     */
26✔
74
    protected transactionRetries: number = 0
26✔
75

26✔
76
    // -------------------------------------------------------------------------
26✔
77
    // Constructor
26✔
78
    // -------------------------------------------------------------------------
26✔
79

26✔
80
    constructor(driver: CockroachDriver, mode: ReplicationMode) {
26✔
81
        super()
12,309✔
82
        this.driver = driver
12,309✔
83
        this.connection = driver.connection
12,309✔
84
        this.mode = mode
12,309✔
85
        this.broadcaster = new Broadcaster(this)
12,309✔
86
    }
12,309✔
87

26✔
88
    // -------------------------------------------------------------------------
26✔
89
    // Public Methods
26✔
90
    // -------------------------------------------------------------------------
26✔
91

26✔
92
    /**
26✔
93
     * Creates/uses database connection from the connection pool to perform further operations.
26✔
94
     * Returns obtained database connection.
26✔
95
     */
26✔
96
    connect(): Promise<any> {
26✔
97
        if (this.databaseConnection)
85,532✔
98
            return Promise.resolve(this.databaseConnection)
85,532✔
99

12,351✔
100
        if (this.databaseConnectionPromise)
12,351✔
101
            return this.databaseConnectionPromise
85,532✔
102

12,288✔
103
        if (this.mode === "slave" && this.driver.isReplicated) {
85,532!
104
            this.databaseConnectionPromise = this.driver
×
105
                .obtainSlaveConnection()
×
106
                .then(([connection, release]: any[]) => {
×
107
                    this.driver.connectedQueryRunners.push(this)
×
108
                    this.databaseConnection = connection
×
109

×
110
                    const onErrorCallback = (err: Error) =>
×
111
                        this.releaseConnection(err)
×
112
                    this.releaseCallback = (err?: Error) => {
×
113
                        this.databaseConnection.removeListener(
×
114
                            "error",
×
115
                            onErrorCallback,
×
116
                        )
×
117
                        release(err)
×
118
                    }
×
119
                    this.databaseConnection.on("error", onErrorCallback)
×
120

×
121
                    return this.databaseConnection
×
122
                })
×
123
        } else {
85,532✔
124
            // master
12,288✔
125
            this.databaseConnectionPromise = this.driver
12,288✔
126
                .obtainMasterConnection()
12,288✔
127
                .then(([connection, release]: any[]) => {
12,288✔
128
                    this.driver.connectedQueryRunners.push(this)
12,288✔
129
                    this.databaseConnection = connection
12,288✔
130

12,288✔
131
                    const onErrorCallback = (err: Error) =>
12,288✔
132
                        this.releaseConnection(err)
×
133
                    this.releaseCallback = (err?: Error) => {
12,288✔
134
                        this.databaseConnection.removeListener(
12,288✔
135
                            "error",
12,288✔
136
                            onErrorCallback,
12,288✔
137
                        )
12,288✔
138
                        release(err)
12,288✔
139
                    }
12,288✔
140
                    this.databaseConnection.on("error", onErrorCallback)
12,288✔
141

12,288✔
142
                    return this.databaseConnection
12,288✔
143
                })
12,288✔
144
        }
12,288✔
145

12,288✔
146
        return this.databaseConnectionPromise
12,288✔
147
    }
12,288✔
148

26✔
149
    /**
26✔
150
     * Release a connection back to the pool, optionally specifying an Error to release with.
26✔
151
     * Per pg-pool documentation this will prevent the pool from re-using the broken connection.
26✔
152
     */
26✔
153
    private async releaseConnection(err?: Error) {
26✔
154
        if (this.isReleased) {
12,309!
155
            return
×
156
        }
×
157

12,309✔
158
        this.isReleased = true
12,309✔
159
        if (this.releaseCallback) {
12,309✔
160
            this.releaseCallback(err)
12,288✔
161
            this.releaseCallback = undefined
12,288✔
162
        }
12,288✔
163

12,309✔
164
        const index = this.driver.connectedQueryRunners.indexOf(this)
12,309✔
165

12,309✔
166
        if (index !== -1) {
12,309✔
167
            this.driver.connectedQueryRunners.splice(index, 1)
12,288✔
168
        }
12,288✔
169
    }
12,309✔
170

26✔
171
    /**
26✔
172
     * Releases used database connection.
26✔
173
     * You cannot use query runner methods once its released.
26✔
174
     */
26✔
175
    release(): Promise<void> {
26✔
176
        return this.releaseConnection()
12,309✔
177
    }
12,309✔
178

26✔
179
    /**
26✔
180
     * Starts transaction.
26✔
181
     */
26✔
182
    async startTransaction(isolationLevel?: IsolationLevel): Promise<void> {
26✔
183
        this.isTransactionActive = true
7,501✔
184
        this.transactionRetries = 0
7,501✔
185
        try {
7,501✔
186
            await this.broadcaster.broadcast("BeforeTransactionStart")
7,501✔
187
        } catch (err) {
7,501!
188
            this.isTransactionActive = false
×
189
            throw err
×
190
        }
×
191

7,501✔
192
        if (this.transactionDepth === 0) {
7,501✔
193
            await this.query("START TRANSACTION")
7,485✔
194
            await this.query("SAVEPOINT cockroach_restart")
7,485✔
195
            if (isolationLevel) {
7,485✔
196
                await this.query(
2✔
197
                    "SET TRANSACTION ISOLATION LEVEL " + isolationLevel,
2✔
198
                )
2✔
199
            }
2✔
200
        } else {
7,501✔
201
            await this.query(`SAVEPOINT typeorm_${this.transactionDepth}`)
16✔
202
        }
16✔
203

7,501✔
204
        this.transactionDepth += 1
7,501✔
205
        this.storeQueries = true
7,501✔
206

7,501✔
207
        await this.broadcaster.broadcast("AfterTransactionStart")
7,501✔
208
    }
7,501✔
209

26✔
210
    /**
26✔
211
     * Commits transaction.
26✔
212
     * Error will be thrown if transaction was not started.
26✔
213
     */
26✔
214
    async commitTransaction(): Promise<void> {
26✔
215
        if (!this.isTransactionActive) throw new TransactionNotStartedError()
7,473!
216

7,473✔
217
        await this.broadcaster.broadcast("BeforeTransactionCommit")
7,473✔
218

7,473✔
219
        if (this.transactionDepth > 1) {
7,473✔
220
            await this.query(
10✔
221
                `RELEASE SAVEPOINT typeorm_${this.transactionDepth - 1}`,
10✔
222
            )
10✔
223
            this.transactionDepth -= 1
10✔
224
        } else {
7,473✔
225
            this.storeQueries = false
7,463✔
226
            await this.query("RELEASE SAVEPOINT cockroach_restart")
7,463✔
227
            await this.query("COMMIT")
7,463✔
228
            this.queries = []
7,463✔
229
            this.isTransactionActive = false
7,463✔
230
            this.transactionRetries = 0
7,463✔
231
            this.transactionDepth -= 1
7,463✔
232
        }
7,463✔
233

7,473✔
234
        await this.broadcaster.broadcast("AfterTransactionCommit")
7,473✔
235
    }
7,473✔
236

26✔
237
    /**
26✔
238
     * Rollbacks transaction.
26✔
239
     * Error will be thrown if transaction was not started.
26✔
240
     */
26✔
241
    async rollbackTransaction(): Promise<void> {
26✔
242
        if (!this.isTransactionActive) throw new TransactionNotStartedError()
28!
243

28✔
244
        await this.broadcaster.broadcast("BeforeTransactionRollback")
28✔
245

28✔
246
        if (this.transactionDepth > 1) {
28✔
247
            await this.query(
6✔
248
                `ROLLBACK TO SAVEPOINT typeorm_${this.transactionDepth - 1}`,
6✔
249
            )
6✔
250
        } else {
28✔
251
            this.storeQueries = false
22✔
252
            await this.query("ROLLBACK")
22✔
253
            this.queries = []
22✔
254
            this.isTransactionActive = false
22✔
255
            this.transactionRetries = 0
22✔
256
        }
22✔
257
        this.transactionDepth -= 1
28✔
258

28✔
259
        await this.broadcaster.broadcast("AfterTransactionRollback")
28✔
260
    }
28✔
261

26✔
262
    /**
26✔
263
     * Executes a given SQL query.
26✔
264
     */
26✔
265
    async query(
26✔
266
        query: string,
85,526✔
267
        parameters?: any[],
85,526✔
268
        useStructuredResult = false,
85,526✔
269
    ): Promise<any> {
85,526✔
270
        if (this.isReleased) throw new QueryRunnerAlreadyReleasedError()
85,526!
271

85,526✔
272
        const databaseConnection = await this.connect()
85,526✔
273

85,526✔
274
        this.driver.connection.logger.logQuery(query, parameters, this)
85,526✔
275
        await this.broadcaster.broadcast("BeforeQuery", query, parameters)
85,526✔
276

85,526✔
277
        const broadcasterResult = new BroadcasterResult()
85,526✔
278
        const queryStartTime = Date.now()
85,526✔
279

85,526✔
280
        if (this.isTransactionActive && this.storeQueries) {
85,526✔
281
            this.queries.push({ query, parameters })
22,226✔
282
        }
22,226✔
283

85,526✔
284
        try {
85,526✔
285
            const raw = await new Promise<any>((ok, fail) => {
85,526✔
286
                databaseConnection.query(
85,526✔
287
                    query,
85,526✔
288
                    parameters,
85,526✔
289
                    (err: any, raw: any) => (err ? fail(err) : ok(raw)),
85,526✔
290
                )
85,526✔
291
            })
85,526✔
292

85,512✔
293
            // log slow queries if maxQueryExecution time is set
85,512✔
294
            const maxQueryExecutionTime =
85,512✔
295
                this.driver.options.maxQueryExecutionTime
85,512✔
296
            const queryEndTime = Date.now()
85,512✔
297
            const queryExecutionTime = queryEndTime - queryStartTime
85,512✔
298
            if (
85,512✔
299
                maxQueryExecutionTime &&
85,512!
300
                queryExecutionTime > maxQueryExecutionTime
×
301
            ) {
85,526!
302
                this.driver.connection.logger.logQuerySlow(
×
303
                    queryExecutionTime,
×
304
                    query,
×
305
                    parameters,
×
306
                    this,
×
307
                )
×
308
            }
×
309

85,512✔
310
            const result = new QueryResult()
85,512✔
311

85,512✔
312
            if (raw.hasOwnProperty("rowCount")) {
85,526✔
313
                result.affected = raw.rowCount
85,479✔
314
            }
85,479✔
315

85,512✔
316
            if (raw.hasOwnProperty("rows")) {
85,526✔
317
                result.records = raw.rows
85,479✔
318
            }
85,479✔
319

85,512✔
320
            switch (raw.command) {
85,512✔
321
                case "DELETE":
85,526✔
322
                    // for DELETE query additionally return number of affected rows
112✔
323
                    result.raw = [raw.rows, raw.rowCount]
112✔
324
                    break
112✔
325
                default:
85,526✔
326
                    result.raw = raw.rows
85,400✔
327
            }
85,526✔
328

85,512✔
329
            this.broadcaster.broadcastAfterQueryEvent(
85,512✔
330
                broadcasterResult,
85,512✔
331
                query,
85,512✔
332
                parameters,
85,512✔
333
                true,
85,512✔
334
                queryExecutionTime,
85,512✔
335
                raw,
85,512✔
336
                undefined,
85,512✔
337
            )
85,512✔
338

85,512✔
339
            if (useStructuredResult) {
85,526✔
340
                return result
14,859✔
341
            } else {
85,526✔
342
                return result.raw
70,653✔
343
            }
70,653✔
344
        } catch (err) {
85,526✔
345
            if (
14✔
346
                err.code === "40001" &&
14✔
347
                this.isTransactionActive &&
14✔
348
                this.transactionRetries <
5✔
349
                    (this.driver.options.maxTransactionRetries || 5)
5✔
350
            ) {
14✔
351
                this.transactionRetries += 1
5✔
352
                this.storeQueries = false
5✔
353
                await this.query("ROLLBACK TO SAVEPOINT cockroach_restart")
5✔
354
                const sleepTime =
5✔
355
                    2 ** this.transactionRetries *
5✔
356
                    0.1 *
5✔
357
                    (Math.random() + 0.5) *
5✔
358
                    1000
5✔
359
                await new Promise((resolve) => setTimeout(resolve, sleepTime))
5✔
360

5✔
361
                let result = undefined
5✔
362
                for (const q of this.queries) {
5✔
363
                    this.driver.connection.logger.logQuery(
7✔
364
                        `Retrying transaction for query "${q.query}"`,
7✔
365
                        q.parameters,
7✔
366
                        this,
7✔
367
                    )
7✔
368
                    result = await this.query(q.query, q.parameters)
7✔
369
                }
7✔
370
                this.transactionRetries = 0
5✔
371
                this.storeQueries = true
5✔
372

5✔
373
                return result
5✔
374
            } else {
14✔
375
                this.driver.connection.logger.logQueryError(
9✔
376
                    err,
9✔
377
                    query,
9✔
378
                    parameters,
9✔
379
                    this,
9✔
380
                )
9✔
381
                this.broadcaster.broadcastAfterQueryEvent(
9✔
382
                    broadcasterResult,
9✔
383
                    query,
9✔
384
                    parameters,
9✔
385
                    false,
9✔
386
                    undefined,
9✔
387
                    undefined,
9✔
388
                    err,
9✔
389
                )
9✔
390
                throw new QueryFailedError(query, parameters, err)
9✔
391
            }
9✔
392
        } finally {
85,526!
393
            await broadcasterResult.wait()
85,526✔
394
        }
85,526✔
395
    }
85,526✔
396

26✔
397
    /**
26✔
398
     * Returns raw data stream.
26✔
399
     */
26✔
400
    async stream(
26✔
401
        query: string,
2✔
402
        parameters?: any[],
2✔
403
        onEnd?: Function,
2✔
404
        onError?: Function,
2✔
405
    ): Promise<ReadStream> {
2✔
406
        const QueryStream = this.driver.loadStreamDependency()
2✔
407
        if (this.isReleased) {
2!
408
            throw new QueryRunnerAlreadyReleasedError()
×
409
        }
×
410

2✔
411
        const databaseConnection = await this.connect()
2✔
412
        this.driver.connection.logger.logQuery(query, parameters, this)
2✔
413
        const stream = databaseConnection.query(
2✔
414
            new QueryStream(query, parameters),
2✔
415
        )
2✔
416

2✔
417
        if (onEnd) {
2✔
418
            stream.on("end", onEnd)
1✔
419
        }
1✔
420

2✔
421
        if (onError) {
2✔
422
            stream.on("error", onError)
1✔
423
        }
1✔
424

2✔
425
        return stream
2✔
426
    }
2✔
427

26✔
428
    /**
26✔
429
     * Returns all available database names including system databases.
26✔
430
     */
26✔
431
    async getDatabases(): Promise<string[]> {
26✔
432
        return Promise.resolve([])
×
433
    }
×
434

26✔
435
    /**
26✔
436
     * Returns all available schema names including system schemas.
26✔
437
     * If database parameter specified, returns schemas of that database.
26✔
438
     */
26✔
439
    async getSchemas(database?: string): Promise<string[]> {
26✔
440
        return Promise.resolve([])
×
441
    }
×
442

26✔
443
    /**
26✔
444
     * Checks if database with the given name exist.
26✔
445
     */
26✔
446
    async hasDatabase(database: string): Promise<boolean> {
26✔
447
        const result = await this.query(
3✔
448
            `SELECT * FROM "pg_database" WHERE "datname" = '${database}'`,
3✔
449
        )
3✔
450
        return result.length ? true : false
3✔
451
    }
3✔
452

26✔
453
    /**
26✔
454
     * Loads currently using database
26✔
455
     */
26✔
456
    async getCurrentDatabase(): Promise<string> {
26✔
457
        const query = await this.query(`SELECT * FROM current_database()`)
1,672✔
458
        return query[0]["current_database"]
1,672✔
459
    }
1,672✔
460

26✔
461
    /**
26✔
462
     * Checks if schema with the given name exist.
26✔
463
     */
26✔
464
    async hasSchema(schema: string): Promise<boolean> {
26✔
465
        const result = await this.query(
×
466
            `SELECT * FROM "information_schema"."schemata" WHERE "schema_name" = '${schema}'`,
×
467
        )
×
468
        return result.length ? true : false
×
469
    }
×
470

26✔
471
    /**
26✔
472
     * Loads currently using database schema
26✔
473
     */
26✔
474
    async getCurrentSchema(): Promise<string> {
26✔
475
        const query = await this.query(`SELECT * FROM current_schema()`)
2,186✔
476
        return query[0]["current_schema"]
2,186✔
477
    }
2,186✔
478

26✔
479
    /**
26✔
480
     * Checks if table with the given name exist in the database.
26✔
481
     */
26✔
482
    async hasTable(tableOrName: Table | string): Promise<boolean> {
26✔
483
        const parsedTableName = this.driver.parseTableName(tableOrName)
1,446✔
484

1,446✔
485
        if (!parsedTableName.schema) {
1,446!
486
            parsedTableName.schema = await this.getCurrentSchema()
×
487
        }
×
488

1,446✔
489
        const sql = `SELECT * FROM "information_schema"."tables" WHERE "table_schema" = '${parsedTableName.schema}' AND "table_name" = '${parsedTableName.tableName}'`
1,446✔
490
        const result = await this.query(sql)
1,446✔
491
        return result.length ? true : false
1,446✔
492
    }
1,446✔
493

26✔
494
    /**
26✔
495
     * Checks if column with the given name exist in the given table.
26✔
496
     */
26✔
497
    async hasColumn(
26✔
498
        tableOrName: Table | string,
4✔
499
        columnName: string,
4✔
500
    ): Promise<boolean> {
4✔
501
        const parsedTableName = this.driver.parseTableName(tableOrName)
4✔
502

4✔
503
        if (!parsedTableName.schema) {
4!
504
            parsedTableName.schema = await this.getCurrentSchema()
×
505
        }
×
506

4✔
507
        const sql = `SELECT * FROM "information_schema"."columns" WHERE "table_schema" = '${parsedTableName.schema}' AND "table_name" = '${parsedTableName.tableName}' AND "column_name" = '${columnName}'`
4✔
508
        const result = await this.query(sql)
4✔
509
        return result.length ? true : false
4✔
510
    }
4✔
511

26✔
512
    /**
26✔
513
     * Creates a new database.
26✔
514
     */
26✔
515
    async createDatabase(
26✔
516
        database: string,
2✔
517
        ifNotExist?: boolean,
2✔
518
    ): Promise<void> {
2✔
519
        const up = `CREATE DATABASE ${
2✔
520
            ifNotExist ? "IF NOT EXISTS " : ""
2!
521
        } "${database}"`
2✔
522
        const down = `DROP DATABASE "${database}"`
2✔
523
        await this.executeQueries(new Query(up), new Query(down))
2✔
524
    }
2✔
525

26✔
526
    /**
26✔
527
     * Drops database.
26✔
528
     */
26✔
529
    async dropDatabase(database: string, ifExist?: boolean): Promise<void> {
26✔
530
        const up = `DROP DATABASE ${ifExist ? "IF EXISTS " : ""} "${database}"`
1!
531
        const down = `CREATE DATABASE "${database}"`
1✔
532
        await this.executeQueries(new Query(up), new Query(down))
1✔
533
    }
1✔
534

26✔
535
    /**
26✔
536
     * Creates a new table schema.
26✔
537
     */
26✔
538
    async createSchema(
26✔
539
        schemaPath: string,
2✔
540
        ifNotExist?: boolean,
2✔
541
    ): Promise<void> {
2✔
542
        const schema =
2✔
543
            schemaPath.indexOf(".") === -1
2✔
544
                ? schemaPath
2✔
545
                : schemaPath.split(".")[1]
2!
546

2✔
547
        const up = ifNotExist
2✔
548
            ? `CREATE SCHEMA IF NOT EXISTS "${schema}"`
2✔
549
            : `CREATE SCHEMA "${schema}"`
2!
550
        const down = `DROP SCHEMA "${schema}" CASCADE`
2✔
551
        await this.executeQueries(new Query(up), new Query(down))
2✔
552
    }
2✔
553

26✔
554
    /**
26✔
555
     * Drops table schema.
26✔
556
     */
26✔
557
    async dropSchema(
26✔
558
        schemaPath: string,
×
559
        ifExist?: boolean,
×
560
        isCascade?: boolean,
×
561
    ): Promise<void> {
×
562
        const schema =
×
563
            schemaPath.indexOf(".") === -1
×
564
                ? schemaPath
×
565
                : schemaPath.split(".")[1]
×
566

×
567
        const up = ifExist
×
568
            ? `DROP SCHEMA IF EXISTS "${schema}" ${isCascade ? "CASCADE" : ""}`
×
569
            : `DROP SCHEMA "${schema}" ${isCascade ? "CASCADE" : ""}`
×
570
        const down = `CREATE SCHEMA "${schema}"`
×
571
        await this.executeQueries(new Query(up), new Query(down))
×
572
    }
×
573

26✔
574
    /**
26✔
575
     * Creates a new table.
26✔
576
     */
26✔
577
    async createTable(
26✔
578
        table: Table,
4,500✔
579
        ifNotExist: boolean = false,
4,500✔
580
        createForeignKeys: boolean = true,
4,500✔
581
        createIndices: boolean = true,
4,500✔
582
    ): Promise<void> {
4,500✔
583
        if (ifNotExist) {
4,500✔
584
            const isTableExist = await this.hasTable(table)
26✔
585
            if (isTableExist) return Promise.resolve()
26✔
586
        }
26✔
587
        const upQueries: Query[] = []
4,499✔
588
        const downQueries: Query[] = []
4,499✔
589

4,499✔
590
        // if table have column with ENUM type, we must create this type in postgres.
4,499✔
591
        const enumColumns = table.columns.filter(
4,499✔
592
            (column) => column.type === "enum" || column.type === "simple-enum",
4,499✔
593
        )
4,499✔
594
        const createdEnumTypes: string[] = []
4,499✔
595
        for (const column of enumColumns) {
4,500✔
596
            // TODO: Should also check if values of existing type matches expected ones
108✔
597
            const hasEnum = await this.hasEnumType(table, column)
108✔
598
            const enumName = this.buildEnumName(table, column)
108✔
599

108✔
600
            // if enum with the same "enumName" is defined more then once, me must prevent double creation
108✔
601
            if (!hasEnum && createdEnumTypes.indexOf(enumName) === -1) {
108✔
602
                createdEnumTypes.push(enumName)
106✔
603
                upQueries.push(this.createEnumTypeSql(table, column, enumName))
106✔
604
                downQueries.push(this.dropEnumTypeSql(table, column, enumName))
106✔
605
            }
106✔
606
        }
108✔
607

4,499✔
608
        table.columns
4,499✔
609
            .filter(
4,499✔
610
                (column) =>
4,499✔
611
                    column.isGenerated &&
14,256✔
612
                    column.generationStrategy === "increment",
4,499✔
613
            )
4,499✔
614
            .forEach((column) => {
4,499✔
615
                upQueries.push(
2,615✔
616
                    new Query(
2,615✔
617
                        `CREATE SEQUENCE ${this.escapePath(
2,615✔
618
                            this.buildSequencePath(table, column),
2,615✔
619
                        )}`,
2,615✔
620
                    ),
2,615✔
621
                )
2,615✔
622
                downQueries.push(
2,615✔
623
                    new Query(
2,615✔
624
                        `DROP SEQUENCE ${this.escapePath(
2,615✔
625
                            this.buildSequencePath(table, column),
2,615✔
626
                        )}`,
2,615✔
627
                    ),
2,615✔
628
                )
2,615✔
629
            })
4,499✔
630

4,499✔
631
        upQueries.push(this.createTableSql(table, createForeignKeys))
4,499✔
632
        downQueries.push(this.dropTableSql(table))
4,499✔
633

4,499✔
634
        // if createForeignKeys is true, we must drop created foreign keys in down query.
4,499✔
635
        // createTable does not need separate method to create foreign keys, because it create fk's in the same query with table creation.
4,499✔
636
        if (createForeignKeys)
4,499✔
637
            table.foreignKeys.forEach((foreignKey) =>
4,500✔
638
                downQueries.push(this.dropForeignKeySql(table, foreignKey)),
49✔
639
            )
49✔
640

4,499✔
641
        if (createIndices) {
4,499✔
642
            table.indices
4,499✔
643
                .filter((index) => !index.isUnique)
4,499✔
644
                .forEach((index) => {
4,499✔
645
                    // new index may be passed without name. In this case we generate index name manually.
2,947✔
646
                    if (!index.name)
2,947✔
647
                        index.name = this.connection.namingStrategy.indexName(
2,947✔
648
                            table,
2✔
649
                            index.columnNames,
2✔
650
                            index.where,
2✔
651
                        )
2✔
652
                    upQueries.push(this.createIndexSql(table, index))
2,947✔
653
                    downQueries.push(this.dropIndexSql(table, index))
2,947✔
654
                })
4,499✔
655
        }
4,499✔
656

4,499✔
657
        // if table have column with generated type, we must add the expression to the metadata table
4,499✔
658
        const generatedColumns = table.columns.filter(
4,499✔
659
            (column) => column.generatedType && column.asExpression,
4,499✔
660
        )
4,499✔
661

4,499✔
662
        for (const column of generatedColumns) {
4,500✔
663
            const currentSchema = await this.getCurrentSchema()
28✔
664
            let { schema } = this.driver.parseTableName(table)
28✔
665
            if (!schema) {
28!
666
                schema = currentSchema
×
667
            }
×
668

28✔
669
            const insertQuery = this.insertTypeormMetadataSql({
28✔
670
                schema: schema,
28✔
671
                table: table.name,
28✔
672
                type: MetadataTableType.GENERATED_COLUMN,
28✔
673
                name: column.name,
28✔
674
                value: column.asExpression,
28✔
675
            })
28✔
676

28✔
677
            const deleteQuery = this.deleteTypeormMetadataSql({
28✔
678
                schema: schema,
28✔
679
                table: table.name,
28✔
680
                type: MetadataTableType.GENERATED_COLUMN,
28✔
681
                name: column.name,
28✔
682
            })
28✔
683

28✔
684
            upQueries.push(insertQuery)
28✔
685
            downQueries.push(deleteQuery)
28✔
686
        }
28✔
687

4,499✔
688
        await this.executeQueries(upQueries, downQueries)
4,499✔
689
    }
4,499✔
690

26✔
691
    /**
26✔
692
     * Drops the table.
26✔
693
     */
26✔
694
    async dropTable(
26✔
695
        target: Table | string,
11✔
696
        ifExist?: boolean,
11✔
697
        dropForeignKeys: boolean = true,
11✔
698
        dropIndices: boolean = true,
11✔
699
    ): Promise<void> {
11✔
700
        // It needs because if table does not exist and dropForeignKeys or dropIndices is true, we don't need
11✔
701
        // to perform drop queries for foreign keys and indices.
11✔
702
        if (ifExist) {
11!
703
            const isTableExist = await this.hasTable(target)
×
704
            if (!isTableExist) return Promise.resolve()
×
705
        }
×
706

11✔
707
        // if dropTable called with dropForeignKeys = true, we must create foreign keys in down query.
11✔
708
        const createForeignKeys: boolean = dropForeignKeys
11✔
709
        const tablePath = this.getTablePath(target)
11✔
710
        const table = await this.getCachedTable(tablePath)
11✔
711
        const upQueries: Query[] = []
11✔
712
        const downQueries: Query[] = []
11✔
713

11✔
714
        // foreign keys must be dropped before indices, because fk's rely on indices
11✔
715
        if (dropForeignKeys)
11✔
716
            table.foreignKeys.forEach((foreignKey) =>
11✔
717
                upQueries.push(this.dropForeignKeySql(table, foreignKey)),
11✔
718
            )
11✔
719

11✔
720
        if (dropIndices) {
11✔
721
            table.indices.forEach((index) => {
11✔
722
                upQueries.push(this.dropIndexSql(table, index))
3✔
723
                downQueries.push(this.createIndexSql(table, index))
3✔
724
            })
11✔
725
        }
11✔
726

11✔
727
        upQueries.push(this.dropTableSql(table))
11✔
728
        downQueries.push(this.createTableSql(table, createForeignKeys))
11✔
729

11✔
730
        table.columns
11✔
731
            .filter(
11✔
732
                (column) =>
11✔
733
                    column.isGenerated &&
46✔
734
                    column.generationStrategy === "increment",
11✔
735
            )
11✔
736
            .forEach((column) => {
11✔
737
                upQueries.push(
10✔
738
                    new Query(
10✔
739
                        `DROP SEQUENCE ${this.escapePath(
10✔
740
                            this.buildSequencePath(table, column),
10✔
741
                        )}`,
10✔
742
                    ),
10✔
743
                )
10✔
744
                downQueries.push(
10✔
745
                    new Query(
10✔
746
                        `CREATE SEQUENCE ${this.escapePath(
10✔
747
                            this.buildSequencePath(table, column),
10✔
748
                        )}`,
10✔
749
                    ),
10✔
750
                )
10✔
751
            })
11✔
752

11✔
753
        // if table had columns with generated type, we must remove the expression from the metadata table
11✔
754
        const generatedColumns = table.columns.filter(
11✔
755
            (column) => column.generatedType && column.asExpression,
11✔
756
        )
11✔
757

11✔
758
        for (const column of generatedColumns) {
11✔
759
            const currentSchema = await this.getCurrentSchema()
4✔
760
            let { schema } = this.driver.parseTableName(table)
4✔
761
            if (!schema) {
4!
762
                schema = currentSchema
×
763
            }
×
764

4✔
765
            const deleteQuery = this.deleteTypeormMetadataSql({
4✔
766
                schema: schema,
4✔
767
                table: table.name,
4✔
768
                type: MetadataTableType.GENERATED_COLUMN,
4✔
769
                name: column.name,
4✔
770
            })
4✔
771

4✔
772
            const insertQuery = this.insertTypeormMetadataSql({
4✔
773
                schema: schema,
4✔
774
                table: table.name,
4✔
775
                type: MetadataTableType.GENERATED_COLUMN,
4✔
776
                name: column.name,
4✔
777
                value: column.asExpression,
4✔
778
            })
4✔
779

4✔
780
            upQueries.push(deleteQuery)
4✔
781
            downQueries.push(insertQuery)
4✔
782
        }
4✔
783

11✔
784
        await this.executeQueries(upQueries, downQueries)
11✔
785
    }
11✔
786

26✔
787
    /**
26✔
788
     * Creates a new view.
26✔
789
     */
26✔
790
    async createView(
26✔
791
        view: View,
6✔
792
        syncWithMetadata: boolean = false,
6✔
793
    ): Promise<void> {
6✔
794
        const upQueries: Query[] = []
6✔
795
        const downQueries: Query[] = []
6✔
796
        upQueries.push(this.createViewSql(view))
6✔
797
        if (syncWithMetadata)
6✔
798
            upQueries.push(await this.insertViewDefinitionSql(view))
6✔
799
        downQueries.push(this.dropViewSql(view))
6✔
800
        if (syncWithMetadata)
6✔
801
            downQueries.push(await this.deleteViewDefinitionSql(view))
6✔
802
        await this.executeQueries(upQueries, downQueries)
6✔
803
    }
6✔
804

26✔
805
    /**
26✔
806
     * Drops the view.
26✔
807
     */
26✔
808
    async dropView(target: View | string): Promise<void> {
26✔
809
        const viewName = InstanceChecker.isView(target) ? target.name : target
×
810
        const view = await this.getCachedView(viewName)
×
811

×
812
        const upQueries: Query[] = []
×
813
        const downQueries: Query[] = []
×
814
        upQueries.push(await this.deleteViewDefinitionSql(view))
×
815
        upQueries.push(this.dropViewSql(view))
×
816
        downQueries.push(await this.insertViewDefinitionSql(view))
×
817
        downQueries.push(this.createViewSql(view))
×
818
        await this.executeQueries(upQueries, downQueries)
×
819
    }
×
820

26✔
821
    /**
26✔
822
     * Renames the given table.
26✔
823
     */
26✔
824
    async renameTable(
26✔
825
        oldTableOrName: Table | string,
13✔
826
        newTableName: string,
13✔
827
    ): Promise<void> {
13✔
828
        const upQueries: Query[] = []
13✔
829
        const downQueries: Query[] = []
13✔
830
        const oldTable = InstanceChecker.isTable(oldTableOrName)
13✔
831
            ? oldTableOrName
13✔
832
            : await this.getCachedTable(oldTableOrName)
13✔
833
        const newTable = oldTable.clone()
12✔
834

12✔
835
        const { schema: schemaName, tableName: oldTableName } =
12✔
836
            this.driver.parseTableName(oldTable)
12✔
837

12✔
838
        newTable.name = schemaName
12✔
839
            ? `${schemaName}.${newTableName}`
13✔
840
            : newTableName
13!
841

13✔
842
        upQueries.push(
13✔
843
            new Query(
13✔
844
                `ALTER TABLE ${this.escapePath(
13✔
845
                    oldTable,
13✔
846
                )} RENAME TO "${newTableName}"`,
13✔
847
            ),
13✔
848
        )
13✔
849
        downQueries.push(
13✔
850
            new Query(
13✔
851
                `ALTER TABLE ${this.escapePath(
13✔
852
                    newTable,
13✔
853
                )} RENAME TO "${oldTableName}"`,
13✔
854
            ),
13✔
855
        )
13✔
856

13✔
857
        // rename column primary key constraint
13✔
858
        if (
13✔
859
            newTable.primaryColumns.length > 0 &&
13✔
860
            !newTable.primaryColumns[0].primaryKeyConstraintName
13✔
861
        ) {
13✔
862
            const columnNames = newTable.primaryColumns.map(
9✔
863
                (column) => column.name,
9✔
864
            )
9✔
865

9✔
866
            const oldPkName = this.connection.namingStrategy.primaryKeyName(
9✔
867
                oldTable,
9✔
868
                columnNames,
9✔
869
            )
9✔
870
            const newPkName = this.connection.namingStrategy.primaryKeyName(
9✔
871
                newTable,
9✔
872
                columnNames,
9✔
873
            )
9✔
874

9✔
875
            upQueries.push(
9✔
876
                new Query(
9✔
877
                    `ALTER TABLE ${this.escapePath(
9✔
878
                        newTable,
9✔
879
                    )} RENAME CONSTRAINT "${oldPkName}" TO "${newPkName}"`,
9✔
880
                ),
9✔
881
            )
9✔
882
            downQueries.push(
9✔
883
                new Query(
9✔
884
                    `ALTER TABLE ${this.escapePath(
9✔
885
                        newTable,
9✔
886
                    )} RENAME CONSTRAINT "${newPkName}" TO "${oldPkName}"`,
9✔
887
                ),
9✔
888
            )
9✔
889
        }
9✔
890

13✔
891
        // rename unique constraints
13✔
892
        newTable.uniques.forEach((unique) => {
13✔
893
            const oldUniqueName =
4✔
894
                this.connection.namingStrategy.uniqueConstraintName(
4✔
895
                    oldTable,
4✔
896
                    unique.columnNames,
4✔
897
                )
4✔
898

4✔
899
            // Skip renaming if Unique has user defined constraint name
4✔
900
            if (unique.name !== oldUniqueName) return
4✔
901

×
902
            // build new constraint name
×
903
            const newUniqueName =
×
904
                this.connection.namingStrategy.uniqueConstraintName(
×
905
                    newTable,
×
906
                    unique.columnNames,
×
907
                )
×
908

×
909
            // build queries
×
910
            upQueries.push(
×
911
                new Query(
×
912
                    `ALTER TABLE ${this.escapePath(
×
913
                        newTable,
×
914
                    )} RENAME CONSTRAINT "${
×
915
                        unique.name
×
916
                    }" TO "${newUniqueName}"`,
×
917
                ),
×
918
            )
×
919
            downQueries.push(
×
920
                new Query(
×
921
                    `ALTER TABLE ${this.escapePath(
×
922
                        newTable,
×
923
                    )} RENAME CONSTRAINT "${newUniqueName}" TO "${
×
924
                        unique.name
×
925
                    }"`,
×
926
                ),
×
927
            )
×
928

×
929
            // replace constraint name
×
930
            unique.name = newUniqueName
×
931
        })
13✔
932

13✔
933
        // rename index constraints
13✔
934
        newTable.indices.forEach((index) => {
13✔
935
            const oldIndexName = this.connection.namingStrategy.indexName(
12✔
936
                oldTable,
12✔
937
                index.columnNames,
12✔
938
                index.where,
12✔
939
            )
12✔
940

12✔
941
            // Skip renaming if Index has user defined constraint name
12✔
942
            if (index.name !== oldIndexName) return
12✔
943

8✔
944
            // build new constraint name
8✔
945
            const { schema } = this.driver.parseTableName(newTable)
8✔
946
            const newIndexName = this.connection.namingStrategy.indexName(
8✔
947
                newTable,
8✔
948
                index.columnNames,
8✔
949
                index.where,
8✔
950
            )
8✔
951

8✔
952
            // build queries
8✔
953
            const up = schema
8✔
954
                ? `ALTER INDEX "${schema}"."${index.name}" RENAME TO "${newIndexName}"`
8✔
955
                : `ALTER INDEX "${index.name}" RENAME TO "${newIndexName}"`
12!
956
            const down = schema
12✔
957
                ? `ALTER INDEX "${schema}"."${newIndexName}" RENAME TO "${index.name}"`
12✔
958
                : `ALTER INDEX "${newIndexName}" RENAME TO "${index.name}"`
12!
959
            upQueries.push(new Query(up))
12✔
960
            downQueries.push(new Query(down))
12✔
961

12✔
962
            // replace constraint name
12✔
963
            index.name = newIndexName
12✔
964
        })
13✔
965

13✔
966
        // rename foreign key constraints
13✔
967
        newTable.foreignKeys.forEach((foreignKey) => {
13✔
968
            const oldForeignKeyName =
8✔
969
                this.connection.namingStrategy.foreignKeyName(
8✔
970
                    oldTable,
8✔
971
                    foreignKey.columnNames,
8✔
972
                    this.getTablePath(foreignKey),
8✔
973
                    foreignKey.referencedColumnNames,
8✔
974
                )
8✔
975

8✔
976
            // Skip renaming if foreign key has user defined constraint name
8✔
977
            if (foreignKey.name !== oldForeignKeyName) return
8✔
978

×
979
            // build new constraint name
×
980
            const newForeignKeyName =
×
981
                this.connection.namingStrategy.foreignKeyName(
×
982
                    newTable,
×
983
                    foreignKey.columnNames,
×
984
                    this.getTablePath(foreignKey),
×
985
                    foreignKey.referencedColumnNames,
×
986
                )
×
987

×
988
            // build queries
×
989
            upQueries.push(
×
990
                new Query(
×
991
                    `ALTER TABLE ${this.escapePath(
×
992
                        newTable,
×
993
                    )} RENAME CONSTRAINT "${
×
994
                        foreignKey.name
×
995
                    }" TO "${newForeignKeyName}"`,
×
996
                ),
×
997
            )
×
998
            downQueries.push(
×
999
                new Query(
×
1000
                    `ALTER TABLE ${this.escapePath(
×
1001
                        newTable,
×
1002
                    )} RENAME CONSTRAINT "${newForeignKeyName}" TO "${
×
1003
                        foreignKey.name
×
1004
                    }"`,
×
1005
                ),
×
1006
            )
×
1007

×
1008
            // replace constraint name
×
1009
            foreignKey.name = newForeignKeyName
×
1010
        })
13✔
1011

13✔
1012
        // rename ENUM types
13✔
1013
        const enumColumns = newTable.columns.filter(
13✔
1014
            (column) => column.type === "enum" || column.type === "simple-enum",
13✔
1015
        )
13✔
1016
        for (const column of enumColumns) {
13✔
1017
            // skip renaming for user-defined enum name
4✔
1018
            if (column.enumName) continue
4✔
1019

3✔
1020
            const oldEnumType = await this.getUserDefinedTypeName(
3✔
1021
                oldTable,
3✔
1022
                column,
3✔
1023
            )
3✔
1024
            upQueries.push(
3✔
1025
                new Query(
3✔
1026
                    `ALTER TYPE "${oldEnumType.schema}"."${
3✔
1027
                        oldEnumType.name
3✔
1028
                    }" RENAME TO ${this.buildEnumName(
3✔
1029
                        newTable,
3✔
1030
                        column,
3✔
1031
                        false,
3✔
1032
                    )}`,
3✔
1033
                ),
3✔
1034
            )
3✔
1035
            downQueries.push(
3✔
1036
                new Query(
3✔
1037
                    `ALTER TYPE ${this.buildEnumName(
3✔
1038
                        newTable,
3✔
1039
                        column,
3✔
1040
                    )} RENAME TO "${oldEnumType.name}"`,
3✔
1041
                ),
3✔
1042
            )
3✔
1043
        }
3✔
1044

13✔
1045
        await this.executeQueries(upQueries, downQueries)
13✔
1046
    }
13✔
1047

26✔
1048
    /**
26✔
1049
     * Creates a new column from the column in the table.
26✔
1050
     */
26✔
1051
    async addColumn(
26✔
1052
        tableOrName: Table | string,
21✔
1053
        column: TableColumn,
21✔
1054
    ): Promise<void> {
21✔
1055
        const table = InstanceChecker.isTable(tableOrName)
21✔
1056
            ? tableOrName
21✔
1057
            : await this.getCachedTable(tableOrName)
21✔
1058
        const clonedTable = table.clone()
3✔
1059
        const upQueries: Query[] = []
3✔
1060
        const downQueries: Query[] = []
3✔
1061

3✔
1062
        if (column.generationStrategy === "increment") {
21!
1063
            throw new TypeORMError(
×
1064
                `Adding sequential generated columns into existing table is not supported`,
×
1065
            )
×
1066
        }
×
1067

21✔
1068
        if (column.type === "enum" || column.type === "simple-enum") {
21✔
1069
            const hasEnum = await this.hasEnumType(table, column)
3✔
1070
            if (!hasEnum) {
3✔
1071
                upQueries.push(this.createEnumTypeSql(table, column))
3✔
1072
                downQueries.push(this.dropEnumTypeSql(table, column))
3✔
1073
            }
3✔
1074
        }
3✔
1075

21✔
1076
        upQueries.push(
21✔
1077
            new Query(
21✔
1078
                `ALTER TABLE ${this.escapePath(
21✔
1079
                    table,
21✔
1080
                )} ADD ${this.buildCreateColumnSql(table, column)}`,
21✔
1081
            ),
21✔
1082
        )
21✔
1083
        downQueries.push(
21✔
1084
            new Query(
21✔
1085
                `ALTER TABLE ${this.escapePath(table)} DROP COLUMN "${
21✔
1086
                    column.name
21✔
1087
                }"`,
21✔
1088
            ),
21✔
1089
        )
21✔
1090

21✔
1091
        // create or update primary key constraint
21✔
1092
        if (column.isPrimary) {
21!
1093
            const primaryColumns = clonedTable.primaryColumns
×
1094
            // if table already have primary key, me must drop it and recreate again
×
1095
            // todo: https://go.crdb.dev/issue-v/48026/v21.1
×
1096
            if (primaryColumns.length > 0) {
×
1097
                const pkName = primaryColumns[0].primaryKeyConstraintName
×
1098
                    ? primaryColumns[0].primaryKeyConstraintName
×
1099
                    : this.connection.namingStrategy.primaryKeyName(
×
1100
                          clonedTable,
×
1101
                          primaryColumns.map((column) => column.name),
×
1102
                      )
×
1103

×
1104
                const columnNames = primaryColumns
×
1105
                    .map((column) => `"${column.name}"`)
×
1106
                    .join(", ")
×
1107
                upQueries.push(
×
1108
                    new Query(
×
1109
                        `ALTER TABLE ${this.escapePath(
×
1110
                            table,
×
1111
                        )} DROP CONSTRAINT "${pkName}"`,
×
1112
                    ),
×
1113
                )
×
1114
                downQueries.push(
×
1115
                    new Query(
×
1116
                        `ALTER TABLE ${this.escapePath(
×
1117
                            table,
×
1118
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
×
1119
                    ),
×
1120
                )
×
1121
            }
×
1122

×
1123
            primaryColumns.push(column)
×
1124
            const pkName = primaryColumns[0].primaryKeyConstraintName
×
1125
                ? primaryColumns[0].primaryKeyConstraintName
×
1126
                : this.connection.namingStrategy.primaryKeyName(
×
1127
                      clonedTable,
×
1128
                      primaryColumns.map((column) => column.name),
×
1129
                  )
×
1130

×
1131
            const columnNames = primaryColumns
×
1132
                .map((column) => `"${column.name}"`)
×
1133
                .join(", ")
×
1134
            upQueries.push(
×
1135
                new Query(
×
1136
                    `ALTER TABLE ${this.escapePath(
×
1137
                        table,
×
1138
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
×
1139
                ),
×
1140
            )
×
1141
            downQueries.push(
×
1142
                new Query(
×
1143
                    `ALTER TABLE ${this.escapePath(
×
1144
                        table,
×
1145
                    )} DROP CONSTRAINT "${pkName}"`,
×
1146
                ),
×
1147
            )
×
1148
        }
×
1149

21✔
1150
        if (column.generatedType && column.asExpression) {
21✔
1151
            const currentSchema = await this.getCurrentSchema()
3✔
1152
            let { schema } = this.driver.parseTableName(table)
3✔
1153
            if (!schema) {
3!
1154
                schema = currentSchema
×
1155
            }
×
1156
            const insertQuery = this.insertTypeormMetadataSql({
3✔
1157
                schema: schema,
3✔
1158
                table: table.name,
3✔
1159
                type: MetadataTableType.GENERATED_COLUMN,
3✔
1160
                name: column.name,
3✔
1161
                value: column.asExpression,
3✔
1162
            })
3✔
1163

3✔
1164
            const deleteQuery = this.deleteTypeormMetadataSql({
3✔
1165
                schema: schema,
3✔
1166
                table: table.name,
3✔
1167
                type: MetadataTableType.GENERATED_COLUMN,
3✔
1168
                name: column.name,
3✔
1169
            })
3✔
1170

3✔
1171
            upQueries.push(insertQuery)
3✔
1172
            downQueries.push(deleteQuery)
3✔
1173
        }
3✔
1174

21✔
1175
        // create column index
21✔
1176
        const columnIndex = clonedTable.indices.find(
21✔
1177
            (index) =>
21✔
1178
                index.columnNames.length === 1 &&
×
1179
                index.columnNames[0] === column.name,
21✔
1180
        )
21✔
1181
        if (columnIndex) {
21!
1182
            // CockroachDB stores unique indices as UNIQUE constraints
×
1183
            if (columnIndex.isUnique) {
×
1184
                const unique = new TableUnique({
×
1185
                    name: this.connection.namingStrategy.uniqueConstraintName(
×
1186
                        table,
×
1187
                        columnIndex.columnNames,
×
1188
                    ),
×
1189
                    columnNames: columnIndex.columnNames,
×
1190
                })
×
1191
                upQueries.push(this.createUniqueConstraintSql(table, unique))
×
1192
                downQueries.push(this.dropIndexSql(table, unique))
×
1193
                clonedTable.uniques.push(unique)
×
1194
            } else {
×
1195
                upQueries.push(this.createIndexSql(table, columnIndex))
×
1196
                downQueries.push(this.dropIndexSql(table, columnIndex))
×
1197
            }
×
1198
        }
×
1199

21✔
1200
        // create unique constraint
21✔
1201
        if (column.isUnique) {
21✔
1202
            const uniqueConstraint = new TableUnique({
2✔
1203
                name: this.connection.namingStrategy.uniqueConstraintName(
2✔
1204
                    table,
2✔
1205
                    [column.name],
2✔
1206
                ),
2✔
1207
                columnNames: [column.name],
2✔
1208
            })
2✔
1209
            clonedTable.uniques.push(uniqueConstraint)
2✔
1210
            upQueries.push(
2✔
1211
                this.createUniqueConstraintSql(table, uniqueConstraint),
2✔
1212
            )
2✔
1213
            downQueries.push(this.dropIndexSql(table, uniqueConstraint.name!)) // CockroachDB creates indices for unique constraints
2✔
1214
        }
2✔
1215

21✔
1216
        // create column's comment
21✔
1217
        if (column.comment) {
21!
1218
            upQueries.push(
×
1219
                new Query(
×
1220
                    `COMMENT ON COLUMN ${this.escapePath(table)}."${
×
1221
                        column.name
×
1222
                    }" IS ${this.escapeComment(column.comment)}`,
×
1223
                ),
×
1224
            )
×
1225
            downQueries.push(
×
1226
                new Query(
×
1227
                    `COMMENT ON COLUMN ${this.escapePath(table)}."${
×
1228
                        column.name
×
1229
                    }" IS ${this.escapeComment(column.comment)}`,
×
1230
                ),
×
1231
            )
×
1232
        }
×
1233

21✔
1234
        await this.executeQueries(upQueries, downQueries)
21✔
1235

20✔
1236
        clonedTable.addColumn(column)
20✔
1237
        this.replaceCachedTable(table, clonedTable)
20✔
1238
    }
20✔
1239

26✔
1240
    /**
26✔
1241
     * Creates a new columns from the column in the table.
26✔
1242
     */
26✔
1243
    async addColumns(
26✔
1244
        tableOrName: Table | string,
4✔
1245
        columns: TableColumn[],
4✔
1246
    ): Promise<void> {
4✔
1247
        for (const column of columns) {
4✔
1248
            await this.addColumn(tableOrName, column)
5✔
1249
        }
5✔
1250
    }
4✔
1251

26✔
1252
    /**
26✔
1253
     * Renames column in the given table.
26✔
1254
     */
26✔
1255
    async renameColumn(
26✔
1256
        tableOrName: Table | string,
13✔
1257
        oldTableColumnOrName: TableColumn | string,
13✔
1258
        newTableColumnOrName: TableColumn | string,
13✔
1259
    ): Promise<void> {
13✔
1260
        const table = InstanceChecker.isTable(tableOrName)
13✔
1261
            ? tableOrName
13✔
1262
            : await this.getCachedTable(tableOrName)
13✔
1263
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
2✔
1264
            ? oldTableColumnOrName
13✔
1265
            : table.columns.find((c) => c.name === oldTableColumnOrName)
13✔
1266
        if (!oldColumn)
13✔
1267
            throw new TypeORMError(
13!
1268
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
×
1269
            )
×
1270

13✔
1271
        let newColumn
13✔
1272
        if (InstanceChecker.isTableColumn(newTableColumnOrName)) {
13✔
1273
            newColumn = newTableColumnOrName
8✔
1274
        } else {
13✔
1275
            newColumn = oldColumn.clone()
5✔
1276
            newColumn.name = newTableColumnOrName
5✔
1277
        }
5✔
1278

13✔
1279
        return this.changeColumn(table, oldColumn, newColumn)
13✔
1280
    }
13✔
1281

26✔
1282
    /**
26✔
1283
     * Changes a column in the table.
26✔
1284
     */
26✔
1285
    async changeColumn(
26✔
1286
        tableOrName: Table | string,
86✔
1287
        oldTableColumnOrName: TableColumn | string,
86✔
1288
        newColumn: TableColumn,
86✔
1289
    ): Promise<void> {
86✔
1290
        const table = InstanceChecker.isTable(tableOrName)
86✔
1291
            ? tableOrName
86✔
1292
            : await this.getCachedTable(tableOrName)
86!
1293
        let clonedTable = table.clone()
×
1294
        const upQueries: Query[] = []
×
1295
        const downQueries: Query[] = []
×
1296
        let defaultValueChanged = false
×
1297

×
1298
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
×
1299
            ? oldTableColumnOrName
86✔
1300
            : table.columns.find(
86!
1301
                  (column) => column.name === oldTableColumnOrName,
×
1302
              )
86✔
1303
        if (!oldColumn)
86✔
1304
            throw new TypeORMError(
86!
1305
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
×
1306
            )
×
1307

86✔
1308
        if (
86✔
1309
            oldColumn.type !== newColumn.type ||
86✔
1310
            oldColumn.length !== newColumn.length ||
86✔
1311
            newColumn.isArray !== oldColumn.isArray ||
86✔
1312
            oldColumn.generatedType !== newColumn.generatedType ||
86✔
1313
            oldColumn.asExpression !== newColumn.asExpression
77✔
1314
        ) {
86✔
1315
            // To avoid data conversion, we just recreate column
10✔
1316
            await this.dropColumn(table, oldColumn)
10✔
1317
            await this.addColumn(table, newColumn)
10✔
1318

10✔
1319
            // update cloned table
10✔
1320
            clonedTable = table.clone()
10✔
1321
        } else {
86✔
1322
            if (oldColumn.name !== newColumn.name) {
76✔
1323
                // rename column
22✔
1324
                upQueries.push(
22✔
1325
                    new Query(
22✔
1326
                        `ALTER TABLE ${this.escapePath(table)} RENAME COLUMN "${
22✔
1327
                            oldColumn.name
22✔
1328
                        }" TO "${newColumn.name}"`,
22✔
1329
                    ),
22✔
1330
                )
22✔
1331
                downQueries.push(
22✔
1332
                    new Query(
22✔
1333
                        `ALTER TABLE ${this.escapePath(table)} RENAME COLUMN "${
22✔
1334
                            newColumn.name
22✔
1335
                        }" TO "${oldColumn.name}"`,
22✔
1336
                    ),
22✔
1337
                )
22✔
1338

22✔
1339
                // rename ENUM type
22✔
1340
                if (
22✔
1341
                    oldColumn.type === "enum" ||
22✔
1342
                    oldColumn.type === "simple-enum"
21✔
1343
                ) {
22✔
1344
                    const oldEnumType = await this.getUserDefinedTypeName(
1✔
1345
                        table,
1✔
1346
                        oldColumn,
1✔
1347
                    )
1✔
1348
                    upQueries.push(
1✔
1349
                        new Query(
1✔
1350
                            `ALTER TYPE "${oldEnumType.schema}"."${
1✔
1351
                                oldEnumType.name
1✔
1352
                            }" RENAME TO ${this.buildEnumName(
1✔
1353
                                table,
1✔
1354
                                newColumn,
1✔
1355
                                false,
1✔
1356
                            )}`,
1✔
1357
                        ),
1✔
1358
                    )
1✔
1359
                    downQueries.push(
1✔
1360
                        new Query(
1✔
1361
                            `ALTER TYPE ${this.buildEnumName(
1✔
1362
                                table,
1✔
1363
                                newColumn,
1✔
1364
                            )} RENAME TO "${oldEnumType.name}"`,
1✔
1365
                        ),
1✔
1366
                    )
1✔
1367
                }
1✔
1368

22✔
1369
                // rename column primary key constraint
22✔
1370
                if (
22✔
1371
                    oldColumn.isPrimary === true &&
22✔
1372
                    !oldColumn.primaryKeyConstraintName
7✔
1373
                ) {
22✔
1374
                    const primaryColumns = clonedTable.primaryColumns
5✔
1375

5✔
1376
                    // build old primary constraint name
5✔
1377
                    const columnNames = primaryColumns.map(
5✔
1378
                        (column) => column.name,
5✔
1379
                    )
5✔
1380
                    const oldPkName =
5✔
1381
                        this.connection.namingStrategy.primaryKeyName(
5✔
1382
                            clonedTable,
5✔
1383
                            columnNames,
5✔
1384
                        )
5✔
1385

5✔
1386
                    // replace old column name with new column name
5✔
1387
                    columnNames.splice(columnNames.indexOf(oldColumn.name), 1)
5✔
1388
                    columnNames.push(newColumn.name)
5✔
1389

5✔
1390
                    // build new primary constraint name
5✔
1391
                    const newPkName =
5✔
1392
                        this.connection.namingStrategy.primaryKeyName(
5✔
1393
                            clonedTable,
5✔
1394
                            columnNames,
5✔
1395
                        )
5✔
1396

5✔
1397
                    upQueries.push(
5✔
1398
                        new Query(
5✔
1399
                            `ALTER TABLE ${this.escapePath(
5✔
1400
                                table,
5✔
1401
                            )} RENAME CONSTRAINT "${oldPkName}" TO "${newPkName}"`,
5✔
1402
                        ),
5✔
1403
                    )
5✔
1404
                    downQueries.push(
5✔
1405
                        new Query(
5✔
1406
                            `ALTER TABLE ${this.escapePath(
5✔
1407
                                table,
5✔
1408
                            )} RENAME CONSTRAINT "${newPkName}" TO "${oldPkName}"`,
5✔
1409
                        ),
5✔
1410
                    )
5✔
1411
                }
5✔
1412

22✔
1413
                // rename unique constraints
22✔
1414
                clonedTable.findColumnUniques(oldColumn).forEach((unique) => {
22✔
1415
                    const oldUniqueName =
7✔
1416
                        this.connection.namingStrategy.uniqueConstraintName(
7✔
1417
                            clonedTable,
7✔
1418
                            unique.columnNames,
7✔
1419
                        )
7✔
1420

7✔
1421
                    // Skip renaming if Unique has user defined constraint name
7✔
1422
                    if (unique.name !== oldUniqueName) return
7✔
1423

3✔
1424
                    // build new constraint name
3✔
1425
                    unique.columnNames.splice(
3✔
1426
                        unique.columnNames.indexOf(oldColumn.name),
3✔
1427
                        1,
3✔
1428
                    )
3✔
1429
                    unique.columnNames.push(newColumn.name)
3✔
1430
                    const newUniqueName =
3✔
1431
                        this.connection.namingStrategy.uniqueConstraintName(
3✔
1432
                            clonedTable,
3✔
1433
                            unique.columnNames,
3✔
1434
                        )
3✔
1435

3✔
1436
                    // build queries
3✔
1437
                    upQueries.push(
3✔
1438
                        new Query(
3✔
1439
                            `ALTER TABLE ${this.escapePath(
3✔
1440
                                table,
3✔
1441
                            )} RENAME CONSTRAINT "${
3✔
1442
                                unique.name
3✔
1443
                            }" TO "${newUniqueName}"`,
3✔
1444
                        ),
3✔
1445
                    )
3✔
1446
                    downQueries.push(
3✔
1447
                        new Query(
3✔
1448
                            `ALTER TABLE ${this.escapePath(
3✔
1449
                                table,
3✔
1450
                            )} RENAME CONSTRAINT "${newUniqueName}" TO "${
3✔
1451
                                unique.name
3✔
1452
                            }"`,
3✔
1453
                        ),
3✔
1454
                    )
3✔
1455

3✔
1456
                    // replace constraint name
3✔
1457
                    unique.name = newUniqueName
3✔
1458
                })
22✔
1459

22✔
1460
                // rename index constraints
22✔
1461
                clonedTable.findColumnIndices(oldColumn).forEach((index) => {
22✔
1462
                    const oldIndexName =
11✔
1463
                        this.connection.namingStrategy.indexName(
11✔
1464
                            clonedTable,
11✔
1465
                            index.columnNames,
11✔
1466
                            index.where,
11✔
1467
                        )
11✔
1468

11✔
1469
                    // Skip renaming if Index has user defined constraint name
11✔
1470
                    if (index.name !== oldIndexName) return
11✔
1471

9✔
1472
                    // build new constraint name
9✔
1473
                    index.columnNames.splice(
9✔
1474
                        index.columnNames.indexOf(oldColumn.name),
9✔
1475
                        1,
9✔
1476
                    )
9✔
1477
                    index.columnNames.push(newColumn.name)
9✔
1478
                    const { schema } = this.driver.parseTableName(table)
9✔
1479
                    const newIndexName =
9✔
1480
                        this.connection.namingStrategy.indexName(
9✔
1481
                            clonedTable,
9✔
1482
                            index.columnNames,
9✔
1483
                            index.where,
9✔
1484
                        )
9✔
1485

9✔
1486
                    // build queries
9✔
1487
                    const up = schema
9✔
1488
                        ? `ALTER INDEX "${schema}"."${index.name}" RENAME TO "${newIndexName}"`
9✔
1489
                        : `ALTER INDEX "${index.name}" RENAME TO "${newIndexName}"`
11!
1490
                    const down = schema
11✔
1491
                        ? `ALTER INDEX "${schema}"."${newIndexName}" RENAME TO "${index.name}"`
11✔
1492
                        : `ALTER INDEX "${newIndexName}" RENAME TO "${index.name}"`
11!
1493
                    upQueries.push(new Query(up))
11✔
1494
                    downQueries.push(new Query(down))
11✔
1495

11✔
1496
                    // replace constraint name
11✔
1497
                    index.name = newIndexName
11✔
1498
                })
22✔
1499

22✔
1500
                // rename foreign key constraints
22✔
1501
                clonedTable
22✔
1502
                    .findColumnForeignKeys(oldColumn)
22✔
1503
                    .forEach((foreignKey) => {
22✔
1504
                        const foreignKeyName =
9✔
1505
                            this.connection.namingStrategy.foreignKeyName(
9✔
1506
                                clonedTable,
9✔
1507
                                foreignKey.columnNames,
9✔
1508
                                this.getTablePath(foreignKey),
9✔
1509
                                foreignKey.referencedColumnNames,
9✔
1510
                            )
9✔
1511

9✔
1512
                        // Skip renaming if foreign key has user defined constraint name
9✔
1513
                        if (foreignKey.name !== foreignKeyName) return
9✔
1514

1✔
1515
                        // build new constraint name
1✔
1516
                        foreignKey.columnNames.splice(
1✔
1517
                            foreignKey.columnNames.indexOf(oldColumn.name),
1✔
1518
                            1,
1✔
1519
                        )
1✔
1520
                        foreignKey.columnNames.push(newColumn.name)
1✔
1521
                        const newForeignKeyName =
1✔
1522
                            this.connection.namingStrategy.foreignKeyName(
1✔
1523
                                clonedTable,
1✔
1524
                                foreignKey.columnNames,
1✔
1525
                                this.getTablePath(foreignKey),
1✔
1526
                                foreignKey.referencedColumnNames,
1✔
1527
                            )
1✔
1528

1✔
1529
                        // build queries
1✔
1530
                        upQueries.push(
1✔
1531
                            new Query(
1✔
1532
                                `ALTER TABLE ${this.escapePath(
1✔
1533
                                    table,
1✔
1534
                                )} RENAME CONSTRAINT "${
1✔
1535
                                    foreignKey.name
1✔
1536
                                }" TO "${newForeignKeyName}"`,
1✔
1537
                            ),
1✔
1538
                        )
1✔
1539
                        downQueries.push(
1✔
1540
                            new Query(
1✔
1541
                                `ALTER TABLE ${this.escapePath(
1✔
1542
                                    table,
1✔
1543
                                )} RENAME CONSTRAINT "${newForeignKeyName}" TO "${
1✔
1544
                                    foreignKey.name
1✔
1545
                                }"`,
1✔
1546
                            ),
1✔
1547
                        )
1✔
1548

1✔
1549
                        // replace constraint name
1✔
1550
                        foreignKey.name = newForeignKeyName
1✔
1551
                    })
22✔
1552

22✔
1553
                // rename old column in the Table object
22✔
1554
                const oldTableColumn = clonedTable.columns.find(
22✔
1555
                    (column) => column.name === oldColumn.name,
22✔
1556
                )
22✔
1557
                clonedTable.columns[
22✔
1558
                    clonedTable.columns.indexOf(oldTableColumn!)
22✔
1559
                ].name = newColumn.name
22✔
1560
                oldColumn.name = newColumn.name
22✔
1561
            }
22✔
1562

76✔
1563
            if (
76✔
1564
                newColumn.precision !== oldColumn.precision ||
76✔
1565
                newColumn.scale !== oldColumn.scale
76✔
1566
            ) {
76!
1567
                upQueries.push(
×
1568
                    new Query(
×
1569
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
×
1570
                            newColumn.name
×
1571
                        }" TYPE ${this.driver.createFullType(newColumn)}`,
×
1572
                    ),
×
1573
                )
×
1574
                downQueries.push(
×
1575
                    new Query(
×
1576
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
×
1577
                            newColumn.name
×
1578
                        }" TYPE ${this.driver.createFullType(oldColumn)}`,
×
1579
                    ),
×
1580
                )
×
1581
            }
×
1582

76✔
1583
            if (oldColumn.isNullable !== newColumn.isNullable) {
76✔
1584
                if (newColumn.isNullable) {
2✔
1585
                    upQueries.push(
1✔
1586
                        new Query(
1✔
1587
                            `ALTER TABLE ${this.escapePath(
1✔
1588
                                table,
1✔
1589
                            )} ALTER COLUMN "${oldColumn.name}" DROP NOT NULL`,
1✔
1590
                        ),
1✔
1591
                    )
1✔
1592
                    downQueries.push(
1✔
1593
                        new Query(
1✔
1594
                            `ALTER TABLE ${this.escapePath(
1✔
1595
                                table,
1✔
1596
                            )} ALTER COLUMN "${oldColumn.name}" SET NOT NULL`,
1✔
1597
                        ),
1✔
1598
                    )
1✔
1599
                } else {
1✔
1600
                    upQueries.push(
1✔
1601
                        new Query(
1✔
1602
                            `ALTER TABLE ${this.escapePath(
1✔
1603
                                table,
1✔
1604
                            )} ALTER COLUMN "${oldColumn.name}" SET NOT NULL`,
1✔
1605
                        ),
1✔
1606
                    )
1✔
1607
                    downQueries.push(
1✔
1608
                        new Query(
1✔
1609
                            `ALTER TABLE ${this.escapePath(
1✔
1610
                                table,
1✔
1611
                            )} ALTER COLUMN "${oldColumn.name}" DROP NOT NULL`,
1✔
1612
                        ),
1✔
1613
                    )
1✔
1614
                }
1✔
1615
            }
2✔
1616

76✔
1617
            if (oldColumn.comment !== newColumn.comment) {
76✔
1618
                upQueries.push(
5✔
1619
                    new Query(
5✔
1620
                        `COMMENT ON COLUMN ${this.escapePath(table)}."${
5✔
1621
                            oldColumn.name
5✔
1622
                        }" IS ${this.escapeComment(newColumn.comment)}`,
5✔
1623
                    ),
5✔
1624
                )
5✔
1625
                downQueries.push(
5✔
1626
                    new Query(
5✔
1627
                        `COMMENT ON COLUMN ${this.escapePath(table)}."${
5✔
1628
                            newColumn.name
5✔
1629
                        }" IS ${this.escapeComment(oldColumn.comment)}`,
5✔
1630
                    ),
5✔
1631
                )
5✔
1632
            }
5✔
1633

76✔
1634
            if (newColumn.isPrimary !== oldColumn.isPrimary) {
76!
1635
                const primaryColumns = clonedTable.primaryColumns
×
1636

×
1637
                // if primary column state changed, we must always drop existed constraint.
×
1638
                if (primaryColumns.length > 0) {
×
1639
                    const pkName = primaryColumns[0].primaryKeyConstraintName
×
1640
                        ? primaryColumns[0].primaryKeyConstraintName
×
1641
                        : this.connection.namingStrategy.primaryKeyName(
×
1642
                              clonedTable,
×
1643
                              primaryColumns.map((column) => column.name),
×
1644
                          )
×
1645

×
1646
                    const columnNames = primaryColumns
×
1647
                        .map((column) => `"${column.name}"`)
×
1648
                        .join(", ")
×
1649

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

×
1666
                if (newColumn.isPrimary === true) {
×
1667
                    primaryColumns.push(newColumn)
×
1668
                    // update column in table
×
1669
                    const column = clonedTable.columns.find(
×
1670
                        (column) => column.name === newColumn.name,
×
1671
                    )
×
1672
                    column!.isPrimary = true
×
1673
                    const pkName = primaryColumns[0].primaryKeyConstraintName
×
1674
                        ? primaryColumns[0].primaryKeyConstraintName
×
1675
                        : this.connection.namingStrategy.primaryKeyName(
×
1676
                              clonedTable,
×
1677
                              primaryColumns.map((column) => column.name),
×
1678
                          )
×
1679

×
1680
                    const columnNames = primaryColumns
×
1681
                        .map((column) => `"${column.name}"`)
×
1682
                        .join(", ")
×
1683

×
1684
                    upQueries.push(
×
1685
                        new Query(
×
1686
                            `ALTER TABLE ${this.escapePath(
×
1687
                                table,
×
1688
                            )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
×
1689
                        ),
×
1690
                    )
×
1691
                    downQueries.push(
×
1692
                        new Query(
×
1693
                            `ALTER TABLE ${this.escapePath(
×
1694
                                table,
×
1695
                            )} DROP CONSTRAINT "${pkName}"`,
×
1696
                        ),
×
1697
                    )
×
1698
                } else {
×
1699
                    const primaryColumn = primaryColumns.find(
×
1700
                        (c) => c.name === newColumn.name,
×
1701
                    )
×
1702
                    primaryColumns.splice(
×
1703
                        primaryColumns.indexOf(primaryColumn!),
×
1704
                        1,
×
1705
                    )
×
1706

×
1707
                    // update column in table
×
1708
                    const column = clonedTable.columns.find(
×
1709
                        (column) => column.name === newColumn.name,
×
1710
                    )
×
1711
                    column!.isPrimary = false
×
1712

×
1713
                    // if we have another primary keys, we must recreate constraint.
×
1714
                    if (primaryColumns.length > 0) {
×
1715
                        const pkName = primaryColumns[0]
×
1716
                            .primaryKeyConstraintName
×
1717
                            ? primaryColumns[0].primaryKeyConstraintName
×
1718
                            : this.connection.namingStrategy.primaryKeyName(
×
1719
                                  clonedTable,
×
1720
                                  primaryColumns.map((column) => column.name),
×
1721
                              )
×
1722

×
1723
                        const columnNames = primaryColumns
×
1724
                            .map((column) => `"${column.name}"`)
×
1725
                            .join(", ")
×
1726
                        upQueries.push(
×
1727
                            new Query(
×
1728
                                `ALTER TABLE ${this.escapePath(
×
1729
                                    table,
×
1730
                                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
×
1731
                            ),
×
1732
                        )
×
1733
                        downQueries.push(
×
1734
                            new Query(
×
1735
                                `ALTER TABLE ${this.escapePath(
×
1736
                                    table,
×
1737
                                )} DROP CONSTRAINT "${pkName}"`,
×
1738
                            ),
×
1739
                        )
×
1740
                    }
×
1741
                }
×
1742
            }
×
1743

76✔
1744
            if (newColumn.isUnique !== oldColumn.isUnique) {
76✔
1745
                if (newColumn.isUnique) {
2✔
1746
                    const uniqueConstraint = new TableUnique({
1✔
1747
                        name: this.connection.namingStrategy.uniqueConstraintName(
1✔
1748
                            table,
1✔
1749
                            [newColumn.name],
1✔
1750
                        ),
1✔
1751
                        columnNames: [newColumn.name],
1✔
1752
                    })
1✔
1753
                    clonedTable.uniques.push(uniqueConstraint)
1✔
1754
                    upQueries.push(
1✔
1755
                        this.createUniqueConstraintSql(table, uniqueConstraint),
1✔
1756
                    )
1✔
1757
                    // CockroachDB creates index for UNIQUE constraint.
1✔
1758
                    // We must use DROP INDEX ... CASCADE instead of DROP CONSTRAINT.
1✔
1759
                    downQueries.push(this.dropIndexSql(table, uniqueConstraint))
1✔
1760
                } else {
1✔
1761
                    const uniqueConstraint = clonedTable.uniques.find(
1✔
1762
                        (unique) => {
1✔
1763
                            return (
1✔
1764
                                unique.columnNames.length === 1 &&
1✔
1765
                                !!unique.columnNames.find(
1✔
1766
                                    (columnName) =>
1✔
1767
                                        columnName === newColumn.name,
1✔
1768
                                )
1✔
1769
                            )
1✔
1770
                        },
1✔
1771
                    )
1✔
1772
                    clonedTable.uniques.splice(
1✔
1773
                        clonedTable.uniques.indexOf(uniqueConstraint!),
1✔
1774
                        1,
1✔
1775
                    )
1✔
1776
                    // CockroachDB creates index for UNIQUE constraint.
1✔
1777
                    // We must use DROP INDEX ... CASCADE instead of DROP CONSTRAINT.
1✔
1778
                    upQueries.push(this.dropIndexSql(table, uniqueConstraint!))
1✔
1779
                    downQueries.push(
1✔
1780
                        this.createUniqueConstraintSql(
1✔
1781
                            table,
1✔
1782
                            uniqueConstraint!,
1✔
1783
                        ),
1✔
1784
                    )
1✔
1785
                }
1✔
1786
            }
2✔
1787

76✔
1788
            if (
76✔
1789
                (newColumn.type === "enum" ||
76✔
1790
                    newColumn.type === "simple-enum") &&
76✔
1791
                (oldColumn.type === "enum" ||
22!
1792
                    oldColumn.type === "simple-enum") &&
76✔
1793
                (!OrmUtils.isArraysEqual(newColumn.enum!, oldColumn.enum!) ||
22✔
1794
                    newColumn.enumName !== oldColumn.enumName)
22✔
1795
            ) {
76✔
1796
                const arraySuffix = newColumn.isArray ? "[]" : ""
3!
1797

3✔
1798
                // "public"."new_enum"
3✔
1799
                const newEnumName = this.buildEnumName(table, newColumn)
3✔
1800

3✔
1801
                // "public"."old_enum"
3✔
1802
                const oldEnumName = this.buildEnumName(table, oldColumn)
3✔
1803

3✔
1804
                // "old_enum"
3✔
1805
                const oldEnumNameWithoutSchema = this.buildEnumName(
3✔
1806
                    table,
3✔
1807
                    oldColumn,
3✔
1808
                    false,
3✔
1809
                )
3✔
1810

3✔
1811
                //"public"."old_enum_old"
3✔
1812
                const oldEnumNameWithSchema_old = this.buildEnumName(
3✔
1813
                    table,
3✔
1814
                    oldColumn,
3✔
1815
                    true,
3✔
1816
                    false,
3✔
1817
                    true,
3✔
1818
                )
3✔
1819

3✔
1820
                //"old_enum_old"
3✔
1821
                const oldEnumNameWithoutSchema_old = this.buildEnumName(
3✔
1822
                    table,
3✔
1823
                    oldColumn,
3✔
1824
                    false,
3✔
1825
                    false,
3✔
1826
                    true,
3✔
1827
                )
3✔
1828

3✔
1829
                // rename old ENUM
3✔
1830
                upQueries.push(
3✔
1831
                    new Query(
3✔
1832
                        `ALTER TYPE ${oldEnumName} RENAME TO ${oldEnumNameWithoutSchema_old}`,
3✔
1833
                    ),
3✔
1834
                )
3✔
1835
                downQueries.push(
3✔
1836
                    new Query(
3✔
1837
                        `ALTER TYPE ${oldEnumNameWithSchema_old} RENAME TO ${oldEnumNameWithoutSchema}`,
3✔
1838
                    ),
3✔
1839
                )
3✔
1840

3✔
1841
                // create new ENUM
3✔
1842
                upQueries.push(
3✔
1843
                    this.createEnumTypeSql(table, newColumn, newEnumName),
3✔
1844
                )
3✔
1845
                downQueries.push(
3✔
1846
                    this.dropEnumTypeSql(table, newColumn, newEnumName),
3✔
1847
                )
3✔
1848

3✔
1849
                // if column have default value, we must drop it to avoid issues with type casting
3✔
1850
                if (
3✔
1851
                    oldColumn.default !== null &&
3✔
1852
                    oldColumn.default !== undefined
3✔
1853
                ) {
3!
1854
                    // mark default as changed to prevent double update
×
1855
                    defaultValueChanged = true
×
1856
                    upQueries.push(
×
1857
                        new Query(
×
1858
                            `ALTER TABLE ${this.escapePath(
×
1859
                                table,
×
1860
                            )} ALTER COLUMN "${oldColumn.name}" DROP DEFAULT`,
×
1861
                        ),
×
1862
                    )
×
1863
                    downQueries.push(
×
1864
                        new Query(
×
1865
                            `ALTER TABLE ${this.escapePath(
×
1866
                                table,
×
1867
                            )} ALTER COLUMN "${oldColumn.name}" SET DEFAULT ${
×
1868
                                oldColumn.default
×
1869
                            }`,
×
1870
                        ),
×
1871
                    )
×
1872
                }
×
1873

3✔
1874
                // build column types
3✔
1875
                const upType = `${newEnumName}${arraySuffix} USING "${newColumn.name}"::"text"::${newEnumName}${arraySuffix}`
3✔
1876
                const downType = `${oldEnumNameWithSchema_old}${arraySuffix} USING "${newColumn.name}"::"text"::${oldEnumNameWithSchema_old}${arraySuffix}`
3✔
1877

3✔
1878
                upQueries.push(
3✔
1879
                    new Query(
3✔
1880
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
3✔
1881
                            newColumn.name
3✔
1882
                        }" TYPE ${upType}`,
3✔
1883
                    ),
3✔
1884
                )
3✔
1885

3✔
1886
                // we add a delay here since for some reason cockroachdb fails with
3✔
1887
                // "cannot drop type because other objects still depend on it" error
3✔
1888
                // if we are trying to drop type right after we altered it.
3✔
1889
                upQueries.push(new Query(`SELECT pg_sleep(0.1)`))
3✔
1890
                downQueries.push(new Query(`SELECT pg_sleep(0.1)`))
3✔
1891

3✔
1892
                downQueries.push(
3✔
1893
                    new Query(
3✔
1894
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
3✔
1895
                            newColumn.name
3✔
1896
                        }" TYPE ${downType}`,
3✔
1897
                    ),
3✔
1898
                )
3✔
1899

3✔
1900
                // restore column default or create new one
3✔
1901
                if (
3✔
1902
                    newColumn.default !== null &&
3✔
1903
                    newColumn.default !== undefined
3✔
1904
                ) {
3!
1905
                    upQueries.push(
×
1906
                        new Query(
×
1907
                            `ALTER TABLE ${this.escapePath(
×
1908
                                table,
×
1909
                            )} ALTER COLUMN "${newColumn.name}" SET DEFAULT ${
×
1910
                                newColumn.default
×
1911
                            }`,
×
1912
                        ),
×
1913
                    )
×
1914
                    downQueries.push(
×
1915
                        new Query(
×
1916
                            `ALTER TABLE ${this.escapePath(
×
1917
                                table,
×
1918
                            )} ALTER COLUMN "${newColumn.name}" DROP DEFAULT`,
×
1919
                        ),
×
1920
                    )
×
1921
                }
×
1922

3✔
1923
                // remove old ENUM
3✔
1924
                upQueries.push(
3✔
1925
                    this.dropEnumTypeSql(
3✔
1926
                        table,
3✔
1927
                        oldColumn,
3✔
1928
                        oldEnumNameWithSchema_old,
3✔
1929
                    ),
3✔
1930
                )
3✔
1931
                downQueries.push(
3✔
1932
                    this.createEnumTypeSql(
3✔
1933
                        table,
3✔
1934
                        oldColumn,
3✔
1935
                        oldEnumNameWithSchema_old,
3✔
1936
                    ),
3✔
1937
                )
3✔
1938
            }
3✔
1939

76✔
1940
            if (
76✔
1941
                oldColumn.isGenerated !== newColumn.isGenerated &&
76✔
1942
                newColumn.generationStrategy !== "uuid"
1✔
1943
            ) {
76✔
1944
                if (newColumn.isGenerated) {
1!
1945
                    if (newColumn.generationStrategy === "increment") {
×
1946
                        throw new TypeORMError(
×
1947
                            `Adding sequential generated columns into existing table is not supported`,
×
1948
                        )
×
1949
                    } else if (newColumn.generationStrategy === "rowid") {
×
1950
                        upQueries.push(
×
1951
                            new Query(
×
1952
                                `ALTER TABLE ${this.escapePath(
×
1953
                                    table,
×
1954
                                )} ALTER COLUMN "${
×
1955
                                    newColumn.name
×
1956
                                }" SET DEFAULT unique_rowid()`,
×
1957
                            ),
×
1958
                        )
×
1959
                        downQueries.push(
×
1960
                            new Query(
×
1961
                                `ALTER TABLE ${this.escapePath(
×
1962
                                    table,
×
1963
                                )} ALTER COLUMN "${
×
1964
                                    newColumn.name
×
1965
                                }" DROP DEFAULT`,
×
1966
                            ),
×
1967
                        )
×
1968
                    }
×
1969
                } else {
1✔
1970
                    upQueries.push(
1✔
1971
                        new Query(
1✔
1972
                            `ALTER TABLE ${this.escapePath(
1✔
1973
                                table,
1✔
1974
                            )} ALTER COLUMN "${newColumn.name}" DROP DEFAULT`,
1✔
1975
                        ),
1✔
1976
                    )
1✔
1977
                    downQueries.push(
1✔
1978
                        new Query(
1✔
1979
                            `ALTER TABLE ${this.escapePath(
1✔
1980
                                table,
1✔
1981
                            )} ALTER COLUMN "${
1✔
1982
                                newColumn.name
1✔
1983
                            }" SET DEFAULT unique_rowid()`,
1✔
1984
                        ),
1✔
1985
                    )
1✔
1986
                }
1✔
1987
            }
1✔
1988

76✔
1989
            if (
76✔
1990
                newColumn.default !== oldColumn.default &&
76✔
1991
                !defaultValueChanged
1✔
1992
            ) {
76✔
1993
                if (
1✔
1994
                    newColumn.default !== null &&
1✔
1995
                    newColumn.default !== undefined
1✔
1996
                ) {
1✔
1997
                    upQueries.push(
1✔
1998
                        new Query(
1✔
1999
                            `ALTER TABLE ${this.escapePath(
1✔
2000
                                table,
1✔
2001
                            )} ALTER COLUMN "${newColumn.name}" SET DEFAULT ${
1✔
2002
                                newColumn.default
1✔
2003
                            }`,
1✔
2004
                        ),
1✔
2005
                    )
1✔
2006

1✔
2007
                    if (
1✔
2008
                        oldColumn.default !== null &&
1✔
2009
                        oldColumn.default !== undefined
1✔
2010
                    ) {
1✔
2011
                        downQueries.push(
1✔
2012
                            new Query(
1✔
2013
                                `ALTER TABLE ${this.escapePath(
1✔
2014
                                    table,
1✔
2015
                                )} ALTER COLUMN "${
1✔
2016
                                    newColumn.name
1✔
2017
                                }" SET DEFAULT ${oldColumn.default}`,
1✔
2018
                            ),
1✔
2019
                        )
1✔
2020
                    } else {
1!
2021
                        downQueries.push(
×
2022
                            new Query(
×
2023
                                `ALTER TABLE ${this.escapePath(
×
2024
                                    table,
×
2025
                                )} ALTER COLUMN "${
×
2026
                                    newColumn.name
×
2027
                                }" DROP DEFAULT`,
×
2028
                            ),
×
2029
                        )
×
2030
                    }
×
2031
                } else if (
1!
2032
                    oldColumn.default !== null &&
×
2033
                    oldColumn.default !== undefined
×
2034
                ) {
×
2035
                    upQueries.push(
×
2036
                        new Query(
×
2037
                            `ALTER TABLE ${this.escapePath(
×
2038
                                table,
×
2039
                            )} ALTER COLUMN "${newColumn.name}" DROP DEFAULT`,
×
2040
                        ),
×
2041
                    )
×
2042
                    downQueries.push(
×
2043
                        new Query(
×
2044
                            `ALTER TABLE ${this.escapePath(
×
2045
                                table,
×
2046
                            )} ALTER COLUMN "${newColumn.name}" SET DEFAULT ${
×
2047
                                oldColumn.default
×
2048
                            }`,
×
2049
                        ),
×
2050
                    )
×
2051
                }
×
2052
            }
1✔
2053
        }
76✔
2054

86✔
2055
        if (
86✔
2056
            (newColumn.spatialFeatureType || "").toLowerCase() !==
86✔
2057
                (oldColumn.spatialFeatureType || "").toLowerCase() ||
86✔
2058
            newColumn.srid !== oldColumn.srid
86✔
2059
        ) {
86!
2060
            upQueries.push(
×
2061
                new Query(
×
2062
                    `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
×
2063
                        newColumn.name
×
2064
                    }" TYPE ${this.driver.createFullType(newColumn)}`,
×
2065
                ),
×
2066
            )
×
2067
            downQueries.push(
×
2068
                new Query(
×
2069
                    `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
×
2070
                        newColumn.name
×
2071
                    }" TYPE ${this.driver.createFullType(oldColumn)}`,
×
2072
                ),
×
2073
            )
×
2074
        }
×
2075

86✔
2076
        await this.executeQueries(upQueries, downQueries)
86✔
2077
        this.replaceCachedTable(table, clonedTable)
86✔
2078
    }
86✔
2079

26✔
2080
    /**
26✔
2081
     * Changes a column in the table.
26✔
2082
     */
26✔
2083
    async changeColumns(
26✔
2084
        tableOrName: Table | string,
33✔
2085
        changedColumns: { newColumn: TableColumn; oldColumn: TableColumn }[],
33✔
2086
    ): Promise<void> {
33✔
2087
        for (const { oldColumn, newColumn } of changedColumns) {
33✔
2088
            await this.changeColumn(tableOrName, oldColumn, newColumn)
66✔
2089
        }
66✔
2090
    }
33✔
2091

26✔
2092
    /**
26✔
2093
     * Drops column in the table.
26✔
2094
     */
26✔
2095
    async dropColumn(
26✔
2096
        tableOrName: Table | string,
27✔
2097
        columnOrName: TableColumn | string,
27✔
2098
    ): Promise<void> {
27✔
2099
        const table = InstanceChecker.isTable(tableOrName)
27✔
2100
            ? tableOrName
27✔
2101
            : await this.getCachedTable(tableOrName)
27✔
2102
        const column = InstanceChecker.isTableColumn(columnOrName)
3✔
2103
            ? columnOrName
27✔
2104
            : table.findColumnByName(columnOrName)
27✔
2105
        if (!column)
27✔
2106
            throw new TypeORMError(
27✔
2107
                `Column "${columnOrName}" was not found in table "${table.name}"`,
1✔
2108
            )
1✔
2109

26✔
2110
        const clonedTable = table.clone()
26✔
2111
        const upQueries: Query[] = []
26✔
2112
        const downQueries: Query[] = []
26✔
2113

26✔
2114
        // drop primary key constraint
26✔
2115
        // todo: https://go.crdb.dev/issue-v/48026/v21.1
26✔
2116
        if (column.isPrimary) {
27!
2117
            const pkName = column.primaryKeyConstraintName
×
2118
                ? column.primaryKeyConstraintName
×
2119
                : this.connection.namingStrategy.primaryKeyName(
×
2120
                      clonedTable,
×
2121
                      clonedTable.primaryColumns.map((column) => column.name),
×
2122
                  )
×
2123

×
2124
            const columnNames = clonedTable.primaryColumns
×
2125
                .map((primaryColumn) => `"${primaryColumn.name}"`)
×
2126
                .join(", ")
×
2127
            upQueries.push(
×
2128
                new Query(
×
2129
                    `ALTER TABLE ${this.escapePath(
×
2130
                        clonedTable,
×
2131
                    )} DROP CONSTRAINT "${pkName}"`,
×
2132
                ),
×
2133
            )
×
2134
            downQueries.push(
×
2135
                new Query(
×
2136
                    `ALTER TABLE ${this.escapePath(
×
2137
                        clonedTable,
×
2138
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
×
2139
                ),
×
2140
            )
×
2141

×
2142
            // update column in table
×
2143
            const tableColumn = clonedTable.findColumnByName(column.name)
×
2144
            tableColumn!.isPrimary = false
×
2145

×
2146
            // if primary key have multiple columns, we must recreate it without dropped column
×
2147
            if (clonedTable.primaryColumns.length > 0) {
×
2148
                const pkName = clonedTable.primaryColumns[0]
×
2149
                    .primaryKeyConstraintName
×
2150
                    ? clonedTable.primaryColumns[0].primaryKeyConstraintName
×
2151
                    : this.connection.namingStrategy.primaryKeyName(
×
2152
                          clonedTable,
×
2153
                          clonedTable.primaryColumns.map(
×
2154
                              (column) => column.name,
×
2155
                          ),
×
2156
                      )
×
2157

×
2158
                const columnNames = clonedTable.primaryColumns
×
2159
                    .map((primaryColumn) => `"${primaryColumn.name}"`)
×
2160
                    .join(", ")
×
2161

×
2162
                upQueries.push(
×
2163
                    new Query(
×
2164
                        `ALTER TABLE ${this.escapePath(
×
2165
                            clonedTable,
×
2166
                        )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNames})`,
×
2167
                    ),
×
2168
                )
×
2169
                downQueries.push(
×
2170
                    new Query(
×
2171
                        `ALTER TABLE ${this.escapePath(
×
2172
                            clonedTable,
×
2173
                        )} DROP CONSTRAINT "${pkName}"`,
×
2174
                    ),
×
2175
                )
×
2176
            }
×
2177
        }
×
2178

26✔
2179
        // drop column index
26✔
2180
        const columnIndex = clonedTable.indices.find(
26✔
2181
            (index) =>
26✔
2182
                index.columnNames.length === 1 &&
3✔
2183
                index.columnNames[0] === column.name,
26✔
2184
        )
26✔
2185
        if (columnIndex) {
27✔
2186
            clonedTable.indices.splice(
1✔
2187
                clonedTable.indices.indexOf(columnIndex),
1✔
2188
                1,
1✔
2189
            )
1✔
2190
            upQueries.push(this.dropIndexSql(table, columnIndex))
1✔
2191
            downQueries.push(this.createIndexSql(table, columnIndex))
1✔
2192
        }
1✔
2193

26✔
2194
        // drop column check
26✔
2195
        const columnCheck = clonedTable.checks.find(
26✔
2196
            (check) =>
26✔
2197
                !!check.columnNames &&
8✔
2198
                check.columnNames.length === 1 &&
8✔
2199
                check.columnNames[0] === column.name,
26✔
2200
        )
26✔
2201
        if (columnCheck) {
27✔
2202
            clonedTable.checks.splice(
2✔
2203
                clonedTable.checks.indexOf(columnCheck),
2✔
2204
                1,
2✔
2205
            )
2✔
2206
            upQueries.push(this.dropCheckConstraintSql(table, columnCheck))
2✔
2207
            downQueries.push(this.createCheckConstraintSql(table, columnCheck))
2✔
2208
        }
2✔
2209

26✔
2210
        // drop column unique
26✔
2211
        const columnUnique = clonedTable.uniques.find(
26✔
2212
            (unique) =>
26✔
2213
                unique.columnNames.length === 1 &&
13✔
2214
                unique.columnNames[0] === column.name,
26✔
2215
        )
26✔
2216
        if (columnUnique) {
27✔
2217
            clonedTable.uniques.splice(
4✔
2218
                clonedTable.uniques.indexOf(columnUnique),
4✔
2219
                1,
4✔
2220
            )
4✔
2221
            upQueries.push(this.dropIndexSql(table, columnUnique.name!)) // CockroachDB creates indices for unique constraints
4✔
2222
            downQueries.push(
4✔
2223
                this.createUniqueConstraintSql(table, columnUnique),
4✔
2224
            )
4✔
2225
        }
4✔
2226

26✔
2227
        upQueries.push(
26✔
2228
            new Query(
26✔
2229
                `ALTER TABLE ${this.escapePath(table)} DROP COLUMN "${
26✔
2230
                    column.name
26✔
2231
                }"`,
26✔
2232
            ),
26✔
2233
        )
26✔
2234
        downQueries.push(
26✔
2235
            new Query(
26✔
2236
                `ALTER TABLE ${this.escapePath(
26✔
2237
                    table,
26✔
2238
                )} ADD ${this.buildCreateColumnSql(table, column)}`,
26✔
2239
            ),
26✔
2240
        )
26✔
2241

26✔
2242
        if (column.generationStrategy === "increment") {
27!
2243
            upQueries.push(
×
2244
                new Query(
×
2245
                    `DROP SEQUENCE ${this.escapePath(
×
2246
                        this.buildSequencePath(table, column),
×
2247
                    )}`,
×
2248
                ),
×
2249
            )
×
2250
            downQueries.push(
×
2251
                new Query(
×
2252
                    `CREATE SEQUENCE ${this.escapePath(
×
2253
                        this.buildSequencePath(table, column),
×
2254
                    )}`,
×
2255
                ),
×
2256
            )
×
2257
        }
×
2258

26✔
2259
        if (column.generatedType && column.asExpression) {
27✔
2260
            const currentSchema = await this.getCurrentSchema()
4✔
2261
            let { schema } = this.driver.parseTableName(table)
4✔
2262
            if (!schema) {
4!
2263
                schema = currentSchema
×
2264
            }
×
2265
            const deleteQuery = this.deleteTypeormMetadataSql({
4✔
2266
                schema: schema,
4✔
2267
                table: table.name,
4✔
2268
                type: MetadataTableType.GENERATED_COLUMN,
4✔
2269
                name: column.name,
4✔
2270
            })
4✔
2271
            const insertQuery = this.insertTypeormMetadataSql({
4✔
2272
                schema: schema,
4✔
2273
                table: table.name,
4✔
2274
                type: MetadataTableType.GENERATED_COLUMN,
4✔
2275
                name: column.name,
4✔
2276
                value: column.asExpression,
4✔
2277
            })
4✔
2278

4✔
2279
            upQueries.push(deleteQuery)
4✔
2280
            downQueries.push(insertQuery)
4✔
2281
        }
4✔
2282

26✔
2283
        // drop enum type
26✔
2284
        if (column.type === "enum" || column.type === "simple-enum") {
27✔
2285
            const hasEnum = await this.hasEnumType(table, column)
3✔
2286
            if (hasEnum) {
3✔
2287
                const enumType = await this.getUserDefinedTypeName(
3✔
2288
                    table,
3✔
2289
                    column,
3✔
2290
                )
3✔
2291
                const escapedEnumName = `"${enumType.schema}"."${enumType.name}"`
3✔
2292
                upQueries.push(
3✔
2293
                    this.dropEnumTypeSql(table, column, escapedEnumName),
3✔
2294
                )
3✔
2295
                downQueries.push(
3✔
2296
                    this.createEnumTypeSql(table, column, escapedEnumName),
3✔
2297
                )
3✔
2298
            }
3✔
2299
        }
3✔
2300

26✔
2301
        await this.executeQueries(upQueries, downQueries)
26✔
2302

26✔
2303
        clonedTable.removeColumn(column)
26✔
2304
        this.replaceCachedTable(table, clonedTable)
26✔
2305
    }
26✔
2306

26✔
2307
    /**
26✔
2308
     * Drops the columns in the table.
26✔
2309
     */
26✔
2310
    async dropColumns(
26✔
2311
        tableOrName: Table | string,
5✔
2312
        columns: TableColumn[] | string[],
5✔
2313
    ): Promise<void> {
5✔
2314
        for (const column of [...columns]) {
5✔
2315
            await this.dropColumn(tableOrName, column)
11✔
2316
        }
11✔
2317
    }
5✔
2318

26✔
2319
    /**
26✔
2320
     * Creates a new primary key.
26✔
2321
     */
26✔
2322
    async createPrimaryKey(
26✔
2323
        tableOrName: Table | string,
×
2324
        columnNames: string[],
×
2325
        constraintName?: string,
×
2326
    ): Promise<void> {
×
2327
        const table = InstanceChecker.isTable(tableOrName)
×
2328
            ? tableOrName
×
2329
            : await this.getCachedTable(tableOrName)
×
2330
        const clonedTable = table.clone()
×
2331

×
2332
        const up = this.createPrimaryKeySql(table, columnNames, constraintName)
×
2333

×
2334
        // mark columns as primary, because dropPrimaryKeySql build constraint name from table primary column names.
×
2335
        clonedTable.columns.forEach((column) => {
×
2336
            if (columnNames.find((columnName) => columnName === column.name))
×
2337
                column.isPrimary = true
×
2338
        })
×
2339
        const down = this.dropPrimaryKeySql(clonedTable)
×
2340

×
2341
        await this.executeQueries(up, down)
×
2342
        this.replaceCachedTable(table, clonedTable)
×
2343
    }
×
2344

26✔
2345
    /**
26✔
2346
     * Updates composite primary keys.
26✔
2347
     */
26✔
2348
    async updatePrimaryKeys(
26✔
2349
        tableOrName: Table | string,
×
2350
        columns: TableColumn[],
×
2351
    ): Promise<void> {
×
2352
        const table = InstanceChecker.isTable(tableOrName)
×
2353
            ? tableOrName
×
2354
            : await this.getCachedTable(tableOrName)
×
2355
        const clonedTable = table.clone()
×
2356
        const columnNames = columns.map((column) => column.name)
×
2357
        const upQueries: Query[] = []
×
2358
        const downQueries: Query[] = []
×
2359

×
2360
        // if table already have primary columns, we must drop them.
×
2361
        const primaryColumns = clonedTable.primaryColumns
×
2362
        if (primaryColumns.length > 0) {
×
2363
            const pkName = primaryColumns[0].primaryKeyConstraintName
×
2364
                ? primaryColumns[0].primaryKeyConstraintName
×
2365
                : this.connection.namingStrategy.primaryKeyName(
×
2366
                      clonedTable,
×
2367
                      primaryColumns.map((column) => column.name),
×
2368
                  )
×
2369

×
2370
            const columnNamesString = primaryColumns
×
2371
                .map((column) => `"${column.name}"`)
×
2372
                .join(", ")
×
2373

×
2374
            upQueries.push(
×
2375
                new Query(
×
2376
                    `ALTER TABLE ${this.escapePath(
×
2377
                        table,
×
2378
                    )} DROP CONSTRAINT "${pkName}"`,
×
2379
                ),
×
2380
            )
×
2381
            downQueries.push(
×
2382
                new Query(
×
2383
                    `ALTER TABLE ${this.escapePath(
×
2384
                        table,
×
2385
                    )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
×
2386
                ),
×
2387
            )
×
2388
        }
×
2389

×
2390
        // update columns in table.
×
2391
        clonedTable.columns
×
2392
            .filter((column) => columnNames.indexOf(column.name) !== -1)
×
2393
            .forEach((column) => (column.isPrimary = true))
×
2394

×
2395
        const pkName = primaryColumns[0].primaryKeyConstraintName
×
2396
            ? primaryColumns[0].primaryKeyConstraintName
×
2397
            : this.connection.namingStrategy.primaryKeyName(
×
2398
                  clonedTable,
×
2399
                  columnNames,
×
2400
              )
×
2401

×
2402
        const columnNamesString = columnNames
×
2403
            .map((columnName) => `"${columnName}"`)
×
2404
            .join(", ")
×
2405
        upQueries.push(
×
2406
            new Query(
×
2407
                `ALTER TABLE ${this.escapePath(
×
2408
                    table,
×
2409
                )} ADD CONSTRAINT "${pkName}" PRIMARY KEY (${columnNamesString})`,
×
2410
            ),
×
2411
        )
×
2412
        downQueries.push(
×
2413
            new Query(
×
2414
                `ALTER TABLE ${this.escapePath(
×
2415
                    table,
×
2416
                )} DROP CONSTRAINT "${pkName}"`,
×
2417
            ),
×
2418
        )
×
2419

×
2420
        await this.executeQueries(upQueries, downQueries)
×
2421
        this.replaceCachedTable(table, clonedTable)
×
2422
    }
×
2423

26✔
2424
    /**
26✔
2425
     * Drops a primary key.
26✔
2426
     */
26✔
2427
    async dropPrimaryKey(
26✔
2428
        tableOrName: Table | string,
×
2429
        constraintName?: string,
×
2430
    ): Promise<void> {
×
2431
        const table = InstanceChecker.isTable(tableOrName)
×
2432
            ? tableOrName
×
2433
            : await this.getCachedTable(tableOrName)
×
2434
        const up = this.dropPrimaryKeySql(table)
×
2435
        const down = this.createPrimaryKeySql(
×
2436
            table,
×
2437
            table.primaryColumns.map((column) => column.name),
×
2438
            constraintName,
×
2439
        )
×
2440
        await this.executeQueries(up, down)
×
2441
        table.primaryColumns.forEach((column) => {
×
2442
            column.isPrimary = false
×
2443
        })
×
2444
    }
×
2445

26✔
2446
    /**
26✔
2447
     * Creates new unique constraint.
26✔
2448
     */
26✔
2449
    async createUniqueConstraint(
26✔
2450
        tableOrName: Table | string,
11✔
2451
        uniqueConstraint: TableUnique,
11✔
2452
    ): Promise<void> {
11✔
2453
        const table = InstanceChecker.isTable(tableOrName)
11✔
2454
            ? tableOrName
11✔
2455
            : await this.getCachedTable(tableOrName)
11✔
2456

6✔
2457
        // new unique constraint may be passed without name. In this case we generate unique name manually.
6✔
2458
        if (!uniqueConstraint.name)
6✔
2459
            uniqueConstraint.name =
11✔
2460
                this.connection.namingStrategy.uniqueConstraintName(
3✔
2461
                    table,
3✔
2462
                    uniqueConstraint.columnNames,
3✔
2463
                )
3✔
2464

11✔
2465
        const up = this.createUniqueConstraintSql(table, uniqueConstraint)
11✔
2466
        // CockroachDB creates index for UNIQUE constraint.
11✔
2467
        // We must use DROP INDEX ... CASCADE instead of DROP CONSTRAINT.
11✔
2468
        const down = this.dropIndexSql(table, uniqueConstraint)
11✔
2469
        await this.executeQueries(up, down)
11✔
2470
        table.addUniqueConstraint(uniqueConstraint)
11✔
2471
    }
11✔
2472

26✔
2473
    /**
26✔
2474
     * Creates new unique constraints.
26✔
2475
     */
26✔
2476
    async createUniqueConstraints(
26✔
2477
        tableOrName: Table | string,
6✔
2478
        uniqueConstraints: TableUnique[],
6✔
2479
    ): Promise<void> {
6✔
2480
        for (const uniqueConstraint of uniqueConstraints) {
6✔
2481
            await this.createUniqueConstraint(tableOrName, uniqueConstraint)
9✔
2482
        }
9✔
2483
    }
6✔
2484

26✔
2485
    /**
26✔
2486
     * Drops unique constraint.
26✔
2487
     */
26✔
2488
    async dropUniqueConstraint(
26✔
2489
        tableOrName: Table | string,
12✔
2490
        uniqueOrName: TableUnique | string,
12✔
2491
    ): Promise<void> {
12✔
2492
        const table = InstanceChecker.isTable(tableOrName)
12✔
2493
            ? tableOrName
12✔
2494
            : await this.getCachedTable(tableOrName)
12✔
2495
        const uniqueConstraint = InstanceChecker.isTableUnique(uniqueOrName)
4✔
2496
            ? uniqueOrName
12✔
2497
            : table.uniques.find((u) => u.name === uniqueOrName)
12!
2498
        if (!uniqueConstraint)
12✔
2499
            throw new TypeORMError(
12!
2500
                `Supplied unique constraint was not found in table ${table.name}`,
×
2501
            )
×
2502

12✔
2503
        // CockroachDB creates index for UNIQUE constraint.
12✔
2504
        // We must use DROP INDEX ... CASCADE instead of DROP CONSTRAINT.
12✔
2505
        const up = this.dropIndexSql(table, uniqueConstraint)
12✔
2506
        const down = this.createUniqueConstraintSql(table, uniqueConstraint)
12✔
2507
        await this.executeQueries(up, down)
12✔
2508
        table.removeUniqueConstraint(uniqueConstraint)
12✔
2509
    }
12✔
2510

26✔
2511
    /**
26✔
2512
     * Drops unique constraints.
26✔
2513
     */
26✔
2514
    async dropUniqueConstraints(
26✔
2515
        tableOrName: Table | string,
8✔
2516
        uniqueConstraints: TableUnique[],
8✔
2517
    ): Promise<void> {
8✔
2518
        for (const uniqueConstraint of [...uniqueConstraints]) {
8✔
2519
            await this.dropUniqueConstraint(tableOrName, uniqueConstraint)
11✔
2520
        }
11✔
2521
    }
8✔
2522

26✔
2523
    /**
26✔
2524
     * Creates new check constraint.
26✔
2525
     */
26✔
2526
    async createCheckConstraint(
26✔
2527
        tableOrName: Table | string,
5✔
2528
        checkConstraint: TableCheck,
5✔
2529
    ): Promise<void> {
5✔
2530
        const table = InstanceChecker.isTable(tableOrName)
5✔
2531
            ? tableOrName
5✔
2532
            : await this.getCachedTable(tableOrName)
5✔
2533

3✔
2534
        // new unique constraint may be passed without name. In this case we generate unique name manually.
3✔
2535
        if (!checkConstraint.name)
3✔
2536
            checkConstraint.name =
3✔
2537
                this.connection.namingStrategy.checkConstraintName(
3✔
2538
                    table,
3✔
2539
                    checkConstraint.expression!,
3✔
2540
                )
3✔
2541

5✔
2542
        const up = this.createCheckConstraintSql(table, checkConstraint)
5✔
2543
        const down = this.dropCheckConstraintSql(table, checkConstraint)
5✔
2544
        await this.executeQueries(up, down)
5✔
2545
        table.addCheckConstraint(checkConstraint)
5✔
2546
    }
5✔
2547

26✔
2548
    /**
26✔
2549
     * Creates new check constraints.
26✔
2550
     */
26✔
2551
    async createCheckConstraints(
26✔
2552
        tableOrName: Table | string,
2✔
2553
        checkConstraints: TableCheck[],
2✔
2554
    ): Promise<void> {
2✔
2555
        const promises = checkConstraints.map((checkConstraint) =>
2✔
2556
            this.createCheckConstraint(tableOrName, checkConstraint),
2✔
2557
        )
2✔
2558
        await Promise.all(promises)
2✔
2559
    }
2✔
2560

26✔
2561
    /**
26✔
2562
     * Drops check constraint.
26✔
2563
     */
26✔
2564
    async dropCheckConstraint(
26✔
2565
        tableOrName: Table | string,
3✔
2566
        checkOrName: TableCheck | string,
3✔
2567
    ): Promise<void> {
3✔
2568
        const table = InstanceChecker.isTable(tableOrName)
3✔
2569
            ? tableOrName
3✔
2570
            : await this.getCachedTable(tableOrName)
3!
2571
        const checkConstraint = InstanceChecker.isTableCheck(checkOrName)
×
2572
            ? checkOrName
3✔
2573
            : table.checks.find((c) => c.name === checkOrName)
3!
2574
        if (!checkConstraint)
3✔
2575
            throw new TypeORMError(
3!
2576
                `Supplied check constraint was not found in table ${table.name}`,
×
2577
            )
×
2578

3✔
2579
        const up = this.dropCheckConstraintSql(table, checkConstraint)
3✔
2580
        const down = this.createCheckConstraintSql(table, checkConstraint)
3✔
2581
        await this.executeQueries(up, down)
3✔
2582
        table.removeCheckConstraint(checkConstraint)
3✔
2583
    }
3✔
2584

26✔
2585
    /**
26✔
2586
     * Drops check constraints.
26✔
2587
     */
26✔
2588
    async dropCheckConstraints(
26✔
2589
        tableOrName: Table | string,
2✔
2590
        checkConstraints: TableCheck[],
2✔
2591
    ): Promise<void> {
2✔
2592
        const promises = checkConstraints.map((checkConstraint) =>
2✔
2593
            this.dropCheckConstraint(tableOrName, checkConstraint),
2✔
2594
        )
2✔
2595
        await Promise.all(promises)
2✔
2596
    }
2✔
2597

26✔
2598
    /**
26✔
2599
     * Creates new exclusion constraint.
26✔
2600
     */
26✔
2601
    async createExclusionConstraint(
26✔
2602
        tableOrName: Table | string,
×
2603
        exclusionConstraint: TableExclusion,
×
2604
    ): Promise<void> {
×
2605
        throw new TypeORMError(
×
2606
            `CockroachDB does not support exclusion constraints.`,
×
2607
        )
×
2608
    }
×
2609

26✔
2610
    /**
26✔
2611
     * Creates new exclusion constraints.
26✔
2612
     */
26✔
2613
    async createExclusionConstraints(
26✔
2614
        tableOrName: Table | string,
×
2615
        exclusionConstraints: TableExclusion[],
×
2616
    ): Promise<void> {
×
2617
        throw new TypeORMError(
×
2618
            `CockroachDB does not support exclusion constraints.`,
×
2619
        )
×
2620
    }
×
2621

26✔
2622
    /**
26✔
2623
     * Drops exclusion constraint.
26✔
2624
     */
26✔
2625
    async dropExclusionConstraint(
26✔
2626
        tableOrName: Table | string,
×
2627
        exclusionOrName: TableExclusion | string,
×
2628
    ): Promise<void> {
×
2629
        throw new TypeORMError(
×
2630
            `CockroachDB does not support exclusion constraints.`,
×
2631
        )
×
2632
    }
×
2633

26✔
2634
    /**
26✔
2635
     * Drops exclusion constraints.
26✔
2636
     */
26✔
2637
    async dropExclusionConstraints(
26✔
2638
        tableOrName: Table | string,
×
2639
        exclusionConstraints: TableExclusion[],
×
2640
    ): Promise<void> {
×
2641
        throw new TypeORMError(
×
2642
            `CockroachDB does not support exclusion constraints.`,
×
2643
        )
×
2644
    }
×
2645

26✔
2646
    /**
26✔
2647
     * Creates a new foreign key.
26✔
2648
     */
26✔
2649
    async createForeignKey(
26✔
2650
        tableOrName: Table | string,
2,828✔
2651
        foreignKey: TableForeignKey,
2,828✔
2652
    ): Promise<void> {
2,828✔
2653
        const table = InstanceChecker.isTable(tableOrName)
2,828✔
2654
            ? tableOrName
2,828✔
2655
            : await this.getCachedTable(tableOrName)
2,828✔
2656

5✔
2657
        // new FK may be passed without name. In this case we generate FK name manually.
5✔
2658
        if (!foreignKey.name)
5✔
2659
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
2,828✔
2660
                table,
1✔
2661
                foreignKey.columnNames,
1✔
2662
                this.getTablePath(foreignKey),
1✔
2663
                foreignKey.referencedColumnNames,
1✔
2664
            )
1✔
2665

2,828✔
2666
        const up = this.createForeignKeySql(table, foreignKey)
2,828✔
2667
        const down = this.dropForeignKeySql(table, foreignKey)
2,828✔
2668
        await this.executeQueries(up, down)
2,828✔
2669
        table.addForeignKey(foreignKey)
2,828✔
2670
    }
2,828✔
2671

26✔
2672
    /**
26✔
2673
     * Creates a new foreign keys.
26✔
2674
     */
26✔
2675
    async createForeignKeys(
26✔
2676
        tableOrName: Table | string,
1,631✔
2677
        foreignKeys: TableForeignKey[],
1,631✔
2678
    ): Promise<void> {
1,631✔
2679
        for (const foreignKey of foreignKeys) {
1,631✔
2680
            await this.createForeignKey(tableOrName, foreignKey)
2,827✔
2681
        }
2,827✔
2682
    }
1,631✔
2683

26✔
2684
    /**
26✔
2685
     * Drops a foreign key from the table.
26✔
2686
     */
26✔
2687
    async dropForeignKey(
26✔
2688
        tableOrName: Table | string,
12✔
2689
        foreignKeyOrName: TableForeignKey | string,
12✔
2690
    ): Promise<void> {
12✔
2691
        const table = InstanceChecker.isTable(tableOrName)
12✔
2692
            ? tableOrName
12✔
2693
            : await this.getCachedTable(tableOrName)
12✔
2694
        const foreignKey = InstanceChecker.isTableForeignKey(foreignKeyOrName)
4✔
2695
            ? foreignKeyOrName
12✔
2696
            : table.foreignKeys.find((fk) => fk.name === foreignKeyOrName)
12!
2697
        if (!foreignKey)
12✔
2698
            throw new TypeORMError(
12!
2699
                `Supplied foreign key was not found in table ${table.name}`,
×
2700
            )
×
2701

12✔
2702
        const up = this.dropForeignKeySql(table, foreignKey)
12✔
2703
        const down = this.createForeignKeySql(table, foreignKey)
12✔
2704
        await this.executeQueries(up, down)
12✔
2705
        table.removeForeignKey(foreignKey)
12✔
2706
    }
12✔
2707

26✔
2708
    /**
26✔
2709
     * Drops a foreign keys from the table.
26✔
2710
     */
26✔
2711
    async dropForeignKeys(
26✔
2712
        tableOrName: Table | string,
8✔
2713
        foreignKeys: TableForeignKey[],
8✔
2714
    ): Promise<void> {
8✔
2715
        for (const foreignKey of [...foreignKeys]) {
8✔
2716
            await this.dropForeignKey(tableOrName, foreignKey)
11✔
2717
        }
11✔
2718
    }
8✔
2719

26✔
2720
    /**
26✔
2721
     * Creates a new index.
26✔
2722
     */
26✔
2723
    async createIndex(
26✔
2724
        tableOrName: Table | string,
15✔
2725
        index: TableIndex,
15✔
2726
    ): Promise<void> {
15✔
2727
        const table = InstanceChecker.isTable(tableOrName)
15✔
2728
            ? tableOrName
15✔
2729
            : await this.getCachedTable(tableOrName)
15✔
2730

7✔
2731
        // new index may be passed without name. In this case we generate index name manually.
7✔
2732
        if (!index.name) index.name = this.generateIndexName(table, index)
15✔
2733

15✔
2734
        // CockroachDB stores unique indices and UNIQUE constraints
15✔
2735
        if (index.isUnique) {
15✔
2736
            const unique = new TableUnique({
5✔
2737
                name: index.name,
5✔
2738
                columnNames: index.columnNames,
5✔
2739
            })
5✔
2740
            const up = this.createUniqueConstraintSql(table, unique)
5✔
2741
            // CockroachDB also creates index for UNIQUE constraints.
5✔
2742
            // We can't drop UNIQUE constraint with DROP CONSTRAINT. We must use DROP INDEX ... CASCADE instead.
5✔
2743
            const down = this.dropIndexSql(table, unique)
5✔
2744
            await this.executeQueries(up, down)
5✔
2745
            table.addUniqueConstraint(unique)
5✔
2746
        } else {
15✔
2747
            const up = this.createIndexSql(table, index)
10✔
2748
            const down = this.dropIndexSql(table, index)
10✔
2749
            await this.executeQueries(up, down)
10✔
2750
            table.addIndex(index)
10✔
2751
        }
10✔
2752
    }
15✔
2753

26✔
2754
    /**
26✔
2755
     * Creates a new indices
26✔
2756
     */
26✔
2757
    async createIndices(
26✔
2758
        tableOrName: Table | string,
8✔
2759
        indices: TableIndex[],
8✔
2760
    ): Promise<void> {
8✔
2761
        for (const index of indices) {
8✔
2762
            await this.createIndex(tableOrName, index)
11✔
2763
        }
11✔
2764
    }
8✔
2765

26✔
2766
    /**
26✔
2767
     * Drops an index from the table.
26✔
2768
     */
26✔
2769
    async dropIndex(
26✔
2770
        tableOrName: Table | string,
12✔
2771
        indexOrName: TableIndex | string,
12✔
2772
    ): Promise<void> {
12✔
2773
        const table = InstanceChecker.isTable(tableOrName)
12✔
2774
            ? tableOrName
12✔
2775
            : await this.getCachedTable(tableOrName)
12✔
2776
        const index = InstanceChecker.isTableIndex(indexOrName)
5✔
2777
            ? indexOrName
12✔
2778
            : table.indices.find((i) => i.name === indexOrName)
12!
2779
        if (!index)
12✔
2780
            throw new TypeORMError(
12!
2781
                `Supplied index ${indexOrName} was not found in table ${table.name}`,
×
2782
            )
×
2783

12✔
2784
        // old index may be passed without name. In this case we generate index name manually.
12✔
2785
        if (!index.name) index.name = this.generateIndexName(table, index)
12✔
2786

12✔
2787
        const up = this.dropIndexSql(table, index)
12✔
2788
        const down = this.createIndexSql(table, index)
12✔
2789
        await this.executeQueries(up, down)
12✔
2790
        table.removeIndex(index)
12✔
2791
    }
12✔
2792

26✔
2793
    /**
26✔
2794
     * Drops an indices from the table.
26✔
2795
     */
26✔
2796
    async dropIndices(
26✔
2797
        tableOrName: Table | string,
1✔
2798
        indices: TableIndex[],
1✔
2799
    ): Promise<void> {
1✔
2800
        for (const index of [...indices]) {
1✔
2801
            await this.dropIndex(tableOrName, index)
4✔
2802
        }
4✔
2803
    }
1✔
2804

26✔
2805
    /**
26✔
2806
     * Clears all table contents.
26✔
2807
     * Note: this operation uses SQL's TRUNCATE query which cannot be reverted in transactions.
26✔
2808
     */
26✔
2809
    async clearTable(tableName: string): Promise<void> {
26✔
2810
        await this.query(`TRUNCATE TABLE ${this.escapePath(tableName)}`)
2✔
2811
    }
2✔
2812

26✔
2813
    /**
26✔
2814
     * Removes all tables from the currently connected database.
26✔
2815
     */
26✔
2816
    async clearDatabase(): Promise<void> {
26✔
2817
        const schemas: string[] = []
1,340✔
2818
        this.connection.entityMetadatas
1,340✔
2819
            .filter((metadata) => metadata.schema)
1,340✔
2820
            .forEach((metadata) => {
1,340✔
2821
                const isSchemaExist = !!schemas.find(
2✔
2822
                    (schema) => schema === metadata.schema,
2✔
2823
                )
2✔
2824
                if (!isSchemaExist) schemas.push(metadata.schema!)
2✔
2825
            })
1,340✔
2826
        schemas.push(this.driver.options.schema || "current_schema()")
1,340✔
2827
        const schemaNamesString = schemas
1,340✔
2828
            .map((name) => {
1,340✔
2829
                return name === "current_schema()" ? name : "'" + name + "'"
1,342✔
2830
            })
1,340✔
2831
            .join(", ")
1,340✔
2832

1,340✔
2833
        const isAnotherTransactionActive = this.isTransactionActive
1,340✔
2834
        if (!isAnotherTransactionActive) await this.startTransaction()
1,340✔
2835
        try {
1,340✔
2836
            const version = await this.getVersion()
1,340✔
2837
            const selectViewDropsQuery =
1,340✔
2838
                `SELECT 'DROP VIEW IF EXISTS "' || schemaname || '"."' || viewname || '" CASCADE;' as "query" ` +
1,340✔
2839
                `FROM "pg_views" WHERE "schemaname" IN (${schemaNamesString})`
1,340✔
2840
            const dropViewQueries: ObjectLiteral[] = await this.query(
1,340✔
2841
                selectViewDropsQuery,
1,340✔
2842
            )
1,340✔
2843
            await Promise.all(
1,340✔
2844
                dropViewQueries.map((q) => this.query(q["query"])),
1,340✔
2845
            )
1,340✔
2846

1,340✔
2847
            const selectDropsQuery = `SELECT 'DROP TABLE IF EXISTS "' || table_schema || '"."' || table_name || '" CASCADE;' as "query" FROM "information_schema"."tables" WHERE "table_schema" IN (${schemaNamesString})`
1,340✔
2848
            const dropQueries: ObjectLiteral[] = await this.query(
1,340✔
2849
                selectDropsQuery,
1,340✔
2850
            )
1,340✔
2851
            await Promise.all(dropQueries.map((q) => this.query(q["query"])))
1,340✔
2852

1,340✔
2853
            const selectSequenceDropsQuery = `SELECT 'DROP SEQUENCE "' || sequence_schema || '"."' || sequence_name || '";' as "query" FROM "information_schema"."sequences" WHERE "sequence_schema" IN (${schemaNamesString})`
1,340✔
2854
            const sequenceDropQueries: ObjectLiteral[] = await this.query(
1,340✔
2855
                selectSequenceDropsQuery,
1,340✔
2856
            )
1,340✔
2857
            await Promise.all(
1,340✔
2858
                sequenceDropQueries.map((q) => this.query(q["query"])),
1,340✔
2859
            )
1,340✔
2860

1,340✔
2861
            // drop enum types. Supported starting from v20.2.19.
1,340✔
2862
            if (VersionUtils.isGreaterOrEqual(version, "20.2.19")) {
1,340✔
2863
                await this.dropEnumTypes(schemaNamesString)
1,340✔
2864
            }
1,340✔
2865

1,340✔
2866
            if (!isAnotherTransactionActive) await this.commitTransaction()
1,340✔
2867
        } catch (error) {
1,340!
2868
            try {
×
2869
                // we throw original error even if rollback thrown an error
×
2870
                if (!isAnotherTransactionActive)
×
2871
                    await this.rollbackTransaction()
×
2872
            } catch {
×
2873
                // no-op
×
2874
            }
×
2875
            throw error
×
2876
        }
×
2877
    }
1,340✔
2878

26✔
2879
    // -------------------------------------------------------------------------
26✔
2880
    // Protected Methods
26✔
2881
    // -------------------------------------------------------------------------
26✔
2882

26✔
2883
    protected async loadViews(viewNames?: string[]): Promise<View[]> {
26✔
2884
        const hasTable = await this.hasTable(this.getTypeormMetadataTableName())
1,397✔
2885
        if (!hasTable) {
1,397✔
2886
            return []
1,385✔
2887
        }
1,385✔
2888

12✔
2889
        if (!viewNames) {
1,397!
2890
            viewNames = []
×
2891
        }
×
2892

12✔
2893
        const currentDatabase = await this.getCurrentDatabase()
12✔
2894
        const currentSchema = await this.getCurrentSchema()
12✔
2895

12✔
2896
        const viewsCondition = viewNames
12✔
2897
            .map((viewName) => {
12✔
2898
                const { schema, tableName } =
8✔
2899
                    this.driver.parseTableName(viewName)
8✔
2900

8✔
2901
                return `("t"."schema" = '${
8✔
2902
                    schema || currentSchema
8!
2903
                }' AND "t"."name" = '${tableName}')`
8✔
2904
            })
12✔
2905
            .join(" OR ")
12✔
2906

12✔
2907
        const query =
12✔
2908
            `SELECT "t".*, "v"."check_option" FROM ${this.escapePath(
12✔
2909
                this.getTypeormMetadataTableName(),
12✔
2910
            )} "t" ` +
12✔
2911
            `INNER JOIN "information_schema"."views" "v" ON "v"."table_schema" = "t"."schema" AND "v"."table_name" = "t"."name" WHERE "t"."type" = '${
12✔
2912
                MetadataTableType.VIEW
12✔
2913
            }' ${viewsCondition ? `AND (${viewsCondition})` : ""}`
1,397✔
2914
        const dbViews = await this.query(query)
1,397✔
2915
        return dbViews.map((dbView: any) => {
12✔
2916
            const view = new View()
2✔
2917
            const schema =
2✔
2918
                dbView["schema"] === currentSchema &&
2✔
2919
                !this.driver.options.schema
2✔
2920
                    ? undefined
2✔
2921
                    : dbView["schema"]
2!
2922
            view.database = currentDatabase
2✔
2923
            view.schema = dbView["schema"]
2✔
2924
            view.name = this.driver.buildTableName(dbView["name"], schema)
2✔
2925
            view.expression = dbView["value"]
2✔
2926
            return view
2✔
2927
        })
12✔
2928
    }
12✔
2929

26✔
2930
    /**
26✔
2931
     * Loads all tables (with given names) from the database and creates a Table from them.
26✔
2932
     */
26✔
2933
    protected async loadTables(tableNames?: string[]): Promise<Table[]> {
26✔
2934
        // if no tables given then no need to proceed
1,666✔
2935
        if (tableNames && tableNames.length === 0) {
1,666✔
2936
            return []
6✔
2937
        }
6✔
2938

1,660✔
2939
        const currentSchema = await this.getCurrentSchema()
1,660✔
2940
        const currentDatabase = await this.getCurrentDatabase()
1,660✔
2941

1,660✔
2942
        const dbTables: { table_schema: string; table_name: string }[] = []
1,660✔
2943

1,660✔
2944
        if (!tableNames) {
1,666!
2945
            const tablesSql = `SELECT "table_schema", "table_name" FROM "information_schema"."tables"`
×
2946
            dbTables.push(...(await this.query(tablesSql)))
×
2947
        } else {
1,666✔
2948
            const tablesCondition = tableNames
1,660✔
2949
                .map((tableName) => this.driver.parseTableName(tableName))
1,660✔
2950
                .map(({ schema, tableName }) => {
1,660✔
2951
                    return `("table_schema" = '${
4,917✔
2952
                        schema || currentSchema
4,917!
2953
                    }' AND "table_name" = '${tableName}')`
4,917✔
2954
                })
1,660✔
2955
                .join(" OR ")
1,660✔
2956
            const tablesSql =
1,660✔
2957
                `SELECT "table_schema", "table_name" FROM "information_schema"."tables" WHERE ` +
1,660✔
2958
                tablesCondition
1,660✔
2959

1,660✔
2960
            dbTables.push(...(await this.query(tablesSql)))
1,660✔
2961
        }
1,660✔
2962

1,660✔
2963
        if (dbTables.length === 0) {
1,666✔
2964
            return []
1,358✔
2965
        }
1,358✔
2966

302✔
2967
        const columnsCondiiton = dbTables
302✔
2968
            .map(({ table_name, table_schema }) => {
302✔
2969
                return `("table_schema" = '${table_schema}' AND "table_name" = '${table_name}')`
440✔
2970
            })
302✔
2971
            .join(" OR ")
302✔
2972
        const columnsSql =
302✔
2973
            `SELECT "columns".*, "attr"."attgenerated" as "generated_type", ` +
302✔
2974
            `pg_catalog.col_description(('"' || table_catalog || '"."' || table_schema || '"."' || table_name || '"')::regclass::oid, ordinal_position) as description ` +
302✔
2975
            `FROM "information_schema"."columns" ` +
302✔
2976
            `LEFT JOIN "pg_class" AS "cls" ON "cls"."relname" = "table_name" ` +
302✔
2977
            `LEFT JOIN "pg_namespace" AS "ns" ON "ns"."oid" = "cls"."relnamespace" AND "ns"."nspname" = "table_schema" ` +
302✔
2978
            `LEFT JOIN "pg_attribute" AS "attr" ON "attr"."attrelid" = "cls"."oid" AND "attr"."attname" = "column_name" AND "attr"."attnum" = "ordinal_position" ` +
302✔
2979
            `WHERE "is_hidden" = 'NO' AND ` +
302✔
2980
            columnsCondiiton
302✔
2981

302✔
2982
        const constraintsCondition = dbTables
302✔
2983
            .map(({ table_name, table_schema }) => {
302✔
2984
                return `("ns"."nspname" = '${table_schema}' AND "t"."relname" = '${table_name}')`
440✔
2985
            })
302✔
2986
            .join(" OR ")
302✔
2987

302✔
2988
        const constraintsSql =
302✔
2989
            `SELECT "ns"."nspname" AS "table_schema", "t"."relname" AS "table_name", "cnst"."conname" AS "constraint_name", ` +
302✔
2990
            `pg_get_constraintdef("cnst"."oid") AS "expression", ` +
302✔
2991
            `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" ` +
302✔
2992
            `FROM "pg_constraint" "cnst" ` +
302✔
2993
            `INNER JOIN "pg_class" "t" ON "t"."oid" = "cnst"."conrelid" ` +
302✔
2994
            `INNER JOIN "pg_namespace" "ns" ON "ns"."oid" = "cnst"."connamespace" ` +
302✔
2995
            `LEFT JOIN "pg_attribute" "a" ON "a"."attrelid" = "cnst"."conrelid" AND "a"."attnum" = ANY ("cnst"."conkey") ` +
302✔
2996
            `WHERE "t"."relkind" = 'r' AND (${constraintsCondition})`
302✔
2997

302✔
2998
        const indicesSql =
302✔
2999
            `SELECT "ns"."nspname" AS "table_schema", "t"."relname" AS "table_name", "i"."relname" AS "constraint_name", "a"."attname" AS "column_name", ` +
302✔
3000
            `CASE "ix"."indisunique" WHEN 't' THEN 'TRUE' ELSE'FALSE' END AS "is_unique", pg_get_expr("ix"."indpred", "ix"."indrelid") AS "condition", ` +
302✔
3001
            `"types"."typname" AS "type_name" ` +
302✔
3002
            `FROM "pg_class" "t" ` +
302✔
3003
            `INNER JOIN "pg_index" "ix" ON "ix"."indrelid" = "t"."oid" ` +
302✔
3004
            `INNER JOIN "pg_attribute" "a" ON "a"."attrelid" = "t"."oid"  AND "a"."attnum" = ANY ("ix"."indkey") ` +
302✔
3005
            `INNER JOIN "pg_namespace" "ns" ON "ns"."oid" = "t"."relnamespace" ` +
302✔
3006
            `INNER JOIN "pg_class" "i" ON "i"."oid" = "ix"."indexrelid" ` +
302✔
3007
            `INNER JOIN "pg_type" "types" ON "types"."oid" = "a"."atttypid" ` +
302✔
3008
            `LEFT JOIN "pg_constraint" "cnst" ON "cnst"."conname" = "i"."relname" ` +
302✔
3009
            `WHERE "t"."relkind" = 'r' AND "cnst"."contype" IS NULL AND (${constraintsCondition})`
302✔
3010

302✔
3011
        const foreignKeysCondition = dbTables
302✔
3012
            .map(({ table_name, table_schema }) => {
302✔
3013
                return `("ns"."nspname" = '${table_schema}' AND "cl"."relname" = '${table_name}')`
440✔
3014
            })
302✔
3015
            .join(" OR ")
302✔
3016
        const foreignKeysSql =
302✔
3017
            `SELECT "con"."conname" AS "constraint_name", "con"."nspname" AS "table_schema", "con"."relname" AS "table_name", "att2"."attname" AS "column_name", ` +
302✔
3018
            `"ns"."nspname" AS "referenced_table_schema", "cl"."relname" AS "referenced_table_name", "att"."attname" AS "referenced_column_name", "con"."confdeltype" AS "on_delete", "con"."confupdtype" AS "on_update" ` +
302✔
3019
            `FROM ( ` +
302✔
3020
            `SELECT UNNEST ("con1"."conkey") AS "parent", UNNEST ("con1"."confkey") AS "child", "con1"."confrelid", "con1"."conrelid", "con1"."conname", "con1"."contype", "ns"."nspname", "cl"."relname", ` +
302✔
3021
            `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", ` +
302✔
3022
            `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" ` +
302✔
3023
            `FROM "pg_class" "cl" ` +
302✔
3024
            `INNER JOIN "pg_namespace" "ns" ON "cl"."relnamespace" = "ns"."oid" ` +
302✔
3025
            `INNER JOIN "pg_constraint" "con1" ON "con1"."conrelid" = "cl"."oid" ` +
302✔
3026
            `WHERE "con1"."contype" = 'f' AND (${foreignKeysCondition}) ` +
302✔
3027
            `) "con" ` +
302✔
3028
            `INNER JOIN "pg_attribute" "att" ON "att"."attrelid" = "con"."confrelid" AND "att"."attnum" = "con"."child" ` +
302✔
3029
            `INNER JOIN "pg_class" "cl" ON "cl"."oid" = "con"."confrelid" ` +
302✔
3030
            `INNER JOIN "pg_namespace" "ns" ON "cl"."relnamespace" = "ns"."oid" ` +
302✔
3031
            `INNER JOIN "pg_attribute" "att2" ON "att2"."attrelid" = "con"."conrelid" AND "att2"."attnum" = "con"."parent"`
302✔
3032

302✔
3033
        const tableSchemas = dbTables
302✔
3034
            .map((dbTable) => `'${dbTable.table_schema}'`)
302✔
3035
            .join(", ")
302✔
3036
        const enumsSql =
302✔
3037
            `SELECT "t"."typname" AS "name", string_agg("e"."enumlabel", '|') AS "value" ` +
302✔
3038
            `FROM "pg_enum" "e" ` +
302✔
3039
            `INNER JOIN "pg_type" "t" ON "t"."oid" = "e"."enumtypid" ` +
302✔
3040
            `INNER JOIN "pg_namespace" "n" ON "n"."oid" = "t"."typnamespace" ` +
302✔
3041
            `WHERE "n"."nspname" IN (${tableSchemas}) ` +
302✔
3042
            `GROUP BY "t"."typname"`
302✔
3043

302✔
3044
        const [
302✔
3045
            dbColumns,
302✔
3046
            dbConstraints,
302✔
3047
            dbIndices,
302✔
3048
            dbForeignKeys,
302✔
3049
            dbEnums,
302✔
3050
        ]: ObjectLiteral[][] = await Promise.all([
302✔
3051
            this.query(columnsSql),
302✔
3052
            this.query(constraintsSql),
302✔
3053
            this.query(indicesSql),
302✔
3054
            this.query(foreignKeysSql),
302✔
3055
            this.query(enumsSql),
302✔
3056
        ])
302✔
3057

302✔
3058
        // create tables for loaded tables
302✔
3059
        return Promise.all(
302✔
3060
            dbTables.map(async (dbTable) => {
302✔
3061
                const table = new Table()
440✔
3062

440✔
3063
                const getSchemaFromKey = (dbObject: any, key: string) => {
440✔
3064
                    return dbObject[key] === currentSchema &&
607✔
3065
                        (!this.driver.options.schema ||
607✔
3066
                            this.driver.options.schema === currentSchema)
607✔
3067
                        ? undefined
607✔
3068
                        : dbObject[key]
607!
3069
                }
607✔
3070

440✔
3071
                // We do not need to join schema name, when database is by default.
440✔
3072
                const schema = getSchemaFromKey(dbTable, "table_schema")
440✔
3073
                table.database = currentDatabase
440✔
3074
                table.schema = dbTable["table_schema"]
440✔
3075
                table.name = this.driver.buildTableName(
440✔
3076
                    dbTable["table_name"],
440✔
3077
                    schema,
440✔
3078
                )
440✔
3079

440✔
3080
                // create columns from the loaded columns
440✔
3081
                table.columns = await Promise.all(
440✔
3082
                    dbColumns
440✔
3083
                        .filter(
440✔
3084
                            (dbColumn) =>
440✔
3085
                                dbColumn["table_name"] ===
4,813✔
3086
                                    dbTable["table_name"] &&
4,813✔
3087
                                dbColumn["table_schema"] ===
1,759✔
3088
                                    dbTable["table_schema"],
440✔
3089
                        )
440✔
3090
                        .map(async (dbColumn) => {
440✔
3091
                            const columnConstraints = dbConstraints.filter(
1,759✔
3092
                                (dbConstraint) => {
1,759✔
3093
                                    return (
10,033✔
3094
                                        dbConstraint["table_name"] ===
10,033✔
3095
                                            dbColumn["table_name"] &&
10,033✔
3096
                                        dbConstraint["table_schema"] ===
4,064✔
3097
                                            dbColumn["table_schema"] &&
10,033✔
3098
                                        dbConstraint["column_name"] ===
4,064✔
3099
                                            dbColumn["column_name"]
4,064✔
3100
                                    )
10,033✔
3101
                                },
1,759✔
3102
                            )
1,759✔
3103

1,759✔
3104
                            const tableColumn = new TableColumn()
1,759✔
3105
                            tableColumn.name = dbColumn["column_name"]
1,759✔
3106

1,759✔
3107
                            tableColumn.type =
1,759✔
3108
                                dbColumn["crdb_sql_type"].toLowerCase()
1,759✔
3109
                            if (
1,759✔
3110
                                dbColumn["crdb_sql_type"].indexOf("COLLATE") !==
1,759✔
3111
                                -1
1,759✔
3112
                            ) {
1,759✔
3113
                                tableColumn.collation = dbColumn[
1✔
3114
                                    "crdb_sql_type"
1✔
3115
                                ].substr(
1✔
3116
                                    dbColumn["crdb_sql_type"].indexOf(
1✔
3117
                                        "COLLATE",
1✔
3118
                                    ) +
1✔
3119
                                        "COLLATE".length +
1✔
3120
                                        1,
1✔
3121
                                    dbColumn["crdb_sql_type"].length,
1✔
3122
                                )
1✔
3123
                                tableColumn.type = tableColumn.type.substr(
1✔
3124
                                    0,
1✔
3125
                                    dbColumn["crdb_sql_type"].indexOf(
1✔
3126
                                        "COLLATE",
1✔
3127
                                    ) - 1,
1✔
3128
                                )
1✔
3129
                            }
1✔
3130

1,759✔
3131
                            if (tableColumn.type.indexOf("(") !== -1)
1,759✔
3132
                                tableColumn.type = tableColumn.type.substr(
1,759✔
3133
                                    0,
113✔
3134
                                    tableColumn.type.indexOf("("),
113✔
3135
                                )
113✔
3136

1,759✔
3137
                            if (
1,759✔
3138
                                tableColumn.type === "numeric" ||
1,759✔
3139
                                tableColumn.type === "decimal"
1,759✔
3140
                            ) {
1,759✔
3141
                                if (
12✔
3142
                                    dbColumn["numeric_precision"] !== null &&
12✔
3143
                                    !this.isDefaultColumnPrecision(
7✔
3144
                                        table,
7✔
3145
                                        tableColumn,
7✔
3146
                                        dbColumn["numeric_precision"],
7✔
3147
                                    )
12✔
3148
                                ) {
12✔
3149
                                    tableColumn.precision = parseInt(
7✔
3150
                                        dbColumn["numeric_precision"],
7✔
3151
                                    )
7✔
3152
                                } else if (
12✔
3153
                                    dbColumn["numeric_scale"] !== null &&
5!
3154
                                    !this.isDefaultColumnScale(
×
3155
                                        table,
×
3156
                                        tableColumn,
×
3157
                                        dbColumn["numeric_scale"],
×
3158
                                    )
5✔
3159
                                ) {
5!
3160
                                    tableColumn.precision = undefined
×
3161
                                }
×
3162
                                if (
12✔
3163
                                    dbColumn["numeric_scale"] !== null &&
12✔
3164
                                    !this.isDefaultColumnScale(
7✔
3165
                                        table,
7✔
3166
                                        tableColumn,
7✔
3167
                                        dbColumn["numeric_scale"],
7✔
3168
                                    )
12✔
3169
                                ) {
12✔
3170
                                    tableColumn.scale = parseInt(
7✔
3171
                                        dbColumn["numeric_scale"],
7✔
3172
                                    )
7✔
3173
                                } else if (
12✔
3174
                                    dbColumn["numeric_precision"] !== null &&
5!
3175
                                    !this.isDefaultColumnPrecision(
×
3176
                                        table,
×
3177
                                        tableColumn,
×
3178
                                        dbColumn["numeric_precision"],
×
3179
                                    )
5✔
3180
                                ) {
5!
3181
                                    tableColumn.scale = undefined
×
3182
                                }
×
3183
                            }
12✔
3184

1,759✔
3185
                            // docs: https://www.postgresql.org/docs/current/xtypes.html
1,759✔
3186
                            // When you define a new base type, PostgreSQL automatically provides support for arrays of that type.
1,759✔
3187
                            // The array type typically has the same name as the base type with the underscore character (_) prepended.
1,759✔
3188
                            // ----
1,759✔
3189
                            // so, we must remove this underscore character from enum type name
1,759✔
3190
                            let udtName = dbColumn["udt_name"]
1,759✔
3191
                            if (udtName.indexOf("_") === 0) {
1,759✔
3192
                                udtName = udtName.substr(1, udtName.length)
56✔
3193
                            }
56✔
3194

1,759✔
3195
                            const enumType = dbEnums.find((dbEnum) => {
1,759✔
3196
                                return dbEnum["name"] === udtName
603✔
3197
                            })
1,759✔
3198
                            if (enumType) {
1,759✔
3199
                                // check if `enumName` is specified by user
130✔
3200
                                const builtEnumName = this.buildEnumName(
130✔
3201
                                    table,
130✔
3202
                                    tableColumn,
130✔
3203
                                    false,
130✔
3204
                                    true,
130✔
3205
                                )
130✔
3206
                                const enumName =
130✔
3207
                                    builtEnumName !== enumType["name"]
130✔
3208
                                        ? enumType["name"]
130✔
3209
                                        : undefined
130✔
3210

130✔
3211
                                tableColumn.type = "enum"
130✔
3212
                                tableColumn.enum = enumType["value"].split("|")
130✔
3213
                                tableColumn.enumName = enumName
130✔
3214
                            }
130✔
3215

1,759✔
3216
                            if (
1,759✔
3217
                                dbColumn["data_type"].toLowerCase() === "array"
1,759✔
3218
                            ) {
1,759✔
3219
                                tableColumn.isArray = true
56✔
3220
                                if (!enumType) {
56✔
3221
                                    const type = dbColumn["crdb_sql_type"]
1✔
3222
                                        .replace("[]", "")
1✔
3223
                                        .toLowerCase()
1✔
3224
                                    tableColumn.type =
1✔
3225
                                        this.connection.driver.normalizeType({
1✔
3226
                                            type: type,
1✔
3227
                                        })
1✔
3228
                                }
1✔
3229
                            }
56✔
3230

1,759✔
3231
                            // check only columns that have length property
1,759✔
3232
                            if (
1,759✔
3233
                                this.driver.withLengthColumnTypes.indexOf(
1,759✔
3234
                                    tableColumn.type as ColumnType,
1,759✔
3235
                                ) !== -1 &&
1,759✔
3236
                                dbColumn["character_maximum_length"]
846✔
3237
                            ) {
1,759✔
3238
                                const length =
102✔
3239
                                    dbColumn[
102✔
3240
                                        "character_maximum_length"
102✔
3241
                                    ].toString()
102✔
3242
                                tableColumn.length =
102✔
3243
                                    !this.isDefaultColumnLength(
102✔
3244
                                        table,
102✔
3245
                                        tableColumn,
102✔
3246
                                        length,
102✔
3247
                                    )
102✔
3248
                                        ? length
102✔
3249
                                        : ""
102✔
3250
                            }
102✔
3251
                            tableColumn.isNullable =
1,759✔
3252
                                dbColumn["is_nullable"] === "YES"
1,759✔
3253

1,759✔
3254
                            const primaryConstraint = columnConstraints.find(
1,759✔
3255
                                (constraint) =>
1,759✔
3256
                                    constraint["constraint_type"] === "PRIMARY",
1,759✔
3257
                            )
1,759✔
3258
                            if (primaryConstraint) {
1,759✔
3259
                                tableColumn.isPrimary = true
472✔
3260
                                // find another columns involved in primary key constraint
472✔
3261
                                const anotherPrimaryConstraints =
472✔
3262
                                    dbConstraints.filter(
472✔
3263
                                        (constraint) =>
472✔
3264
                                            constraint["table_name"] ===
2,972✔
3265
                                                dbColumn["table_name"] &&
2,972✔
3266
                                            constraint["table_schema"] ===
1,054✔
3267
                                                dbColumn["table_schema"] &&
2,972✔
3268
                                            constraint["column_name"] !==
1,054✔
3269
                                                dbColumn["column_name"] &&
2,972✔
3270
                                            constraint["constraint_type"] ===
553✔
3271
                                                "PRIMARY",
472✔
3272
                                    )
472✔
3273

472✔
3274
                                // collect all column names
472✔
3275
                                const columnNames =
472✔
3276
                                    anotherPrimaryConstraints.map(
472✔
3277
                                        (constraint) =>
472✔
3278
                                            constraint["column_name"],
472✔
3279
                                    )
472✔
3280
                                columnNames.push(dbColumn["column_name"])
472✔
3281

472✔
3282
                                // build default primary key constraint name
472✔
3283
                                const pkName =
472✔
3284
                                    this.connection.namingStrategy.primaryKeyName(
472✔
3285
                                        table,
472✔
3286
                                        columnNames,
472✔
3287
                                    )
472✔
3288

472✔
3289
                                // if primary key has user-defined constraint name, write it in table column
472✔
3290
                                if (
472✔
3291
                                    primaryConstraint["constraint_name"] !==
472✔
3292
                                    pkName
472✔
3293
                                ) {
472✔
3294
                                    tableColumn.primaryKeyConstraintName =
26✔
3295
                                        primaryConstraint["constraint_name"]
26✔
3296
                                }
26✔
3297
                            }
472✔
3298

1,759✔
3299
                            const uniqueConstraints = columnConstraints.filter(
1,759✔
3300
                                (constraint) =>
1,759✔
3301
                                    constraint["constraint_type"] === "UNIQUE",
1,759✔
3302
                            )
1,759✔
3303
                            const isConstraintComposite =
1,759✔
3304
                                uniqueConstraints.every((uniqueConstraint) => {
1,759✔
3305
                                    return dbConstraints.some(
264✔
3306
                                        (dbConstraint) =>
264✔
3307
                                            dbConstraint["constraint_type"] ===
1,262✔
3308
                                                "UNIQUE" &&
1,262✔
3309
                                            dbConstraint["constraint_name"] ===
650✔
3310
                                                uniqueConstraint[
650✔
3311
                                                    "constraint_name"
650✔
3312
                                                ] &&
1,262✔
3313
                                            dbConstraint["column_name"] !==
336✔
3314
                                                dbColumn["column_name"],
264✔
3315
                                    )
264✔
3316
                                })
1,759✔
3317
                            tableColumn.isUnique =
1,759✔
3318
                                uniqueConstraints.length > 0 &&
1,759✔
3319
                                !isConstraintComposite
261✔
3320

1,759✔
3321
                            if (
1,759✔
3322
                                dbColumn["column_default"] !== null &&
1,759✔
3323
                                dbColumn["column_default"] !== undefined
405✔
3324
                            ) {
1,759✔
3325
                                if (
405✔
3326
                                    dbColumn["column_default"] ===
405✔
3327
                                    "unique_rowid()"
405✔
3328
                                ) {
405✔
3329
                                    tableColumn.isGenerated = true
4✔
3330
                                    tableColumn.generationStrategy = "rowid"
4✔
3331
                                } else if (
405✔
3332
                                    dbColumn["column_default"].indexOf(
401✔
3333
                                        "nextval",
401✔
3334
                                    ) !== -1
401✔
3335
                                ) {
401✔
3336
                                    tableColumn.isGenerated = true
269✔
3337
                                    tableColumn.generationStrategy = "increment"
269✔
3338
                                } else if (
401✔
3339
                                    dbColumn["column_default"] ===
132✔
3340
                                    "gen_random_uuid()"
132✔
3341
                                ) {
132✔
3342
                                    tableColumn.isGenerated = true
6✔
3343
                                    tableColumn.generationStrategy = "uuid"
6✔
3344
                                } else {
132✔
3345
                                    tableColumn.default = dbColumn[
126✔
3346
                                        "column_default"
126✔
3347
                                    ].replace(/:::[\w\s[\]"]+/g, "")
126✔
3348
                                    tableColumn.default =
126✔
3349
                                        tableColumn.default.replace(
126✔
3350
                                            /^(-?[\d.]+)$/,
126✔
3351
                                            "($1)",
126✔
3352
                                        )
126✔
3353

126✔
3354
                                    if (enumType) {
126✔
3355
                                        tableColumn.default =
12✔
3356
                                            tableColumn.default.replace(
12✔
3357
                                                `.${enumType["name"]}`,
12✔
3358
                                                "",
12✔
3359
                                            )
12✔
3360
                                    }
12✔
3361
                                }
126✔
3362
                            }
405✔
3363

1,759✔
3364
                            if (
1,759✔
3365
                                (dbColumn["is_generated"] === "YES" ||
1,759✔
3366
                                    dbColumn["is_generated"] === "ALWAYS") &&
1,759✔
3367
                                dbColumn["generation_expression"]
51✔
3368
                            ) {
1,759✔
3369
                                tableColumn.generatedType =
51✔
3370
                                    dbColumn["generated_type"] === "s"
51✔
3371
                                        ? "STORED"
51✔
3372
                                        : "VIRTUAL"
51✔
3373
                                // We cannot relay on information_schema.columns.generation_expression, because it is formatted different.
51✔
3374
                                const asExpressionQuery =
51✔
3375
                                    this.selectTypeormMetadataSql({
51✔
3376
                                        schema: dbTable["table_schema"],
51✔
3377
                                        table: dbTable["table_name"],
51✔
3378
                                        type: MetadataTableType.GENERATED_COLUMN,
51✔
3379
                                        name: tableColumn.name,
51✔
3380
                                    })
51✔
3381

51✔
3382
                                const results = await this.query(
51✔
3383
                                    asExpressionQuery.query,
51✔
3384
                                    asExpressionQuery.parameters,
51✔
3385
                                )
51✔
3386
                                if (results[0] && results[0].value) {
51✔
3387
                                    tableColumn.asExpression = results[0].value
51✔
3388
                                } else {
51!
3389
                                    tableColumn.asExpression = ""
×
3390
                                }
×
3391
                            }
51✔
3392

1,759✔
3393
                            tableColumn.comment =
1,759✔
3394
                                dbColumn["description"] == null
1,759✔
3395
                                    ? undefined
1,759✔
3396
                                    : dbColumn["description"]
1,759✔
3397
                            if (dbColumn["character_set_name"])
1,759✔
3398
                                tableColumn.charset =
1,759!
3399
                                    dbColumn["character_set_name"]
×
3400

1,759✔
3401
                            if (
1,759✔
3402
                                tableColumn.type === "geometry" ||
1,759✔
3403
                                tableColumn.type === "geography"
1,743✔
3404
                            ) {
1,759✔
3405
                                const sql =
26✔
3406
                                    `SELECT * FROM (` +
26✔
3407
                                    `SELECT "f_table_schema" "table_schema", "f_table_name" "table_name", ` +
26✔
3408
                                    `"f_${tableColumn.type}_column" "column_name", "srid", "type" ` +
26✔
3409
                                    `FROM "${tableColumn.type}_columns"` +
26✔
3410
                                    `) AS _ ` +
26✔
3411
                                    `WHERE "column_name" = '${dbColumn["column_name"]}' AND ` +
26✔
3412
                                    `"table_schema" = '${dbColumn["table_schema"]}' AND ` +
26✔
3413
                                    `"table_name" = '${dbColumn["table_name"]}'`
26✔
3414

26✔
3415
                                const results: ObjectLiteral[] =
26✔
3416
                                    await this.query(sql)
26✔
3417

26✔
3418
                                if (results.length > 0) {
26✔
3419
                                    tableColumn.spatialFeatureType =
26✔
3420
                                        results[0].type
26✔
3421
                                    tableColumn.srid = results[0].srid
26✔
3422
                                        ? parseInt(results[0].srid)
26✔
3423
                                        : undefined
26!
3424
                                }
26✔
3425
                            }
26✔
3426

1,759✔
3427
                            return tableColumn
1,759✔
3428
                        }),
440✔
3429
                )
440✔
3430

440✔
3431
                // find unique constraints of table, group them by constraint name and build TableUnique.
440✔
3432
                const tableUniqueConstraints = OrmUtils.uniq(
440✔
3433
                    dbConstraints.filter((dbConstraint) => {
440✔
3434
                        return (
2,885✔
3435
                            dbConstraint["table_name"] ===
2,885✔
3436
                                dbTable["table_name"] &&
2,885✔
3437
                            dbConstraint["table_schema"] ===
969✔
3438
                                dbTable["table_schema"] &&
2,885✔
3439
                            dbConstraint["constraint_type"] === "UNIQUE"
969✔
3440
                        )
2,885✔
3441
                    }),
440✔
3442
                    (dbConstraint) => dbConstraint["constraint_name"],
440✔
3443
                )
440✔
3444

440✔
3445
                table.uniques = tableUniqueConstraints.map((constraint) => {
440✔
3446
                    const uniques = dbConstraints.filter(
191✔
3447
                        (dbC) =>
191✔
3448
                            dbC["constraint_name"] ===
1,341✔
3449
                            constraint["constraint_name"],
191✔
3450
                    )
191✔
3451
                    return new TableUnique({
191✔
3452
                        name: constraint["constraint_name"],
191✔
3453
                        columnNames: uniques.map((u) => u["column_name"]),
191✔
3454
                    })
191✔
3455
                })
440✔
3456

440✔
3457
                // find check constraints of table, group them by constraint name and build TableCheck.
440✔
3458
                const tableCheckConstraints = OrmUtils.uniq(
440✔
3459
                    dbConstraints.filter((dbConstraint) => {
440✔
3460
                        return (
2,885✔
3461
                            dbConstraint["table_name"] ===
2,885✔
3462
                                dbTable["table_name"] &&
2,885✔
3463
                            dbConstraint["table_schema"] ===
969✔
3464
                                dbTable["table_schema"] &&
2,885✔
3465
                            dbConstraint["constraint_type"] === "CHECK"
969✔
3466
                        )
2,885✔
3467
                    }),
440✔
3468
                    (dbConstraint) => dbConstraint["constraint_name"],
440✔
3469
                )
440✔
3470

440✔
3471
                table.checks = tableCheckConstraints.map((constraint) => {
440✔
3472
                    const checks = dbConstraints.filter(
65✔
3473
                        (dbC) =>
65✔
3474
                            dbC["constraint_name"] ===
551✔
3475
                            constraint["constraint_name"],
65✔
3476
                    )
65✔
3477
                    return new TableCheck({
65✔
3478
                        name: constraint["constraint_name"],
65✔
3479
                        columnNames: checks.map((c) => c["column_name"]),
65✔
3480
                        expression: constraint["expression"].replace(
65✔
3481
                            /^\s*CHECK\s*\((.*)\)\s*$/i,
65✔
3482
                            "$1",
65✔
3483
                        ),
65✔
3484
                    })
65✔
3485
                })
440✔
3486

440✔
3487
                // find exclusion constraints of table, group them by constraint name and build TableExclusion.
440✔
3488
                const tableExclusionConstraints = OrmUtils.uniq(
440✔
3489
                    dbConstraints.filter((dbConstraint) => {
440✔
3490
                        return (
2,885✔
3491
                            dbConstraint["table_name"] ===
2,885✔
3492
                                dbTable["table_name"] &&
2,885✔
3493
                            dbConstraint["table_schema"] ===
969✔
3494
                                dbTable["table_schema"] &&
2,885✔
3495
                            dbConstraint["constraint_type"] === "EXCLUDE"
969✔
3496
                        )
2,885✔
3497
                    }),
440✔
3498
                    (dbConstraint) => dbConstraint["constraint_name"],
440✔
3499
                )
440✔
3500

440✔
3501
                table.exclusions = tableExclusionConstraints.map(
440✔
3502
                    (constraint) => {
440✔
3503
                        return new TableExclusion({
×
3504
                            name: constraint["constraint_name"],
×
3505
                            expression: constraint["expression"].substring(8), // trim EXCLUDE from start of expression
×
3506
                        })
×
3507
                    },
440✔
3508
                )
440✔
3509

440✔
3510
                // find foreign key constraints of table, group them by constraint name and build TableForeignKey.
440✔
3511
                const tableForeignKeyConstraints = OrmUtils.uniq(
440✔
3512
                    dbForeignKeys.filter((dbForeignKey) => {
440✔
3513
                        return (
569✔
3514
                            dbForeignKey["table_name"] ===
569✔
3515
                                dbTable["table_name"] &&
569✔
3516
                            dbForeignKey["table_schema"] ===
171✔
3517
                                dbTable["table_schema"]
171✔
3518
                        )
569✔
3519
                    }),
440✔
3520
                    (dbForeignKey) => dbForeignKey["constraint_name"],
440✔
3521
                )
440✔
3522

440✔
3523
                table.foreignKeys = tableForeignKeyConstraints.map(
440✔
3524
                    (dbForeignKey) => {
440✔
3525
                        const foreignKeys = dbForeignKeys.filter(
167✔
3526
                            (dbFk) =>
167✔
3527
                                dbFk["constraint_name"] ===
449✔
3528
                                dbForeignKey["constraint_name"],
167✔
3529
                        )
167✔
3530

167✔
3531
                        // if referenced table located in currently used schema, we don't need to concat schema name to table name.
167✔
3532
                        const schema = getSchemaFromKey(
167✔
3533
                            dbForeignKey,
167✔
3534
                            "referenced_table_schema",
167✔
3535
                        )
167✔
3536
                        const referencedTableName = this.driver.buildTableName(
167✔
3537
                            dbForeignKey["referenced_table_name"],
167✔
3538
                            schema,
167✔
3539
                        )
167✔
3540

167✔
3541
                        return new TableForeignKey({
167✔
3542
                            name: dbForeignKey["constraint_name"],
167✔
3543
                            columnNames: foreignKeys.map(
167✔
3544
                                (dbFk) => dbFk["column_name"],
167✔
3545
                            ),
167✔
3546
                            referencedSchema:
167✔
3547
                                dbForeignKey["referenced_table_schema"],
167✔
3548
                            referencedTableName: referencedTableName,
167✔
3549
                            referencedColumnNames: foreignKeys.map(
167✔
3550
                                (dbFk) => dbFk["referenced_column_name"],
167✔
3551
                            ),
167✔
3552
                            onDelete: dbForeignKey["on_delete"],
167✔
3553
                            onUpdate: dbForeignKey["on_update"],
167✔
3554
                        })
167✔
3555
                    },
440✔
3556
                )
440✔
3557

440✔
3558
                // find index constraints of table, group them by constraint name and build TableIndex.
440✔
3559
                const tableIndexConstraints = OrmUtils.uniq(
440✔
3560
                    dbIndices.filter((dbIndex) => {
440✔
3561
                        return (
755✔
3562
                            dbIndex["table_name"] === dbTable["table_name"] &&
755✔
3563
                            dbIndex["table_schema"] === dbTable["table_schema"]
231✔
3564
                        )
755✔
3565
                    }),
440✔
3566
                    (dbIndex) => dbIndex["constraint_name"],
440✔
3567
                )
440✔
3568

440✔
3569
                table.indices = tableIndexConstraints.map((constraint) => {
440✔
3570
                    const indices = dbIndices.filter(
224✔
3571
                        (index) =>
224✔
3572
                            index["constraint_name"] ===
631✔
3573
                            constraint["constraint_name"],
224✔
3574
                    )
224✔
3575
                    return new TableIndex(<TableIndexOptions>{
224✔
3576
                        table: table,
224✔
3577
                        name: constraint["constraint_name"],
224✔
3578
                        columnNames: indices.map((i) => i["column_name"]),
224✔
3579
                        isUnique: constraint["is_unique"] === "TRUE",
224✔
3580
                        where: constraint["condition"],
224✔
3581
                        isSpatial: indices.every(
224✔
3582
                            (i) =>
224✔
3583
                                this.driver.spatialTypes.indexOf(
224✔
3584
                                    i["type_name"],
224✔
3585
                                ) >= 0,
224✔
3586
                        ),
224✔
3587
                        isFulltext: false,
224✔
3588
                    })
224✔
3589
                })
440✔
3590

440✔
3591
                return table
440✔
3592
            }),
302✔
3593
        )
302✔
3594
    }
302✔
3595

26✔
3596
    /**
26✔
3597
     * Builds create table sql.
26✔
3598
     */
26✔
3599
    protected createTableSql(table: Table, createForeignKeys?: boolean): Query {
26✔
3600
        const columnDefinitions = table.columns
4,510✔
3601
            .map((column) => this.buildCreateColumnSql(table, column))
4,510✔
3602
            .join(", ")
4,510✔
3603
        let sql = `CREATE TABLE ${this.escapePath(table)} (${columnDefinitions}`
4,510✔
3604

4,510✔
3605
        table.columns
4,510✔
3606
            .filter((column) => column.isUnique)
4,510✔
3607
            .forEach((column) => {
4,510✔
3608
                const isUniqueExist = table.uniques.some(
730✔
3609
                    (unique) =>
730✔
3610
                        unique.columnNames.length === 1 &&
1,130✔
3611
                        unique.columnNames[0] === column.name,
730✔
3612
                )
730✔
3613
                if (!isUniqueExist)
730✔
3614
                    table.uniques.push(
730✔
3615
                        new TableUnique({
5✔
3616
                            name: this.connection.namingStrategy.uniqueConstraintName(
5✔
3617
                                table,
5✔
3618
                                [column.name],
5✔
3619
                            ),
5✔
3620
                            columnNames: [column.name],
5✔
3621
                        }),
5✔
3622
                    )
5✔
3623
            })
4,510✔
3624

4,510✔
3625
        table.indices
4,510✔
3626
            .filter((index) => index.isUnique)
4,510✔
3627
            .forEach((index) => {
4,510✔
3628
                table.uniques.push(
3✔
3629
                    new TableUnique({
3✔
3630
                        name: this.connection.namingStrategy.uniqueConstraintName(
3✔
3631
                            table,
3✔
3632
                            index.columnNames,
3✔
3633
                        ),
3✔
3634
                        columnNames: index.columnNames,
3✔
3635
                    }),
3✔
3636
                )
3✔
3637
            })
4,510✔
3638

4,510✔
3639
        if (table.uniques.length > 0) {
4,510✔
3640
            const uniquesSql = table.uniques
595✔
3641
                .map((unique) => {
595✔
3642
                    const uniqueName = unique.name
938✔
3643
                        ? unique.name
938✔
3644
                        : this.connection.namingStrategy.uniqueConstraintName(
938✔
3645
                              table,
2✔
3646
                              unique.columnNames,
2✔
3647
                          )
938✔
3648
                    const columnNames = unique.columnNames
938✔
3649
                        .map((columnName) => `"${columnName}"`)
938✔
3650
                        .join(", ")
938✔
3651
                    return `CONSTRAINT "${uniqueName}" UNIQUE (${columnNames})`
938✔
3652
                })
595✔
3653
                .join(", ")
595✔
3654

595✔
3655
            sql += `, ${uniquesSql}`
595✔
3656
        }
595✔
3657

4,510✔
3658
        if (table.checks.length > 0) {
4,510✔
3659
            const checksSql = table.checks
71✔
3660
                .map((check) => {
71✔
3661
                    const checkName = check.name
72✔
3662
                        ? check.name
72✔
3663
                        : this.connection.namingStrategy.checkConstraintName(
72✔
3664
                              table,
1✔
3665
                              check.expression!,
1✔
3666
                          )
72✔
3667
                    return `CONSTRAINT "${checkName}" CHECK (${check.expression})`
72✔
3668
                })
71✔
3669
                .join(", ")
71✔
3670

71✔
3671
            sql += `, ${checksSql}`
71✔
3672
        }
71✔
3673

4,510✔
3674
        if (table.foreignKeys.length > 0 && createForeignKeys) {
4,510✔
3675
            const foreignKeysSql = table.foreignKeys
4✔
3676
                .map((fk) => {
4✔
3677
                    const columnNames = fk.columnNames
5✔
3678
                        .map((columnName) => `"${columnName}"`)
5✔
3679
                        .join(", ")
5✔
3680
                    if (!fk.name)
5✔
3681
                        fk.name = this.connection.namingStrategy.foreignKeyName(
5✔
3682
                            table,
3✔
3683
                            fk.columnNames,
3✔
3684
                            this.getTablePath(fk),
3✔
3685
                            fk.referencedColumnNames,
3✔
3686
                        )
3✔
3687
                    const referencedColumnNames = fk.referencedColumnNames
5✔
3688
                        .map((columnName) => `"${columnName}"`)
5✔
3689
                        .join(", ")
5✔
3690

5✔
3691
                    let constraint = `CONSTRAINT "${
5✔
3692
                        fk.name
5✔
3693
                    }" FOREIGN KEY (${columnNames}) REFERENCES ${this.escapePath(
5✔
3694
                        this.getTablePath(fk),
5✔
3695
                    )} (${referencedColumnNames})`
5✔
3696
                    if (fk.onDelete) constraint += ` ON DELETE ${fk.onDelete}`
5✔
3697
                    if (fk.onUpdate) constraint += ` ON UPDATE ${fk.onUpdate}`
5✔
3698

5✔
3699
                    return constraint
5✔
3700
                })
4✔
3701
                .join(", ")
4✔
3702

4✔
3703
            sql += `, ${foreignKeysSql}`
4✔
3704
        }
4✔
3705

4,510✔
3706
        const primaryColumns = table.columns.filter(
4,510✔
3707
            (column) => column.isPrimary,
4,510✔
3708
        )
4,510✔
3709
        if (primaryColumns.length > 0) {
4,510✔
3710
            const primaryKeyName = primaryColumns[0].primaryKeyConstraintName
4,499✔
3711
                ? primaryColumns[0].primaryKeyConstraintName
4,499✔
3712
                : this.connection.namingStrategy.primaryKeyName(
4,499✔
3713
                      table,
4,483✔
3714
                      primaryColumns.map((column) => column.name),
4,483✔
3715
                  )
4,499✔
3716

4,499✔
3717
            const columnNames = primaryColumns
4,499✔
3718
                .map((column) => `"${column.name}"`)
4,499✔
3719
                .join(", ")
4,499✔
3720
            sql += `, CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNames})`
4,499✔
3721
        }
4,499✔
3722

4,510✔
3723
        sql += `)`
4,510✔
3724

4,510✔
3725
        table.columns
4,510✔
3726
            .filter((it) => it.comment)
4,510✔
3727
            .forEach(
4,510✔
3728
                (it) =>
4,510✔
3729
                    (sql += `; COMMENT ON COLUMN ${this.escapePath(table)}."${
58✔
3730
                        it.name
58✔
3731
                    }" IS ${this.escapeComment(it.comment)}`),
4,510✔
3732
            )
4,510✔
3733

4,510✔
3734
        return new Query(sql)
4,510✔
3735
    }
4,510✔
3736

26✔
3737
    /**
26✔
3738
     * Loads Cockroachdb version.
26✔
3739
     */
26✔
3740
    async getVersion(): Promise<string> {
26✔
3741
        const result: [{ version: string }] = await this.query(
1,340✔
3742
            `SELECT version() AS "version"`,
1,340✔
3743
        )
1,340✔
3744
        const versionString = result[0].version
1,340✔
3745

1,340✔
3746
        return versionString.replace(/^CockroachDB CCL v([\d.]+) .*$/, "$1")
1,340✔
3747
    }
1,340✔
3748

26✔
3749
    /**
26✔
3750
     * Builds drop table sql.
26✔
3751
     */
26✔
3752
    protected dropTableSql(tableOrPath: Table | string): Query {
26✔
3753
        return new Query(`DROP TABLE ${this.escapePath(tableOrPath)}`)
4,510✔
3754
    }
4,510✔
3755

26✔
3756
    protected createViewSql(view: View): Query {
26✔
3757
        if (typeof view.expression === "string") {
6!
3758
            return new Query(
×
3759
                `CREATE VIEW ${this.escapePath(view)} AS ${view.expression}`,
×
3760
            )
×
3761
        } else {
6✔
3762
            return new Query(
6✔
3763
                `CREATE VIEW ${this.escapePath(view)} AS ${view
6✔
3764
                    .expression(this.connection)
6✔
3765
                    .getQuery()}`,
6✔
3766
            )
6✔
3767
        }
6✔
3768
    }
6✔
3769

26✔
3770
    protected async insertViewDefinitionSql(view: View): Promise<Query> {
26✔
3771
        const currentSchema = await this.getCurrentSchema()
6✔
3772
        let { schema, tableName: name } = this.driver.parseTableName(view)
6✔
3773
        if (!schema) {
6!
3774
            schema = currentSchema
×
3775
        }
×
3776

6✔
3777
        const expression =
6✔
3778
            typeof view.expression === "string"
6✔
3779
                ? view.expression.trim()
6!
3780
                : view.expression(this.connection).getQuery()
6✔
3781
        return this.insertTypeormMetadataSql({
6✔
3782
            type: MetadataTableType.VIEW,
6✔
3783
            schema: schema,
6✔
3784
            name: name,
6✔
3785
            value: expression,
6✔
3786
        })
6✔
3787
    }
6✔
3788

26✔
3789
    /**
26✔
3790
     * Builds drop view sql.
26✔
3791
     */
26✔
3792
    protected dropViewSql(viewOrPath: View | string): Query {
26✔
3793
        return new Query(`DROP VIEW ${this.escapePath(viewOrPath)}`)
6✔
3794
    }
6✔
3795

26✔
3796
    /**
26✔
3797
     * Builds remove view sql.
26✔
3798
     */
26✔
3799
    protected async deleteViewDefinitionSql(
26✔
3800
        viewOrPath: View | string,
6✔
3801
    ): Promise<Query> {
6✔
3802
        const currentSchema = await this.getCurrentSchema()
6✔
3803

6✔
3804
        let { schema, tableName: name } = this.driver.parseTableName(viewOrPath)
6✔
3805

6✔
3806
        if (!schema) {
6!
3807
            schema = currentSchema
×
3808
        }
×
3809

6✔
3810
        return this.deleteTypeormMetadataSql({
6✔
3811
            type: MetadataTableType.VIEW,
6✔
3812
            schema,
6✔
3813
            name,
6✔
3814
        })
6✔
3815
    }
6✔
3816

26✔
3817
    /**
26✔
3818
     * Drops ENUM type from given schemas.
26✔
3819
     */
26✔
3820
    protected async dropEnumTypes(schemaNames: string): Promise<void> {
26✔
3821
        const selectDropsQuery =
1,340✔
3822
            `SELECT 'DROP TYPE IF EXISTS "' || n.nspname || '"."' || t.typname || '";' as "query" FROM "pg_type" "t" ` +
1,340✔
3823
            `INNER JOIN "pg_enum" "e" ON "e"."enumtypid" = "t"."oid" ` +
1,340✔
3824
            `INNER JOIN "pg_namespace" "n" ON "n"."oid" = "t"."typnamespace" ` +
1,340✔
3825
            `WHERE "n"."nspname" IN (${schemaNames}) GROUP BY "n"."nspname", "t"."typname"`
1,340✔
3826
        const dropQueries: ObjectLiteral[] = await this.query(selectDropsQuery)
1,340✔
3827
        await Promise.all(dropQueries.map((q) => this.query(q["query"])))
1,340✔
3828
    }
1,340✔
3829

26✔
3830
    /**
26✔
3831
     * Checks if enum with the given name exist in the database.
26✔
3832
     */
26✔
3833
    protected async hasEnumType(
26✔
3834
        table: Table,
114✔
3835
        column: TableColumn,
114✔
3836
    ): Promise<boolean> {
114✔
3837
        let { schema } = this.driver.parseTableName(table)
114✔
3838

114✔
3839
        if (!schema) {
114!
3840
            schema = await this.getCurrentSchema()
×
3841
        }
×
3842

114✔
3843
        const enumName = this.buildEnumName(table, column, false, true)
114✔
3844
        const sql =
114✔
3845
            `SELECT "n"."nspname", "t"."typname" FROM "pg_type" "t" ` +
114✔
3846
            `INNER JOIN "pg_namespace" "n" ON "n"."oid" = "t"."typnamespace" ` +
114✔
3847
            `WHERE "n"."nspname" = '${schema}' AND "t"."typname" = '${enumName}'`
114✔
3848
        const result = await this.query(sql)
114✔
3849
        return result.length ? true : false
114✔
3850
    }
114✔
3851

26✔
3852
    /**
26✔
3853
     * Builds create ENUM type sql.
26✔
3854
     */
26✔
3855
    protected createEnumTypeSql(
26✔
3856
        table: Table,
118✔
3857
        column: TableColumn,
118✔
3858
        enumName?: string,
118✔
3859
    ): Query {
118✔
3860
        if (!enumName) enumName = this.buildEnumName(table, column)
118✔
3861
        const enumValues = column
118✔
3862
            .enum!.map((value) => `'${value.replaceAll("'", "''")}'`)
118✔
3863
            .join(", ")
118✔
3864
        return new Query(`CREATE TYPE ${enumName} AS ENUM(${enumValues})`)
118✔
3865
    }
118✔
3866

26✔
3867
    /**
26✔
3868
     * Builds create ENUM type sql.
26✔
3869
     */
26✔
3870
    protected dropEnumTypeSql(
26✔
3871
        table: Table,
118✔
3872
        column: TableColumn,
118✔
3873
        enumName?: string,
118✔
3874
    ): Query {
118✔
3875
        if (!enumName) enumName = this.buildEnumName(table, column)
118✔
3876
        return new Query(`DROP TYPE ${enumName}`)
118✔
3877
    }
118✔
3878

26✔
3879
    /**
26✔
3880
     * Builds create index sql.
26✔
3881
     * UNIQUE indices creates as UNIQUE constraints.
26✔
3882
     */
26✔
3883
    protected createIndexSql(table: Table, index: TableIndex): Query {
26✔
3884
        const columns = index.columnNames
2,973✔
3885
            .map((columnName) => `"${columnName}"`)
2,973✔
3886
            .join(", ")
2,973✔
3887
        return new Query(
2,973✔
3888
            `CREATE ${index.isUnique ? "UNIQUE " : ""}INDEX "${
2,973✔
3889
                index.name
2,973✔
3890
            }" ON ${this.escapePath(table)} ${
2,973✔
3891
                index.isSpatial ? "USING GiST " : ""
2,973✔
3892
            }(${columns}) ${index.where ? "WHERE " + index.where : ""}`,
2,973!
3893
        )
2,973✔
3894
    }
2,973✔
3895

26✔
3896
    /**
26✔
3897
     * Builds drop index sql.
26✔
3898
     */
26✔
3899
    protected dropIndexSql(
26✔
3900
        table: Table,
3,009✔
3901
        indexOrName: TableIndex | TableUnique | string,
3,009✔
3902
    ): Query {
3,009✔
3903
        const indexName =
3,009✔
3904
            InstanceChecker.isTableIndex(indexOrName) ||
3,009✔
3905
            InstanceChecker.isTableUnique(indexOrName)
36✔
3906
                ? indexOrName.name
3,009✔
3907
                : indexOrName
3,009✔
3908
        return new Query(
3,009✔
3909
            `DROP INDEX ${this.escapePath(table)}@"${indexName}" CASCADE`,
3,009✔
3910
        )
3,009✔
3911
    }
3,009✔
3912

26✔
3913
    /**
26✔
3914
     * Builds create primary key sql.
26✔
3915
     */
26✔
3916
    protected createPrimaryKeySql(
26✔
3917
        table: Table,
×
3918
        columnNames: string[],
×
3919
        constraintName?: string,
×
3920
    ): Query {
×
3921
        const primaryKeyName = constraintName
×
3922
            ? constraintName
×
3923
            : this.connection.namingStrategy.primaryKeyName(table, columnNames)
×
3924
        const columnNamesString = columnNames
×
3925
            .map((columnName) => `"${columnName}"`)
×
3926
            .join(", ")
×
3927
        return new Query(
×
3928
            `ALTER TABLE ${this.escapePath(
×
3929
                table,
×
3930
            )} ADD CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNamesString})`,
×
3931
        )
×
3932
    }
×
3933

26✔
3934
    /**
26✔
3935
     * Builds drop primary key sql.
26✔
3936
     */
26✔
3937
    protected dropPrimaryKeySql(table: Table): Query {
26✔
3938
        if (!table.primaryColumns.length)
×
3939
            throw new TypeORMError(`Table ${table} has no primary keys.`)
×
3940

×
3941
        const columnNames = table.primaryColumns.map((column) => column.name)
×
3942
        const constraintName = table.primaryColumns[0].primaryKeyConstraintName
×
3943
        const primaryKeyName = constraintName
×
3944
            ? constraintName
×
3945
            : this.connection.namingStrategy.primaryKeyName(table, columnNames)
×
3946
        return new Query(
×
3947
            `ALTER TABLE ${this.escapePath(
×
3948
                table,
×
3949
            )} DROP CONSTRAINT "${primaryKeyName}"`,
×
3950
        )
×
3951
    }
×
3952

26✔
3953
    /**
26✔
3954
     * Builds create unique constraint sql.
26✔
3955
     */
26✔
3956
    protected createUniqueConstraintSql(
26✔
3957
        table: Table,
36✔
3958
        uniqueConstraint: TableUnique | TableIndex,
36✔
3959
    ): Query {
36✔
3960
        const columnNames = uniqueConstraint.columnNames
36✔
3961
            .map((column) => `"` + column + `"`)
36✔
3962
            .join(", ")
36✔
3963
        return new Query(
36✔
3964
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
36✔
3965
                uniqueConstraint.name
36✔
3966
            }" UNIQUE (${columnNames})`,
36✔
3967
        )
36✔
3968
    }
36✔
3969

26✔
3970
    /**
26✔
3971
     * Builds drop unique constraint sql.
26✔
3972
     */
26✔
3973
    protected dropUniqueConstraintSql(
26✔
3974
        table: Table,
×
3975
        uniqueOrName: TableUnique | string,
×
3976
    ): Query {
×
3977
        const uniqueName = InstanceChecker.isTableUnique(uniqueOrName)
×
3978
            ? uniqueOrName.name
×
3979
            : uniqueOrName
×
3980
        return new Query(
×
3981
            `ALTER TABLE ${this.escapePath(
×
3982
                table,
×
3983
            )} DROP CONSTRAINT "${uniqueName}"`,
×
3984
        )
×
3985
    }
×
3986

26✔
3987
    /**
26✔
3988
     * Builds create check constraint sql.
26✔
3989
     */
26✔
3990
    protected createCheckConstraintSql(
26✔
3991
        table: Table,
10✔
3992
        checkConstraint: TableCheck,
10✔
3993
    ): Query {
10✔
3994
        return new Query(
10✔
3995
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
10✔
3996
                checkConstraint.name
10✔
3997
            }" CHECK (${checkConstraint.expression})`,
10✔
3998
        )
10✔
3999
    }
10✔
4000

26✔
4001
    /**
26✔
4002
     * Builds drop check constraint sql.
26✔
4003
     */
26✔
4004
    protected dropCheckConstraintSql(
26✔
4005
        table: Table,
10✔
4006
        checkOrName: TableCheck | string,
10✔
4007
    ): Query {
10✔
4008
        const checkName = InstanceChecker.isTableCheck(checkOrName)
10✔
4009
            ? checkOrName.name
10✔
4010
            : checkOrName
10!
4011
        return new Query(
10✔
4012
            `ALTER TABLE ${this.escapePath(
10✔
4013
                table,
10✔
4014
            )} DROP CONSTRAINT "${checkName}"`,
10✔
4015
        )
10✔
4016
    }
10✔
4017

26✔
4018
    /**
26✔
4019
     * Builds create foreign key sql.
26✔
4020
     */
26✔
4021
    protected createForeignKeySql(
26✔
4022
        table: Table,
2,840✔
4023
        foreignKey: TableForeignKey,
2,840✔
4024
    ): Query {
2,840✔
4025
        const columnNames = foreignKey.columnNames
2,840✔
4026
            .map((column) => `"` + column + `"`)
2,840✔
4027
            .join(", ")
2,840✔
4028
        const referencedColumnNames = foreignKey.referencedColumnNames
2,840✔
4029
            .map((column) => `"` + column + `"`)
2,840✔
4030
            .join(",")
2,840✔
4031
        let sql =
2,840✔
4032
            `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${
2,840✔
4033
                foreignKey.name
2,840✔
4034
            }" FOREIGN KEY (${columnNames}) ` +
2,840✔
4035
            `REFERENCES ${this.escapePath(
2,840✔
4036
                this.getTablePath(foreignKey),
2,840✔
4037
            )}(${referencedColumnNames})`
2,840✔
4038
        if (foreignKey.onDelete) sql += ` ON DELETE ${foreignKey.onDelete}`
2,840✔
4039
        if (foreignKey.onUpdate) sql += ` ON UPDATE ${foreignKey.onUpdate}`
2,840✔
4040

2,840✔
4041
        return new Query(sql)
2,840✔
4042
    }
2,840✔
4043

26✔
4044
    /**
26✔
4045
     * Builds drop foreign key sql.
26✔
4046
     */
26✔
4047
    protected dropForeignKeySql(
26✔
4048
        table: Table,
2,845✔
4049
        foreignKeyOrName: TableForeignKey | string,
2,845✔
4050
    ): Query {
2,845✔
4051
        const foreignKeyName = InstanceChecker.isTableForeignKey(
2,845✔
4052
            foreignKeyOrName,
2,845✔
4053
        )
2,845✔
4054
            ? foreignKeyOrName.name
2,845✔
4055
            : foreignKeyOrName
2,845!
4056
        return new Query(
2,845✔
4057
            `ALTER TABLE ${this.escapePath(
2,845✔
4058
                table,
2,845✔
4059
            )} DROP CONSTRAINT "${foreignKeyName}"`,
2,845✔
4060
        )
2,845✔
4061
    }
2,845✔
4062

26✔
4063
    /**
26✔
4064
     * Builds sequence name from given table and column.
26✔
4065
     */
26✔
4066
    protected buildSequenceName(
26✔
4067
        table: Table,
7,875✔
4068
        columnOrName: TableColumn | string,
7,875✔
4069
    ): string {
7,875✔
4070
        const { tableName } = this.driver.parseTableName(table)
7,875✔
4071

7,875✔
4072
        const columnName = InstanceChecker.isTableColumn(columnOrName)
7,875✔
4073
            ? columnOrName.name
7,875✔
4074
            : columnOrName
7,875!
4075

7,875✔
4076
        return `${tableName}_${columnName}_seq`
7,875✔
4077
    }
7,875✔
4078

26✔
4079
    protected buildSequencePath(
26✔
4080
        table: Table,
7,875✔
4081
        columnOrName: TableColumn | string,
7,875✔
4082
    ): string {
7,875✔
4083
        const { schema } = this.driver.parseTableName(table)
7,875✔
4084

7,875✔
4085
        return schema
7,875✔
4086
            ? `${schema}.${this.buildSequenceName(table, columnOrName)}`
7,875✔
4087
            : this.buildSequenceName(table, columnOrName)
7,875!
4088
    }
7,875✔
4089

26✔
4090
    /**
26✔
4091
     * Builds ENUM type name from given table and column.
26✔
4092
     */
26✔
4093
    protected buildEnumName(
26✔
4094
        table: Table,
499✔
4095
        column: TableColumn,
499✔
4096
        withSchema: boolean = true,
499✔
4097
        disableEscape?: boolean,
499✔
4098
        toOld?: boolean,
499✔
4099
    ): string {
499✔
4100
        const { schema, tableName } = this.driver.parseTableName(table)
499✔
4101
        let enumName = column.enumName
499✔
4102
            ? column.enumName
499✔
4103
            : `${tableName}_${column.name.toLowerCase()}_enum`
499✔
4104
        if (schema && withSchema) enumName = `${schema}.${enumName}`
499✔
4105
        if (toOld) enumName = enumName + "_old"
499✔
4106
        return enumName
499✔
4107
            .split(".")
499✔
4108
            .map((i) => {
499✔
4109
                return disableEscape ? i : `"${i}"`
744✔
4110
            })
499✔
4111
            .join(".")
499✔
4112
    }
499✔
4113

26✔
4114
    protected async getUserDefinedTypeName(table: Table, column: TableColumn) {
26✔
4115
        let { schema, tableName: name } = this.driver.parseTableName(table)
7✔
4116

7✔
4117
        if (!schema) {
7!
4118
            schema = await this.getCurrentSchema()
×
4119
        }
×
4120

7✔
4121
        const result = await this.query(
7✔
4122
            `SELECT "udt_schema", "udt_name" ` +
7✔
4123
                `FROM "information_schema"."columns" WHERE "table_schema" = '${schema}' AND "table_name" = '${name}' AND "column_name"='${column.name}'`,
7✔
4124
        )
7✔
4125

7✔
4126
        // docs: https://www.postgresql.org/docs/current/xtypes.html
7✔
4127
        // When you define a new base type, PostgreSQL automatically provides support for arrays of that type.
7✔
4128
        // The array type typically has the same name as the base type with the underscore character (_) prepended.
7✔
4129
        // ----
7✔
4130
        // so, we must remove this underscore character from enum type name
7✔
4131
        let udtName = result[0]["udt_name"]
7✔
4132
        if (udtName.indexOf("_") === 0) {
7✔
4133
            udtName = udtName.substr(1, udtName.length)
2✔
4134
        }
2✔
4135
        return {
7✔
4136
            schema: result[0]["udt_schema"],
7✔
4137
            name: udtName,
7✔
4138
        }
7✔
4139
    }
7✔
4140

26✔
4141
    /**
26✔
4142
     * Escapes a given comment so it's safe to include in a query.
26✔
4143
     */
26✔
4144
    protected escapeComment(comment?: string) {
26✔
4145
        if (comment === undefined || comment.length === 0) {
68✔
4146
            return "NULL"
6✔
4147
        }
6✔
4148

62✔
4149
        comment = comment.replace(/'/g, "''").replace(/\u0000/g, "") // Null bytes aren't allowed in comments
62✔
4150

62✔
4151
        return `'${comment}'`
62✔
4152
    }
62✔
4153

26✔
4154
    /**
26✔
4155
     * Escapes given table or view path.
26✔
4156
     */
26✔
4157
    protected escapePath(target: Table | View | string): string {
26✔
4158
        const { schema, tableName } = this.driver.parseTableName(target)
31,771✔
4159

31,771✔
4160
        if (schema && schema !== this.driver.searchSchema) {
31,771✔
4161
            return `"${schema}"."${tableName}"`
2✔
4162
        }
2✔
4163

31,769✔
4164
        return `"${tableName}"`
31,769✔
4165
    }
31,769✔
4166

26✔
4167
    /**
26✔
4168
     * Builds a query for create column.
26✔
4169
     */
26✔
4170
    protected buildCreateColumnSql(table: Table, column: TableColumn) {
26✔
4171
        let c = '"' + column.name + '"'
14,349✔
4172

14,349✔
4173
        if (column.isGenerated) {
14,349✔
4174
            if (column.generationStrategy === "increment") {
2,762✔
4175
                c += ` INT DEFAULT nextval('${this.escapePath(
2,625✔
4176
                    this.buildSequencePath(table, column),
2,625✔
4177
                )}')`
2,625✔
4178
            } else if (column.generationStrategy === "rowid") {
2,762✔
4179
                c += " INT DEFAULT unique_rowid()"
4✔
4180
            } else if (column.generationStrategy === "uuid") {
137✔
4181
                c += " UUID DEFAULT gen_random_uuid()"
133✔
4182
            }
133✔
4183
        }
2,762✔
4184

14,349✔
4185
        if (column.type === "enum" || column.type === "simple-enum") {
14,349✔
4186
            c += " " + this.buildEnumName(table, column)
118✔
4187
            if (column.isArray) c += " array"
118✔
4188
        } else if (!column.isGenerated) {
14,349✔
4189
            c += " " + this.connection.driver.createFullType(column)
11,469✔
4190
        }
11,469✔
4191

14,349✔
4192
        if (column.asExpression) {
14,349✔
4193
            c += ` AS (${column.asExpression}) ${
39✔
4194
                column.generatedType ? column.generatedType : "VIRTUAL"
39!
4195
            }`
39✔
4196
        } else {
14,349✔
4197
            if (column.charset) c += ' CHARACTER SET "' + column.charset + '"'
14,310!
4198
            if (column.collation) c += ' COLLATE "' + column.collation + '"'
14,310✔
4199
        }
14,310✔
4200

14,349✔
4201
        if (!column.isNullable) c += " NOT NULL"
14,349✔
4202
        if (
14,349✔
4203
            !column.isGenerated &&
14,349✔
4204
            column.default !== undefined &&
14,349✔
4205
            column.default !== null
718✔
4206
        )
14,349✔
4207
            c += " DEFAULT " + column.default
14,349✔
4208

14,349✔
4209
        return c
14,349✔
4210
    }
14,349✔
4211
    /**
26✔
4212
     * Change table comment.
26✔
4213
     */
26✔
4214
    changeTableComment(
26✔
4215
        tableOrName: Table | string,
×
4216
        comment?: string,
×
4217
    ): Promise<void> {
×
4218
        throw new TypeORMError(
×
4219
            `cockroachdb driver does not support change table comment.`,
×
4220
        )
×
4221
    }
×
4222
}
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