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

typeorm / typeorm / 14081280213

26 Mar 2025 10:41AM UTC coverage: 72.384% (+0.004%) from 72.38%
14081280213

push

github

web-flow
refactor: database server version fetching & comparison (#11357)

8658 of 12643 branches covered (68.48%)

Branch coverage included in aggregate %.

37 of 59 new or added lines in 13 files covered. (62.71%)

3 existing lines in 3 files now uncovered.

17889 of 24032 relevant lines covered (74.44%)

159593.23 hits per line

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

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

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

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

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

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

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

61
    /**
62
     * Stores all executed queries to be able to run them again if transaction fails.
63
     */
64
    protected queries: { query: string; parameters?: any[] }[] = []
35,625✔
65

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

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

76
    // -------------------------------------------------------------------------
77
    // Constructor
78
    // -------------------------------------------------------------------------
79

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

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

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

100
        if (this.databaseConnectionPromise)
35,748✔
101
            return this.databaseConnectionPromise
186✔
102

103
        if (this.mode === "slave" && this.driver.isReplicated) {
35,562!
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 {
124
            // master
125
            this.databaseConnectionPromise = this.driver
35,562✔
126
                .obtainMasterConnection()
127
                .then(([connection, release]: any[]) => {
128
                    this.driver.connectedQueryRunners.push(this)
35,562✔
129
                    this.databaseConnection = connection
35,562✔
130

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

142
                    return this.databaseConnection
35,562✔
143
                })
144
        }
145

146
        return this.databaseConnectionPromise
35,562✔
147
    }
148

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

158
        this.isReleased = true
35,625✔
159
        if (this.releaseCallback) {
35,625✔
160
            this.releaseCallback(err)
35,562✔
161
            this.releaseCallback = undefined
35,562✔
162
        }
163

164
        const index = this.driver.connectedQueryRunners.indexOf(this)
35,625✔
165

166
        if (index !== -1) {
35,625✔
167
            this.driver.connectedQueryRunners.splice(index, 1)
35,562✔
168
        }
169
    }
170

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

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

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

204
        this.transactionDepth += 1
21,819✔
205
        this.storeQueries = true
21,819✔
206

207
        await this.broadcaster.broadcast("AfterTransactionStart")
21,819✔
208
    }
209

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

217
        await this.broadcaster.broadcast("BeforeTransactionCommit")
21,735✔
218

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

234
        await this.broadcaster.broadcast("AfterTransactionCommit")
21,735✔
235
    }
236

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

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

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

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

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

272
        const databaseConnection = await this.connect()
247,860✔
273
        const broadcasterResult = new BroadcasterResult()
247,860✔
274

275
        this.driver.connection.logger.logQuery(query, parameters, this)
247,860✔
276
        this.broadcaster.broadcastBeforeQueryEvent(
247,860✔
277
            broadcasterResult,
278
            query,
279
            parameters,
280
        )
281

282
        const queryStartTime = +new Date()
247,860✔
283

284
        if (this.isTransactionActive && this.storeQueries) {
247,860✔
285
            this.queries.push({ query, parameters })
64,215✔
286
        }
287

288
        try {
247,860✔
289
            const raw = await new Promise<any>((ok, fail) => {
247,860✔
290
                databaseConnection.query(
247,860✔
291
                    query,
292
                    parameters,
293
                    (err: any, raw: any) => (err ? fail(err) : ok(raw)),
247,860✔
294
                )
295
            })
296

297
            // log slow queries if maxQueryExecution time is set
298
            const maxQueryExecutionTime =
299
                this.driver.options.maxQueryExecutionTime
247,821✔
300
            const queryEndTime = +new Date()
247,821✔
301
            const queryExecutionTime = queryEndTime - queryStartTime
247,821✔
302
            if (
247,821!
303
                maxQueryExecutionTime &&
247,821!
304
                queryExecutionTime > maxQueryExecutionTime
305
            ) {
306
                this.driver.connection.logger.logQuerySlow(
×
307
                    queryExecutionTime,
308
                    query,
309
                    parameters,
310
                    this,
311
                )
312
            }
313

314
            const result = new QueryResult()
247,821✔
315

316
            if (raw.hasOwnProperty("rowCount")) {
247,821✔
317
                result.affected = raw.rowCount
247,722✔
318
            }
319

320
            if (raw.hasOwnProperty("rows")) {
247,821✔
321
                result.records = raw.rows
247,722✔
322
            }
323

324
            switch (raw.command) {
247,821✔
325
                case "DELETE":
326
                    // for DELETE query additionally return number of affected rows
327
                    result.raw = [raw.rows, raw.rowCount]
324✔
328
                    break
324✔
329
                default:
330
                    result.raw = raw.rows
247,497✔
331
            }
332

333
            this.broadcaster.broadcastAfterQueryEvent(
247,821✔
334
                broadcasterResult,
335
                query,
336
                parameters,
337
                true,
338
                queryExecutionTime,
339
                raw,
340
                undefined,
341
            )
342

343
            if (useStructuredResult) {
247,821✔
344
                return result
43,647✔
345
            } else {
346
                return result.raw
204,174✔
347
            }
348
        } catch (err) {
349
            if (
39✔
350
                err.code === "40001" &&
63✔
351
                this.isTransactionActive &&
352
                this.transactionRetries <
353
                    (this.driver.options.maxTransactionRetries || 5)
24✔
354
            ) {
355
                this.transactionRetries += 1
12✔
356
                this.storeQueries = false
12✔
357
                await this.query("ROLLBACK TO SAVEPOINT cockroach_restart")
12✔
358
                const sleepTime =
359
                    2 ** this.transactionRetries *
12✔
360
                    0.1 *
361
                    (Math.random() + 0.5) *
362
                    1000
363
                await new Promise((resolve) => setTimeout(resolve, sleepTime))
12✔
364

365
                let result = undefined
12✔
366
                for (const q of this.queries) {
12✔
367
                    this.driver.connection.logger.logQuery(
15✔
368
                        `Retrying transaction for query "${q.query}"`,
369
                        q.parameters,
370
                        this,
371
                    )
372
                    result = await this.query(q.query, q.parameters)
15✔
373
                }
374
                this.transactionRetries = 0
12✔
375
                this.storeQueries = true
12✔
376

377
                return result
12✔
378
            } else {
379
                this.driver.connection.logger.logQueryError(
27✔
380
                    err,
381
                    query,
382
                    parameters,
383
                    this,
384
                )
385
                this.broadcaster.broadcastAfterQueryEvent(
27✔
386
                    broadcasterResult,
387
                    query,
388
                    parameters,
389
                    false,
390
                    undefined,
391
                    undefined,
392
                    err,
393
                )
394
                throw new QueryFailedError(query, parameters, err)
27✔
395
            }
396
        } finally {
397
            await broadcasterResult.wait()
247,860✔
398
        }
399
    }
400

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

415
        const databaseConnection = await this.connect()
6✔
416
        this.driver.connection.logger.logQuery(query, parameters, this)
6✔
417
        const stream = databaseConnection.query(
6✔
418
            new QueryStream(query, parameters),
419
        )
420

421
        if (onEnd) {
6✔
422
            stream.on("end", onEnd)
3✔
423
        }
424

425
        if (onError) {
6✔
426
            stream.on("error", onError)
3✔
427
        }
428

429
        return stream
6✔
430
    }
431

432
    /**
433
     * Returns all available database names including system databases.
434
     */
435
    async getDatabases(): Promise<string[]> {
436
        return Promise.resolve([])
×
437
    }
438

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

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

457
    /**
458
     * Loads currently using database
459
     */
460
    async getCurrentDatabase(): Promise<string> {
461
        const query = await this.query(`SELECT * FROM current_database()`)
4,752✔
462
        return query[0]["current_database"]
4,752✔
463
    }
464

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

475
    /**
476
     * Loads currently using database schema
477
     */
478
    async getCurrentSchema(): Promise<string> {
479
        const query = await this.query(`SELECT * FROM current_schema()`)
6,249✔
480
        return query[0]["current_schema"]
6,249✔
481
    }
482

483
    /**
484
     * Checks if table with the given name exist in the database.
485
     */
486
    async hasTable(tableOrName: Table | string): Promise<boolean> {
487
        const parsedTableName = this.driver.parseTableName(tableOrName)
4,122✔
488

489
        if (!parsedTableName.schema) {
4,122!
490
            parsedTableName.schema = await this.getCurrentSchema()
×
491
        }
492

493
        const sql = `SELECT * FROM "information_schema"."tables" WHERE "table_schema" = '${parsedTableName.schema}' AND "table_name" = '${parsedTableName.tableName}'`
4,122✔
494
        const result = await this.query(sql)
4,122✔
495
        return result.length ? true : false
4,122✔
496
    }
497

498
    /**
499
     * Checks if column with the given name exist in the given table.
500
     */
501
    async hasColumn(
502
        tableOrName: Table | string,
503
        columnName: string,
504
    ): Promise<boolean> {
505
        const parsedTableName = this.driver.parseTableName(tableOrName)
12✔
506

507
        if (!parsedTableName.schema) {
12!
508
            parsedTableName.schema = await this.getCurrentSchema()
×
509
        }
510

511
        const sql = `SELECT * FROM "information_schema"."columns" WHERE "table_schema" = '${parsedTableName.schema}' AND "table_name" = '${parsedTableName.tableName}' AND "column_name" = '${columnName}'`
12✔
512
        const result = await this.query(sql)
12✔
513
        return result.length ? true : false
12✔
514
    }
515

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

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

539
    /**
540
     * Creates a new table schema.
541
     */
542
    async createSchema(
543
        schemaPath: string,
544
        ifNotExist?: boolean,
545
    ): Promise<void> {
546
        const schema =
547
            schemaPath.indexOf(".") === -1
3!
548
                ? schemaPath
549
                : schemaPath.split(".")[1]
550

551
        const up = ifNotExist
3!
552
            ? `CREATE SCHEMA IF NOT EXISTS "${schema}"`
553
            : `CREATE SCHEMA "${schema}"`
554
        const down = `DROP SCHEMA "${schema}" CASCADE`
3✔
555
        await this.executeQueries(new Query(up), new Query(down))
3✔
556
    }
557

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

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

578
    /**
579
     * Creates a new table.
580
     */
581
    async createTable(
582
        table: Table,
583
        ifNotExist: boolean = false,
69✔
584
        createForeignKeys: boolean = true,
135✔
585
        createIndices: boolean = true,
13,002✔
586
    ): Promise<void> {
587
        if (ifNotExist) {
13,002✔
588
            const isTableExist = await this.hasTable(table)
66✔
589
            if (isTableExist) return Promise.resolve()
66✔
590
        }
591
        const upQueries: Query[] = []
12,999✔
592
        const downQueries: Query[] = []
12,999✔
593

594
        // if table have column with ENUM type, we must create this type in postgres.
595
        const enumColumns = table.columns.filter(
12,999✔
596
            (column) => column.type === "enum" || column.type === "simple-enum",
41,157✔
597
        )
598
        const createdEnumTypes: string[] = []
12,999✔
599
        for (const column of enumColumns) {
12,999✔
600
            // TODO: Should also check if values of existing type matches expected ones
601
            const hasEnum = await this.hasEnumType(table, column)
318✔
602
            const enumName = this.buildEnumName(table, column)
318✔
603

604
            // if enum with the same "enumName" is defined more then once, me must prevent double creation
605
            if (!hasEnum && createdEnumTypes.indexOf(enumName) === -1) {
318✔
606
                createdEnumTypes.push(enumName)
312✔
607
                upQueries.push(this.createEnumTypeSql(table, column, enumName))
312✔
608
                downQueries.push(this.dropEnumTypeSql(table, column, enumName))
312✔
609
            }
610
        }
611

612
        table.columns
12,999✔
613
            .filter(
614
                (column) =>
615
                    column.isGenerated &&
41,157✔
616
                    column.generationStrategy === "increment",
617
            )
618
            .forEach((column) => {
619
                upQueries.push(
7,533✔
620
                    new Query(
621
                        `CREATE SEQUENCE ${this.escapePath(
622
                            this.buildSequencePath(table, column),
623
                        )}`,
624
                    ),
625
                )
626
                downQueries.push(
7,533✔
627
                    new Query(
628
                        `DROP SEQUENCE ${this.escapePath(
629
                            this.buildSequencePath(table, column),
630
                        )}`,
631
                    ),
632
                )
633
            })
634

635
        upQueries.push(this.createTableSql(table, createForeignKeys))
12,999✔
636
        downQueries.push(this.dropTableSql(table))
12,999✔
637

638
        // if createForeignKeys is true, we must drop created foreign keys in down query.
639
        // createTable does not need separate method to create foreign keys, because it create fk's in the same query with table creation.
640
        if (createForeignKeys)
12,999✔
641
            table.foreignKeys.forEach((foreignKey) =>
132✔
642
                downQueries.push(this.dropForeignKeySql(table, foreignKey)),
9✔
643
            )
644

645
        if (createIndices) {
12,999✔
646
            table.indices
12,999✔
647
                .filter((index) => !index.isUnique)
8,643✔
648
                .forEach((index) => {
649
                    // new index may be passed without name. In this case we generate index name manually.
650
                    if (!index.name)
8,634✔
651
                        index.name = this.connection.namingStrategy.indexName(
6✔
652
                            table,
653
                            index.columnNames,
654
                            index.where,
655
                        )
656
                    upQueries.push(this.createIndexSql(table, index))
8,634✔
657
                    downQueries.push(this.dropIndexSql(table, index))
8,634✔
658
                })
659
        }
660

661
        // if table have column with generated type, we must add the expression to the metadata table
662
        const generatedColumns = table.columns.filter(
12,999✔
663
            (column) => column.generatedType && column.asExpression,
41,157✔
664
        )
665

666
        for (const column of generatedColumns) {
12,999✔
667
            const currentSchema = await this.getCurrentSchema()
84✔
668
            let { schema } = this.driver.parseTableName(table)
84✔
669
            if (!schema) {
84!
670
                schema = currentSchema
×
671
            }
672

673
            const insertQuery = this.insertTypeormMetadataSql({
84✔
674
                schema: schema,
675
                table: table.name,
676
                type: MetadataTableType.GENERATED_COLUMN,
677
                name: column.name,
678
                value: column.asExpression,
679
            })
680

681
            const deleteQuery = this.deleteTypeormMetadataSql({
84✔
682
                schema: schema,
683
                table: table.name,
684
                type: MetadataTableType.GENERATED_COLUMN,
685
                name: column.name,
686
            })
687

688
            upQueries.push(insertQuery)
84✔
689
            downQueries.push(deleteQuery)
84✔
690
        }
691

692
        await this.executeQueries(upQueries, downQueries)
12,999✔
693
    }
694

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

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

718
        // foreign keys must be dropped before indices, because fk's rely on indices
719
        if (dropForeignKeys)
18✔
720
            table.foreignKeys.forEach((foreignKey) =>
18✔
721
                upQueries.push(this.dropForeignKeySql(table, foreignKey)),
6✔
722
            )
723

724
        if (dropIndices) {
18✔
725
            table.indices.forEach((index) => {
18✔
726
                upQueries.push(this.dropIndexSql(table, index))
9✔
727
                downQueries.push(this.createIndexSql(table, index))
9✔
728
            })
729
        }
730

731
        upQueries.push(this.dropTableSql(table))
18✔
732
        downQueries.push(this.createTableSql(table, createForeignKeys))
18✔
733

734
        table.columns
18✔
735
            .filter(
736
                (column) =>
737
                    column.isGenerated &&
84✔
738
                    column.generationStrategy === "increment",
739
            )
740
            .forEach((column) => {
741
                upQueries.push(
15✔
742
                    new Query(
743
                        `DROP SEQUENCE ${this.escapePath(
744
                            this.buildSequencePath(table, column),
745
                        )}`,
746
                    ),
747
                )
748
                downQueries.push(
15✔
749
                    new Query(
750
                        `CREATE SEQUENCE ${this.escapePath(
751
                            this.buildSequencePath(table, column),
752
                        )}`,
753
                    ),
754
                )
755
            })
756

757
        // if table had columns with generated type, we must remove the expression from the metadata table
758
        const generatedColumns = table.columns.filter(
18✔
759
            (column) => column.generatedType && column.asExpression,
84✔
760
        )
761

762
        for (const column of generatedColumns) {
18✔
763
            const currentSchema = await this.getCurrentSchema()
12✔
764
            let { schema } = this.driver.parseTableName(table)
12✔
765
            if (!schema) {
12!
766
                schema = currentSchema
×
767
            }
768

769
            const deleteQuery = this.deleteTypeormMetadataSql({
12✔
770
                schema: schema,
771
                table: table.name,
772
                type: MetadataTableType.GENERATED_COLUMN,
773
                name: column.name,
774
            })
775

776
            const insertQuery = this.insertTypeormMetadataSql({
12✔
777
                schema: schema,
778
                table: table.name,
779
                type: MetadataTableType.GENERATED_COLUMN,
780
                name: column.name,
781
                value: column.asExpression,
782
            })
783

784
            upQueries.push(deleteQuery)
12✔
785
            downQueries.push(insertQuery)
12✔
786
        }
787

788
        await this.executeQueries(upQueries, downQueries)
18✔
789
    }
790

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

809
    /**
810
     * Drops the view.
811
     */
812
    async dropView(target: View | string): Promise<void> {
813
        const viewName = InstanceChecker.isView(target) ? target.name : target
×
814
        const view = await this.getCachedView(viewName)
×
815

816
        const upQueries: Query[] = []
×
817
        const downQueries: Query[] = []
×
818
        upQueries.push(await this.deleteViewDefinitionSql(view))
×
819
        upQueries.push(this.dropViewSql(view))
×
820
        downQueries.push(await this.insertViewDefinitionSql(view))
×
821
        downQueries.push(this.createViewSql(view))
×
822
        await this.executeQueries(upQueries, downQueries)
×
823
    }
824

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

839
        const { schema: schemaName, tableName: oldTableName } =
840
            this.driver.parseTableName(oldTable)
39✔
841

842
        newTable.name = schemaName
39!
843
            ? `${schemaName}.${newTableName}`
844
            : newTableName
845

846
        upQueries.push(
39✔
847
            new Query(
848
                `ALTER TABLE ${this.escapePath(
849
                    oldTable,
850
                )} RENAME TO "${newTableName}"`,
851
            ),
852
        )
853
        downQueries.push(
39✔
854
            new Query(
855
                `ALTER TABLE ${this.escapePath(
856
                    newTable,
857
                )} RENAME TO "${oldTableName}"`,
858
            ),
859
        )
860

861
        // rename column primary key constraint
862
        if (
39✔
863
            newTable.primaryColumns.length > 0 &&
78✔
864
            !newTable.primaryColumns[0].primaryKeyConstraintName
865
        ) {
866
            const columnNames = newTable.primaryColumns.map(
27✔
867
                (column) => column.name,
33✔
868
            )
869

870
            const oldPkName = this.connection.namingStrategy.primaryKeyName(
27✔
871
                oldTable,
872
                columnNames,
873
            )
874
            const newPkName = this.connection.namingStrategy.primaryKeyName(
27✔
875
                newTable,
876
                columnNames,
877
            )
878

879
            upQueries.push(
27✔
880
                new Query(
881
                    `ALTER TABLE ${this.escapePath(
882
                        newTable,
883
                    )} RENAME CONSTRAINT "${oldPkName}" TO "${newPkName}"`,
884
                ),
885
            )
886
            downQueries.push(
27✔
887
                new Query(
888
                    `ALTER TABLE ${this.escapePath(
889
                        newTable,
890
                    )} RENAME CONSTRAINT "${newPkName}" TO "${oldPkName}"`,
891
                ),
892
            )
893
        }
894

895
        // rename unique constraints
896
        newTable.uniques.forEach((unique) => {
39✔
897
            const oldUniqueName =
898
                this.connection.namingStrategy.uniqueConstraintName(
12✔
899
                    oldTable,
900
                    unique.columnNames,
901
                )
902

903
            // Skip renaming if Unique has user defined constraint name
904
            if (unique.name !== oldUniqueName) return
12✔
905

906
            // build new constraint name
907
            const newUniqueName =
908
                this.connection.namingStrategy.uniqueConstraintName(
×
909
                    newTable,
910
                    unique.columnNames,
911
                )
912

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

933
            // replace constraint name
934
            unique.name = newUniqueName
×
935
        })
936

937
        // rename index constraints
938
        newTable.indices.forEach((index) => {
39✔
939
            const oldIndexName = this.connection.namingStrategy.indexName(
36✔
940
                oldTable,
941
                index.columnNames,
942
                index.where,
943
            )
944

945
            // Skip renaming if Index has user defined constraint name
946
            if (index.name !== oldIndexName) return
36✔
947

948
            // build new constraint name
949
            const { schema } = this.driver.parseTableName(newTable)
24✔
950
            const newIndexName = this.connection.namingStrategy.indexName(
24✔
951
                newTable,
952
                index.columnNames,
953
                index.where,
954
            )
955

956
            // build queries
957
            const up = schema
24!
958
                ? `ALTER INDEX "${schema}"."${index.name}" RENAME TO "${newIndexName}"`
959
                : `ALTER INDEX "${index.name}" RENAME TO "${newIndexName}"`
960
            const down = schema
24!
961
                ? `ALTER INDEX "${schema}"."${newIndexName}" RENAME TO "${index.name}"`
962
                : `ALTER INDEX "${newIndexName}" RENAME TO "${index.name}"`
963
            upQueries.push(new Query(up))
24✔
964
            downQueries.push(new Query(down))
24✔
965

966
            // replace constraint name
967
            index.name = newIndexName
24✔
968
        })
969

970
        // rename foreign key constraints
971
        newTable.foreignKeys.forEach((foreignKey) => {
39✔
972
            const oldForeignKeyName =
973
                this.connection.namingStrategy.foreignKeyName(
24✔
974
                    oldTable,
975
                    foreignKey.columnNames,
976
                    this.getTablePath(foreignKey),
977
                    foreignKey.referencedColumnNames,
978
                )
979

980
            // Skip renaming if foreign key has user defined constraint name
981
            if (foreignKey.name !== oldForeignKeyName) return
24✔
982

983
            // build new constraint name
984
            const newForeignKeyName =
985
                this.connection.namingStrategy.foreignKeyName(
×
986
                    newTable,
987
                    foreignKey.columnNames,
988
                    this.getTablePath(foreignKey),
989
                    foreignKey.referencedColumnNames,
990
                )
991

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

1012
            // replace constraint name
1013
            foreignKey.name = newForeignKeyName
×
1014
        })
1015

1016
        // rename ENUM types
1017
        const enumColumns = newTable.columns.filter(
39✔
1018
            (column) => column.type === "enum" || column.type === "simple-enum",
96✔
1019
        )
1020
        for (const column of enumColumns) {
39✔
1021
            // skip renaming for user-defined enum name
1022
            if (column.enumName) continue
12✔
1023

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

1049
        await this.executeQueries(upQueries, downQueries)
39✔
1050
    }
1051

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

1066
        if (column.generationStrategy === "increment") {
63!
1067
            throw new TypeORMError(
×
1068
                `Adding sequential generated columns into existing table is not supported`,
1069
            )
1070
        }
1071

1072
        if (column.type === "enum" || column.type === "simple-enum") {
63✔
1073
            const hasEnum = await this.hasEnumType(table, column)
9✔
1074
            if (!hasEnum) {
9✔
1075
                upQueries.push(this.createEnumTypeSql(table, column))
9✔
1076
                downQueries.push(this.dropEnumTypeSql(table, column))
9✔
1077
            }
1078
        }
1079

1080
        upQueries.push(
63✔
1081
            new Query(
1082
                `ALTER TABLE ${this.escapePath(
1083
                    table,
1084
                )} ADD ${this.buildCreateColumnSql(table, column)}`,
1085
            ),
1086
        )
1087
        downQueries.push(
63✔
1088
            new Query(
1089
                `ALTER TABLE ${this.escapePath(table)} DROP COLUMN "${
1090
                    column.name
1091
                }"`,
1092
            ),
1093
        )
1094

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

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

1127
            primaryColumns.push(column)
×
1128
            const pkName = primaryColumns[0].primaryKeyConstraintName
×
1129
                ? primaryColumns[0].primaryKeyConstraintName
1130
                : this.connection.namingStrategy.primaryKeyName(
1131
                      clonedTable,
1132
                      primaryColumns.map((column) => column.name),
×
1133
                  )
1134

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

1154
        if (column.generatedType && column.asExpression) {
63✔
1155
            const currentSchema = await this.getCurrentSchema()
9✔
1156
            let { schema } = this.driver.parseTableName(table)
9✔
1157
            if (!schema) {
9!
1158
                schema = currentSchema
×
1159
            }
1160
            const insertQuery = this.insertTypeormMetadataSql({
9✔
1161
                schema: schema,
1162
                table: table.name,
1163
                type: MetadataTableType.GENERATED_COLUMN,
1164
                name: column.name,
1165
                value: column.asExpression,
1166
            })
1167

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

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

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

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

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

1238
        await this.executeQueries(upQueries, downQueries)
63✔
1239

1240
        clonedTable.addColumn(column)
60✔
1241
        this.replaceCachedTable(table, clonedTable)
60✔
1242
    }
1243

1244
    /**
1245
     * Creates a new columns from the column in the table.
1246
     */
1247
    async addColumns(
1248
        tableOrName: Table | string,
1249
        columns: TableColumn[],
1250
    ): Promise<void> {
1251
        for (const column of columns) {
12✔
1252
            await this.addColumn(tableOrName, column)
15✔
1253
        }
1254
    }
1255

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

1275
        let newColumn
1276
        if (InstanceChecker.isTableColumn(newTableColumnOrName)) {
39✔
1277
            newColumn = newTableColumnOrName
24✔
1278
        } else {
1279
            newColumn = oldColumn.clone()
15✔
1280
            newColumn.name = newTableColumnOrName
15✔
1281
        }
1282

1283
        return this.changeColumn(table, oldColumn, newColumn)
39✔
1284
    }
1285

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

1302
        const oldColumn = InstanceChecker.isTableColumn(oldTableColumnOrName)
252!
1303
            ? oldTableColumnOrName
1304
            : table.columns.find(
1305
                  (column) => column.name === oldTableColumnOrName,
×
1306
              )
1307
        if (!oldColumn)
252!
1308
            throw new TypeORMError(
×
1309
                `Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`,
1310
            )
1311

1312
        if (
252✔
1313
            oldColumn.type !== newColumn.type ||
1,176✔
1314
            oldColumn.length !== newColumn.length ||
1315
            newColumn.isArray !== oldColumn.isArray ||
1316
            oldColumn.generatedType !== newColumn.generatedType ||
1317
            oldColumn.asExpression !== newColumn.asExpression
1318
        ) {
1319
            // To avoid data conversion, we just recreate column
1320
            await this.dropColumn(table, oldColumn)
30✔
1321
            await this.addColumn(table, newColumn)
30✔
1322

1323
            // update cloned table
1324
            clonedTable = table.clone()
30✔
1325
        } else {
1326
            if (oldColumn.name !== newColumn.name) {
222✔
1327
                // rename column
1328
                upQueries.push(
66✔
1329
                    new Query(
1330
                        `ALTER TABLE ${this.escapePath(table)} RENAME COLUMN "${
1331
                            oldColumn.name
1332
                        }" TO "${newColumn.name}"`,
1333
                    ),
1334
                )
1335
                downQueries.push(
66✔
1336
                    new Query(
1337
                        `ALTER TABLE ${this.escapePath(table)} RENAME COLUMN "${
1338
                            newColumn.name
1339
                        }" TO "${oldColumn.name}"`,
1340
                    ),
1341
                )
1342

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

1373
                // rename column primary key constraint
1374
                if (
66✔
1375
                    oldColumn.isPrimary === true &&
87✔
1376
                    !oldColumn.primaryKeyConstraintName
1377
                ) {
1378
                    const primaryColumns = clonedTable.primaryColumns
15✔
1379

1380
                    // build old primary constraint name
1381
                    const columnNames = primaryColumns.map(
15✔
1382
                        (column) => column.name,
27✔
1383
                    )
1384
                    const oldPkName =
1385
                        this.connection.namingStrategy.primaryKeyName(
15✔
1386
                            clonedTable,
1387
                            columnNames,
1388
                        )
1389

1390
                    // replace old column name with new column name
1391
                    columnNames.splice(columnNames.indexOf(oldColumn.name), 1)
15✔
1392
                    columnNames.push(newColumn.name)
15✔
1393

1394
                    // build new primary constraint name
1395
                    const newPkName =
1396
                        this.connection.namingStrategy.primaryKeyName(
15✔
1397
                            clonedTable,
1398
                            columnNames,
1399
                        )
1400

1401
                    upQueries.push(
15✔
1402
                        new Query(
1403
                            `ALTER TABLE ${this.escapePath(
1404
                                table,
1405
                            )} RENAME CONSTRAINT "${oldPkName}" TO "${newPkName}"`,
1406
                        ),
1407
                    )
1408
                    downQueries.push(
15✔
1409
                        new Query(
1410
                            `ALTER TABLE ${this.escapePath(
1411
                                table,
1412
                            )} RENAME CONSTRAINT "${newPkName}" TO "${oldPkName}"`,
1413
                        ),
1414
                    )
1415
                }
1416

1417
                // rename unique constraints
1418
                clonedTable.findColumnUniques(oldColumn).forEach((unique) => {
66✔
1419
                    const oldUniqueName =
1420
                        this.connection.namingStrategy.uniqueConstraintName(
21✔
1421
                            clonedTable,
1422
                            unique.columnNames,
1423
                        )
1424

1425
                    // Skip renaming if Unique has user defined constraint name
1426
                    if (unique.name !== oldUniqueName) return
21✔
1427

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

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

1460
                    // replace constraint name
1461
                    unique.name = newUniqueName
9✔
1462
                })
1463

1464
                // rename index constraints
1465
                clonedTable.findColumnIndices(oldColumn).forEach((index) => {
66✔
1466
                    const oldIndexName =
1467
                        this.connection.namingStrategy.indexName(
33✔
1468
                            clonedTable,
1469
                            index.columnNames,
1470
                            index.where,
1471
                        )
1472

1473
                    // Skip renaming if Index has user defined constraint name
1474
                    if (index.name !== oldIndexName) return
33✔
1475

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

1490
                    // build queries
1491
                    const up = schema
27!
1492
                        ? `ALTER INDEX "${schema}"."${index.name}" RENAME TO "${newIndexName}"`
1493
                        : `ALTER INDEX "${index.name}" RENAME TO "${newIndexName}"`
1494
                    const down = schema
27!
1495
                        ? `ALTER INDEX "${schema}"."${newIndexName}" RENAME TO "${index.name}"`
1496
                        : `ALTER INDEX "${newIndexName}" RENAME TO "${index.name}"`
1497
                    upQueries.push(new Query(up))
27✔
1498
                    downQueries.push(new Query(down))
27✔
1499

1500
                    // replace constraint name
1501
                    index.name = newIndexName
27✔
1502
                })
1503

1504
                // rename foreign key constraints
1505
                clonedTable
66✔
1506
                    .findColumnForeignKeys(oldColumn)
1507
                    .forEach((foreignKey) => {
1508
                        const foreignKeyName =
1509
                            this.connection.namingStrategy.foreignKeyName(
27✔
1510
                                clonedTable,
1511
                                foreignKey.columnNames,
1512
                                this.getTablePath(foreignKey),
1513
                                foreignKey.referencedColumnNames,
1514
                            )
1515

1516
                        // Skip renaming if foreign key has user defined constraint name
1517
                        if (foreignKey.name !== foreignKeyName) return
27✔
1518

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

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

1553
                        // replace constraint name
1554
                        foreignKey.name = newForeignKeyName
3✔
1555
                    })
1556

1557
                // rename old column in the Table object
1558
                const oldTableColumn = clonedTable.columns.find(
66✔
1559
                    (column) => column.name === oldColumn.name,
156✔
1560
                )
1561
                clonedTable.columns[
66✔
1562
                    clonedTable.columns.indexOf(oldTableColumn!)
1563
                ].name = newColumn.name
1564
                oldColumn.name = newColumn.name
66✔
1565
            }
1566

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

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

1621
            if (oldColumn.comment !== newColumn.comment) {
222✔
1622
                upQueries.push(
15✔
1623
                    new Query(
1624
                        `COMMENT ON COLUMN ${this.escapePath(table)}."${
1625
                            oldColumn.name
1626
                        }" IS ${this.escapeComment(newColumn.comment)}`,
1627
                    ),
1628
                )
1629
                downQueries.push(
15✔
1630
                    new Query(
1631
                        `COMMENT ON COLUMN ${this.escapePath(table)}."${
1632
                            newColumn.name
1633
                        }" IS ${this.escapeComment(oldColumn.comment)}`,
1634
                    ),
1635
                )
1636
            }
1637

1638
            if (newColumn.isPrimary !== oldColumn.isPrimary) {
222!
1639
                const primaryColumns = clonedTable.primaryColumns
×
1640

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

1650
                    const columnNames = primaryColumns
×
1651
                        .map((column) => `"${column.name}"`)
×
1652
                        .join(", ")
1653

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

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

1684
                    const columnNames = primaryColumns
×
1685
                        .map((column) => `"${column.name}"`)
×
1686
                        .join(", ")
1687

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

1711
                    // update column in table
1712
                    const column = clonedTable.columns.find(
×
1713
                        (column) => column.name === newColumn.name,
×
1714
                    )
1715
                    column!.isPrimary = false
×
1716

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

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

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

1792
            if (
222✔
1793
                (newColumn.type === "enum" ||
558!
1794
                    newColumn.type === "simple-enum") &&
1795
                (oldColumn.type === "enum" ||
1796
                    oldColumn.type === "simple-enum") &&
1797
                (!OrmUtils.isArraysEqual(newColumn.enum!, oldColumn.enum!) ||
1798
                    newColumn.enumName !== oldColumn.enumName)
1799
            ) {
1800
                const arraySuffix = newColumn.isArray ? "[]" : ""
9!
1801

1802
                // "public"."new_enum"
1803
                const newEnumName = this.buildEnumName(table, newColumn)
9✔
1804

1805
                // "public"."old_enum"
1806
                const oldEnumName = this.buildEnumName(table, oldColumn)
9✔
1807

1808
                // "old_enum"
1809
                const oldEnumNameWithoutSchema = this.buildEnumName(
9✔
1810
                    table,
1811
                    oldColumn,
1812
                    false,
1813
                )
1814

1815
                //"public"."old_enum_old"
1816
                const oldEnumNameWithSchema_old = this.buildEnumName(
9✔
1817
                    table,
1818
                    oldColumn,
1819
                    true,
1820
                    false,
1821
                    true,
1822
                )
1823

1824
                //"old_enum_old"
1825
                const oldEnumNameWithoutSchema_old = this.buildEnumName(
9✔
1826
                    table,
1827
                    oldColumn,
1828
                    false,
1829
                    false,
1830
                    true,
1831
                )
1832

1833
                // rename old ENUM
1834
                upQueries.push(
9✔
1835
                    new Query(
1836
                        `ALTER TYPE ${oldEnumName} RENAME TO ${oldEnumNameWithoutSchema_old}`,
1837
                    ),
1838
                )
1839
                downQueries.push(
9✔
1840
                    new Query(
1841
                        `ALTER TYPE ${oldEnumNameWithSchema_old} RENAME TO ${oldEnumNameWithoutSchema}`,
1842
                    ),
1843
                )
1844

1845
                // create new ENUM
1846
                upQueries.push(
9✔
1847
                    this.createEnumTypeSql(table, newColumn, newEnumName),
1848
                )
1849
                downQueries.push(
9✔
1850
                    this.dropEnumTypeSql(table, newColumn, newEnumName),
1851
                )
1852

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

1878
                // build column types
1879
                const upType = `${newEnumName}${arraySuffix} USING "${newColumn.name}"::"text"::${newEnumName}${arraySuffix}`
9✔
1880
                const downType = `${oldEnumNameWithSchema_old}${arraySuffix} USING "${newColumn.name}"::"text"::${oldEnumNameWithSchema_old}${arraySuffix}`
9✔
1881

1882
                upQueries.push(
9✔
1883
                    new Query(
1884
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
1885
                            newColumn.name
1886
                        }" TYPE ${upType}`,
1887
                    ),
1888
                )
1889

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

1896
                downQueries.push(
9✔
1897
                    new Query(
1898
                        `ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${
1899
                            newColumn.name
1900
                        }" TYPE ${downType}`,
1901
                    ),
1902
                )
1903

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

1927
                // remove old ENUM
1928
                upQueries.push(
9✔
1929
                    this.dropEnumTypeSql(
1930
                        table,
1931
                        oldColumn,
1932
                        oldEnumNameWithSchema_old,
1933
                    ),
1934
                )
1935
                downQueries.push(
9✔
1936
                    this.createEnumTypeSql(
1937
                        table,
1938
                        oldColumn,
1939
                        oldEnumNameWithSchema_old,
1940
                    ),
1941
                )
1942
            }
1943

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

1993
            if (
222✔
1994
                newColumn.default !== oldColumn.default &&
225✔
1995
                !defaultValueChanged
1996
            ) {
1997
                if (
3!
1998
                    newColumn.default !== null &&
6✔
1999
                    newColumn.default !== undefined
2000
                ) {
2001
                    upQueries.push(
3✔
2002
                        new Query(
2003
                            `ALTER TABLE ${this.escapePath(
2004
                                table,
2005
                            )} ALTER COLUMN "${newColumn.name}" SET DEFAULT ${
2006
                                newColumn.default
2007
                            }`,
2008
                        ),
2009
                    )
2010

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

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

2080
        await this.executeQueries(upQueries, downQueries)
252✔
2081
        this.replaceCachedTable(table, clonedTable)
252✔
2082
    }
2083

2084
    /**
2085
     * Changes a column in the table.
2086
     */
2087
    async changeColumns(
2088
        tableOrName: Table | string,
2089
        changedColumns: { newColumn: TableColumn; oldColumn: TableColumn }[],
2090
    ): Promise<void> {
2091
        for (const { oldColumn, newColumn } of changedColumns) {
99✔
2092
            await this.changeColumn(tableOrName, oldColumn, newColumn)
192✔
2093
        }
2094
    }
2095

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

2114
        const clonedTable = table.clone()
66✔
2115
        const upQueries: Query[] = []
66✔
2116
        const downQueries: Query[] = []
66✔
2117

2118
        // drop primary key constraint
2119
        // todo: https://go.crdb.dev/issue-v/48026/v21.1
2120
        if (column.isPrimary) {
66!
2121
            const pkName = column.primaryKeyConstraintName
×
2122
                ? column.primaryKeyConstraintName
2123
                : this.connection.namingStrategy.primaryKeyName(
2124
                      clonedTable,
2125
                      clonedTable.primaryColumns.map((column) => column.name),
×
2126
                  )
2127

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

2146
            // update column in table
2147
            const tableColumn = clonedTable.findColumnByName(column.name)
×
2148
            tableColumn!.isPrimary = false
×
2149

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

2162
                const columnNames = clonedTable.primaryColumns
×
2163
                    .map((primaryColumn) => `"${primaryColumn.name}"`)
×
2164
                    .join(", ")
2165

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

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

2198
        // drop column check
2199
        const columnCheck = clonedTable.checks.find(
66✔
2200
            (check) =>
2201
                !!check.columnNames &&
24✔
2202
                check.columnNames.length === 1 &&
2203
                check.columnNames[0] === column.name,
2204
        )
2205
        if (columnCheck) {
66✔
2206
            clonedTable.checks.splice(
6✔
2207
                clonedTable.checks.indexOf(columnCheck),
2208
                1,
2209
            )
2210
            upQueries.push(this.dropCheckConstraintSql(table, columnCheck))
6✔
2211
            downQueries.push(this.createCheckConstraintSql(table, columnCheck))
6✔
2212
        }
2213

2214
        // drop column unique
2215
        const columnUnique = clonedTable.uniques.find(
66✔
2216
            (unique) =>
2217
                unique.columnNames.length === 1 &&
39✔
2218
                unique.columnNames[0] === column.name,
2219
        )
2220
        if (columnUnique) {
66✔
2221
            clonedTable.uniques.splice(
12✔
2222
                clonedTable.uniques.indexOf(columnUnique),
2223
                1,
2224
            )
2225
            upQueries.push(this.dropIndexSql(table, columnUnique.name!)) // CockroachDB creates indices for unique constraints
12✔
2226
            downQueries.push(
12✔
2227
                this.createUniqueConstraintSql(table, columnUnique),
2228
            )
2229
        }
2230

2231
        upQueries.push(
66✔
2232
            new Query(
2233
                `ALTER TABLE ${this.escapePath(table)} DROP COLUMN "${
2234
                    column.name
2235
                }"`,
2236
            ),
2237
        )
2238
        downQueries.push(
66✔
2239
            new Query(
2240
                `ALTER TABLE ${this.escapePath(
2241
                    table,
2242
                )} ADD ${this.buildCreateColumnSql(table, column)}`,
2243
            ),
2244
        )
2245

2246
        if (column.generationStrategy === "increment") {
66!
2247
            upQueries.push(
×
2248
                new Query(
2249
                    `DROP SEQUENCE ${this.escapePath(
2250
                        this.buildSequencePath(table, column),
2251
                    )}`,
2252
                ),
2253
            )
2254
            downQueries.push(
×
2255
                new Query(
2256
                    `CREATE SEQUENCE ${this.escapePath(
2257
                        this.buildSequencePath(table, column),
2258
                    )}`,
2259
                ),
2260
            )
2261
        }
2262

2263
        if (column.generatedType && column.asExpression) {
66✔
2264
            const currentSchema = await this.getCurrentSchema()
12✔
2265
            let { schema } = this.driver.parseTableName(table)
12✔
2266
            if (!schema) {
12!
2267
                schema = currentSchema
×
2268
            }
2269
            const deleteQuery = this.deleteTypeormMetadataSql({
12✔
2270
                schema: schema,
2271
                table: table.name,
2272
                type: MetadataTableType.GENERATED_COLUMN,
2273
                name: column.name,
2274
            })
2275
            const insertQuery = this.insertTypeormMetadataSql({
12✔
2276
                schema: schema,
2277
                table: table.name,
2278
                type: MetadataTableType.GENERATED_COLUMN,
2279
                name: column.name,
2280
                value: column.asExpression,
2281
            })
2282

2283
            upQueries.push(deleteQuery)
12✔
2284
            downQueries.push(insertQuery)
12✔
2285
        }
2286

2287
        // drop enum type
2288
        if (column.type === "enum" || column.type === "simple-enum") {
66✔
2289
            const hasEnum = await this.hasEnumType(table, column)
9✔
2290
            if (hasEnum) {
9✔
2291
                const enumType = await this.getUserDefinedTypeName(
9✔
2292
                    table,
2293
                    column,
2294
                )
2295
                const escapedEnumName = `"${enumType.schema}"."${enumType.name}"`
9✔
2296
                upQueries.push(
9✔
2297
                    this.dropEnumTypeSql(table, column, escapedEnumName),
2298
                )
2299
                downQueries.push(
9✔
2300
                    this.createEnumTypeSql(table, column, escapedEnumName),
2301
                )
2302
            }
2303
        }
2304

2305
        await this.executeQueries(upQueries, downQueries)
66✔
2306

2307
        clonedTable.removeColumn(column)
66✔
2308
        this.replaceCachedTable(table, clonedTable)
66✔
2309
    }
2310

2311
    /**
2312
     * Drops the columns in the table.
2313
     */
2314
    async dropColumns(
2315
        tableOrName: Table | string,
2316
        columns: TableColumn[] | string[],
2317
    ): Promise<void> {
2318
        for (const column of columns) {
12✔
2319
            await this.dropColumn(tableOrName, column)
21✔
2320
        }
2321
    }
2322

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

2336
        const up = this.createPrimaryKeySql(table, columnNames, constraintName)
×
2337

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

2345
        await this.executeQueries(up, down)
×
2346
        this.replaceCachedTable(table, clonedTable)
×
2347
    }
2348

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

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

2374
            const columnNamesString = primaryColumns
×
2375
                .map((column) => `"${column.name}"`)
×
2376
                .join(", ")
2377

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

2394
        // update columns in table.
2395
        clonedTable.columns
×
2396
            .filter((column) => columnNames.indexOf(column.name) !== -1)
×
2397
            .forEach((column) => (column.isPrimary = true))
×
2398

2399
        const pkName = primaryColumns[0].primaryKeyConstraintName
×
2400
            ? primaryColumns[0].primaryKeyConstraintName
2401
            : this.connection.namingStrategy.primaryKeyName(
2402
                  clonedTable,
2403
                  columnNames,
2404
              )
2405

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

2424
        await this.executeQueries(upQueries, downQueries)
×
2425
        this.replaceCachedTable(table, clonedTable)
×
2426
    }
2427

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

2450
    /**
2451
     * Creates new unique constraint.
2452
     */
2453
    async createUniqueConstraint(
2454
        tableOrName: Table | string,
2455
        uniqueConstraint: TableUnique,
2456
    ): Promise<void> {
2457
        const table = InstanceChecker.isTable(tableOrName)
21✔
2458
            ? tableOrName
2459
            : await this.getCachedTable(tableOrName)
2460

2461
        // new unique constraint may be passed without name. In this case we generate unique name manually.
2462
        if (!uniqueConstraint.name)
21✔
2463
            uniqueConstraint.name =
9✔
2464
                this.connection.namingStrategy.uniqueConstraintName(
2465
                    table,
2466
                    uniqueConstraint.columnNames,
2467
                )
2468

2469
        const up = this.createUniqueConstraintSql(table, uniqueConstraint)
21✔
2470
        // CockroachDB creates index for UNIQUE constraint.
2471
        // We must use DROP INDEX ... CASCADE instead of DROP CONSTRAINT.
2472
        const down = this.dropIndexSql(table, uniqueConstraint)
21✔
2473
        await this.executeQueries(up, down)
21✔
2474
        table.addUniqueConstraint(uniqueConstraint)
21✔
2475
    }
2476

2477
    /**
2478
     * Creates new unique constraints.
2479
     */
2480
    async createUniqueConstraints(
2481
        tableOrName: Table | string,
2482
        uniqueConstraints: TableUnique[],
2483
    ): Promise<void> {
2484
        for (const uniqueConstraint of uniqueConstraints) {
15✔
2485
            await this.createUniqueConstraint(tableOrName, uniqueConstraint)
15✔
2486
        }
2487
    }
2488

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

2507
        // CockroachDB creates index for UNIQUE constraint.
2508
        // We must use DROP INDEX ... CASCADE instead of DROP CONSTRAINT.
2509
        const up = this.dropIndexSql(table, uniqueConstraint)
24✔
2510
        const down = this.createUniqueConstraintSql(table, uniqueConstraint)
24✔
2511
        await this.executeQueries(up, down)
24✔
2512
        table.removeUniqueConstraint(uniqueConstraint)
24✔
2513
    }
2514

2515
    /**
2516
     * Drops unique constraints.
2517
     */
2518
    async dropUniqueConstraints(
2519
        tableOrName: Table | string,
2520
        uniqueConstraints: TableUnique[],
2521
    ): Promise<void> {
2522
        for (const uniqueConstraint of uniqueConstraints) {
21✔
2523
            await this.dropUniqueConstraint(tableOrName, uniqueConstraint)
21✔
2524
        }
2525
    }
2526

2527
    /**
2528
     * Creates new check constraint.
2529
     */
2530
    async createCheckConstraint(
2531
        tableOrName: Table | string,
2532
        checkConstraint: TableCheck,
2533
    ): Promise<void> {
2534
        const table = InstanceChecker.isTable(tableOrName)
15✔
2535
            ? tableOrName
2536
            : await this.getCachedTable(tableOrName)
2537

2538
        // new unique constraint may be passed without name. In this case we generate unique name manually.
2539
        if (!checkConstraint.name)
15✔
2540
            checkConstraint.name =
9✔
2541
                this.connection.namingStrategy.checkConstraintName(
2542
                    table,
2543
                    checkConstraint.expression!,
2544
                )
2545

2546
        const up = this.createCheckConstraintSql(table, checkConstraint)
15✔
2547
        const down = this.dropCheckConstraintSql(table, checkConstraint)
15✔
2548
        await this.executeQueries(up, down)
15✔
2549
        table.addCheckConstraint(checkConstraint)
15✔
2550
    }
2551

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

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

2583
        const up = this.dropCheckConstraintSql(table, checkConstraint)
9✔
2584
        const down = this.createCheckConstraintSql(table, checkConstraint)
9✔
2585
        await this.executeQueries(up, down)
9✔
2586
        table.removeCheckConstraint(checkConstraint)
9✔
2587
    }
2588

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

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

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

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

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

2650
    /**
2651
     * Creates a new foreign key.
2652
     */
2653
    async createForeignKey(
2654
        tableOrName: Table | string,
2655
        foreignKey: TableForeignKey,
2656
    ): Promise<void> {
2657
        const table = InstanceChecker.isTable(tableOrName)
8,184✔
2658
            ? tableOrName
2659
            : await this.getCachedTable(tableOrName)
2660

2661
        // new FK may be passed without name. In this case we generate FK name manually.
2662
        if (!foreignKey.name)
8,184✔
2663
            foreignKey.name = this.connection.namingStrategy.foreignKeyName(
3✔
2664
                table,
2665
                foreignKey.columnNames,
2666
                this.getTablePath(foreignKey),
2667
                foreignKey.referencedColumnNames,
2668
            )
2669

2670
        const up = this.createForeignKeySql(table, foreignKey)
8,184✔
2671
        const down = this.dropForeignKeySql(table, foreignKey)
8,184✔
2672
        await this.executeQueries(up, down)
8,184✔
2673
        table.addForeignKey(foreignKey)
8,184✔
2674
    }
2675

2676
    /**
2677
     * Creates a new foreign keys.
2678
     */
2679
    async createForeignKeys(
2680
        tableOrName: Table | string,
2681
        foreignKeys: TableForeignKey[],
2682
    ): Promise<void> {
2683
        for (const foreignKey of foreignKeys) {
4,704✔
2684
            await this.createForeignKey(tableOrName, foreignKey)
8,181✔
2685
        }
2686
    }
2687

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

2706
        const up = this.dropForeignKeySql(table, foreignKey)
24✔
2707
        const down = this.createForeignKeySql(table, foreignKey)
24✔
2708
        await this.executeQueries(up, down)
24✔
2709
        table.removeForeignKey(foreignKey)
24✔
2710
    }
2711

2712
    /**
2713
     * Drops a foreign keys from the table.
2714
     */
2715
    async dropForeignKeys(
2716
        tableOrName: Table | string,
2717
        foreignKeys: TableForeignKey[],
2718
    ): Promise<void> {
2719
        for (const foreignKey of foreignKeys) {
21✔
2720
            await this.dropForeignKey(tableOrName, foreignKey)
21✔
2721
        }
2722
    }
2723

2724
    /**
2725
     * Creates a new index.
2726
     */
2727
    async createIndex(
2728
        tableOrName: Table | string,
2729
        index: TableIndex,
2730
    ): Promise<void> {
2731
        const table = InstanceChecker.isTable(tableOrName)
33✔
2732
            ? tableOrName
2733
            : await this.getCachedTable(tableOrName)
2734

2735
        // new index may be passed without name. In this case we generate index name manually.
2736
        if (!index.name) index.name = this.generateIndexName(table, index)
33✔
2737

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

2758
    /**
2759
     * Creates a new indices
2760
     */
2761
    async createIndices(
2762
        tableOrName: Table | string,
2763
        indices: TableIndex[],
2764
    ): Promise<void> {
2765
        for (const index of indices) {
21✔
2766
            await this.createIndex(tableOrName, index)
21✔
2767
        }
2768
    }
2769

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

2788
        // old index may be passed without name. In this case we generate index name manually.
2789
        if (!index.name) index.name = this.generateIndexName(table, index)
24✔
2790

2791
        const up = this.dropIndexSql(table, index)
24✔
2792
        const down = this.createIndexSql(table, index)
24✔
2793
        await this.executeQueries(up, down)
24✔
2794
        table.removeIndex(index)
24✔
2795
    }
2796

2797
    /**
2798
     * Drops an indices from the table.
2799
     */
2800
    async dropIndices(
2801
        tableOrName: Table | string,
2802
        indices: TableIndex[],
2803
    ): Promise<void> {
2804
        for (const index of indices) {
×
2805
            await this.dropIndex(tableOrName, index)
×
2806
        }
2807
    }
2808

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

2817
    /**
2818
     * Removes all tables from the currently connected database.
2819
     */
2820
    async clearDatabase(): Promise<void> {
2821
        const schemas: string[] = []
3,819✔
2822
        this.connection.entityMetadatas
3,819✔
2823
            .filter((metadata) => metadata.schema)
13,389✔
2824
            .forEach((metadata) => {
2825
                const isSchemaExist = !!schemas.find(
3✔
2826
                    (schema) => schema === metadata.schema,
×
2827
                )
2828
                if (!isSchemaExist) schemas.push(metadata.schema!)
3✔
2829
            })
2830
        schemas.push(this.driver.options.schema || "current_schema()")
3,819✔
2831
        const schemaNamesString = schemas
3,819✔
2832
            .map((name) => {
2833
                return name === "current_schema()" ? name : "'" + name + "'"
3,822✔
2834
            })
2835
            .join(", ")
2836

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

2851
            const selectDropsQuery = `SELECT 'DROP TABLE IF EXISTS "' || table_schema || '"."' || table_name || '" CASCADE;' as "query" FROM "information_schema"."tables" WHERE "table_schema" IN (${schemaNamesString})`
3,819✔
2852
            const dropQueries: ObjectLiteral[] = await this.query(
3,819✔
2853
                selectDropsQuery,
2854
            )
2855
            await Promise.all(dropQueries.map((q) => this.query(q["query"])))
12,921✔
2856

2857
            const selectSequenceDropsQuery = `SELECT 'DROP SEQUENCE "' || sequence_schema || '"."' || sequence_name || '";' as "query" FROM "information_schema"."sequences" WHERE "sequence_schema" IN (${schemaNamesString})`
3,819✔
2858
            const sequenceDropQueries: ObjectLiteral[] = await this.query(
3,819✔
2859
                selectSequenceDropsQuery,
2860
            )
2861
            await Promise.all(
3,819✔
2862
                sequenceDropQueries.map((q) => this.query(q["query"])),
7,476✔
2863
            )
2864

2865
            // drop enum types. Supported starting from v20.2.19.
2866
            if (VersionUtils.isGreaterOrEqual(version, "20.2.19")) {
3,819✔
2867
                await this.dropEnumTypes(schemaNamesString)
3,819✔
2868
            }
2869

2870
            if (!isAnotherTransactionActive) await this.commitTransaction()
3,819✔
2871
        } catch (error) {
2872
            try {
×
2873
                // we throw original error even if rollback thrown an error
2874
                if (!isAnotherTransactionActive)
×
2875
                    await this.rollbackTransaction()
×
2876
            } catch {
2877
                // no-op
2878
            }
UNCOV
2879
            throw error
×
2880
        }
2881
    }
2882

2883
    // -------------------------------------------------------------------------
2884
    // Protected Methods
2885
    // -------------------------------------------------------------------------
2886

2887
    protected async loadViews(viewNames?: string[]): Promise<View[]> {
2888
        const hasTable = await this.hasTable(this.getTypeormMetadataTableName())
3,987✔
2889
        if (!hasTable) {
3,987✔
2890
            return []
3,951✔
2891
        }
2892

2893
        if (!viewNames) {
36!
2894
            viewNames = []
×
2895
        }
2896

2897
        const currentDatabase = await this.getCurrentDatabase()
36✔
2898
        const currentSchema = await this.getCurrentSchema()
36✔
2899

2900
        const viewsCondition = viewNames
36✔
2901
            .map((viewName) => {
2902
                const { schema, tableName } =
2903
                    this.driver.parseTableName(viewName)
24✔
2904

2905
                return `("t"."schema" = '${
24✔
2906
                    schema || currentSchema
24!
2907
                }' AND "t"."name" = '${tableName}')`
2908
            })
2909
            .join(" OR ")
2910

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

2934
    /**
2935
     * Loads all tables (with given names) from the database and creates a Table from them.
2936
     */
2937
    protected async loadTables(tableNames?: string[]): Promise<Table[]> {
2938
        // if no tables given then no need to proceed
2939
        if (tableNames && tableNames.length === 0) {
4,734✔
2940
            return []
18✔
2941
        }
2942

2943
        const currentSchema = await this.getCurrentSchema()
4,716✔
2944
        const currentDatabase = await this.getCurrentDatabase()
4,716✔
2945

2946
        const dbTables: { table_schema: string; table_name: string }[] = []
4,716✔
2947

2948
        if (!tableNames) {
4,716!
2949
            const tablesSql = `SELECT "table_schema", "table_name" FROM "information_schema"."tables"`
×
2950
            dbTables.push(...(await this.query(tablesSql)))
×
2951
        } else {
2952
            const tablesCondition = tableNames
4,716✔
2953
                .map((tableName) => this.driver.parseTableName(tableName))
14,202✔
2954
                .map(({ schema, tableName }) => {
2955
                    return `("table_schema" = '${
14,202✔
2956
                        schema || currentSchema
14,202!
2957
                    }' AND "table_name" = '${tableName}')`
2958
                })
2959
                .join(" OR ")
2960
            const tablesSql =
2961
                `SELECT "table_schema", "table_name" FROM "information_schema"."tables" WHERE ` +
4,716✔
2962
                tablesCondition
2963

2964
            dbTables.push(...(await this.query(tablesSql)))
4,716✔
2965
        }
2966

2967
        if (dbTables.length === 0) {
4,716✔
2968
            return []
3,873✔
2969
        }
2970

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

2986
        const constraintsCondition = dbTables
843✔
2987
            .map(({ table_name, table_schema }) => {
2988
                return `("ns"."nspname" = '${table_schema}' AND "t"."relname" = '${table_name}')`
1,254✔
2989
            })
2990
            .join(" OR ")
2991

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

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

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

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

3048
        const [
3049
            dbColumns,
3050
            dbConstraints,
3051
            dbIndices,
3052
            dbForeignKeys,
3053
            dbEnums,
3054
        ]: ObjectLiteral[][] = await Promise.all([
843✔
3055
            this.query(columnsSql),
3056
            this.query(constraintsSql),
3057
            this.query(indicesSql),
3058
            this.query(foreignKeysSql),
3059
            this.query(enumsSql),
3060
        ])
3061

3062
        // create tables for loaded tables
3063
        return Promise.all(
843✔
3064
            dbTables.map(async (dbTable) => {
3065
                const table = new Table()
1,254✔
3066

3067
                const getSchemaFromKey = (dbObject: any, key: string) => {
1,254✔
3068
                    return dbObject[key] === currentSchema &&
1,701!
3069
                        (!this.driver.options.schema ||
3070
                            this.driver.options.schema === currentSchema)
3071
                        ? undefined
3072
                        : dbObject[key]
3073
                }
3074

3075
                // We do not need to join schema name, when database is by default.
3076
                const schema = getSchemaFromKey(dbTable, "table_schema")
1,254✔
3077
                table.database = currentDatabase
1,254✔
3078
                table.schema = dbTable["table_schema"]
1,254✔
3079
                table.name = this.driver.buildTableName(
1,254✔
3080
                    dbTable["table_name"],
3081
                    schema,
3082
                )
3083

3084
                // create columns from the loaded columns
3085
                table.columns = await Promise.all(
1,254✔
3086
                    dbColumns
3087
                        .filter(
3088
                            (dbColumn) =>
3089
                                dbColumn["table_name"] ===
14,007✔
3090
                                    dbTable["table_name"] &&
3091
                                dbColumn["table_schema"] ===
3092
                                    dbTable["table_schema"],
3093
                        )
3094
                        .map(async (dbColumn) => {
3095
                            const columnConstraints = dbConstraints.filter(
4,857✔
3096
                                (dbConstraint) => {
3097
                                    return (
29,208✔
3098
                                        dbConstraint["table_name"] ===
51,858✔
3099
                                            dbColumn["table_name"] &&
3100
                                        dbConstraint["table_schema"] ===
3101
                                            dbColumn["table_schema"] &&
3102
                                        dbConstraint["column_name"] ===
3103
                                            dbColumn["column_name"]
3104
                                    )
3105
                                },
3106
                            )
3107

3108
                            const tableColumn = new TableColumn()
4,857✔
3109
                            tableColumn.name = dbColumn["column_name"]
4,857✔
3110

3111
                            tableColumn.type =
4,857✔
3112
                                dbColumn["crdb_sql_type"].toLowerCase()
3113
                            if (
4,857✔
3114
                                dbColumn["crdb_sql_type"].indexOf("COLLATE") !==
3115
                                -1
3116
                            ) {
3117
                                tableColumn.collation = dbColumn[
3✔
3118
                                    "crdb_sql_type"
3119
                                ].substr(
3120
                                    dbColumn["crdb_sql_type"].indexOf(
3121
                                        "COLLATE",
3122
                                    ) +
3123
                                        "COLLATE".length +
3124
                                        1,
3125
                                    dbColumn["crdb_sql_type"].length,
3126
                                )
3127
                                tableColumn.type = tableColumn.type.substr(
3✔
3128
                                    0,
3129
                                    dbColumn["crdb_sql_type"].indexOf(
3130
                                        "COLLATE",
3131
                                    ) - 1,
3132
                                )
3133
                            }
3134

3135
                            if (tableColumn.type.indexOf("(") !== -1)
4,857✔
3136
                                tableColumn.type = tableColumn.type.substr(
210✔
3137
                                    0,
3138
                                    tableColumn.type.indexOf("("),
3139
                                )
3140

3141
                            if (
4,857✔
3142
                                tableColumn.type === "numeric" ||
9,714✔
3143
                                tableColumn.type === "decimal"
3144
                            ) {
3145
                                if (
36✔
3146
                                    dbColumn["numeric_precision"] !== null &&
57✔
3147
                                    !this.isDefaultColumnPrecision(
3148
                                        table,
3149
                                        tableColumn,
3150
                                        dbColumn["numeric_precision"],
3151
                                    )
3152
                                ) {
3153
                                    tableColumn.precision = parseInt(
21✔
3154
                                        dbColumn["numeric_precision"],
3155
                                    )
3156
                                } else if (
15!
3157
                                    dbColumn["numeric_scale"] !== null &&
15!
3158
                                    !this.isDefaultColumnScale(
3159
                                        table,
3160
                                        tableColumn,
3161
                                        dbColumn["numeric_scale"],
3162
                                    )
3163
                                ) {
3164
                                    tableColumn.precision = undefined
×
3165
                                }
3166
                                if (
36✔
3167
                                    dbColumn["numeric_scale"] !== null &&
57✔
3168
                                    !this.isDefaultColumnScale(
3169
                                        table,
3170
                                        tableColumn,
3171
                                        dbColumn["numeric_scale"],
3172
                                    )
3173
                                ) {
3174
                                    tableColumn.scale = parseInt(
21✔
3175
                                        dbColumn["numeric_scale"],
3176
                                    )
3177
                                } else if (
15!
3178
                                    dbColumn["numeric_precision"] !== null &&
15!
3179
                                    !this.isDefaultColumnPrecision(
3180
                                        table,
3181
                                        tableColumn,
3182
                                        dbColumn["numeric_precision"],
3183
                                    )
3184
                                ) {
3185
                                    tableColumn.scale = undefined
×
3186
                                }
3187
                            }
3188

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

3199
                            const enumType = dbEnums.find((dbEnum) => {
4,857✔
3200
                                return dbEnum["name"] === udtName
1,809✔
3201
                            })
3202
                            if (enumType) {
4,857✔
3203
                                // check if `enumName` is specified by user
3204
                                const builtEnumName = this.buildEnumName(
390✔
3205
                                    table,
3206
                                    tableColumn,
3207
                                    false,
3208
                                    true,
3209
                                )
3210
                                const enumName =
3211
                                    builtEnumName !== enumType["name"]
390✔
3212
                                        ? enumType["name"]
3213
                                        : undefined
3214

3215
                                tableColumn.type = "enum"
390✔
3216
                                tableColumn.enum = enumType["value"].split("|")
390✔
3217
                                tableColumn.enumName = enumName
390✔
3218
                            }
3219

3220
                            if (
4,857✔
3221
                                dbColumn["data_type"].toLowerCase() === "array"
3222
                            ) {
3223
                                tableColumn.isArray = true
168✔
3224
                                if (!enumType) {
168✔
3225
                                    const type = dbColumn["crdb_sql_type"]
3✔
3226
                                        .replace("[]", "")
3227
                                        .toLowerCase()
3228
                                    tableColumn.type =
3✔
3229
                                        this.connection.driver.normalizeType({
3230
                                            type: type,
3231
                                        })
3232
                                }
3233
                            }
3234

3235
                            // check only columns that have length property
3236
                            if (
4,857✔
3237
                                this.driver.withLengthColumnTypes.indexOf(
7,182✔
3238
                                    tableColumn.type as ColumnType,
3239
                                ) !== -1 &&
3240
                                dbColumn["character_maximum_length"]
3241
                            ) {
3242
                                const length =
3243
                                    dbColumn[
177✔
3244
                                        "character_maximum_length"
3245
                                    ].toString()
3246
                                tableColumn.length =
177✔
3247
                                    !this.isDefaultColumnLength(
177✔
3248
                                        table,
3249
                                        tableColumn,
3250
                                        length,
3251
                                    )
3252
                                        ? length
3253
                                        : ""
3254
                            }
3255
                            tableColumn.isNullable =
4,857✔
3256
                                dbColumn["is_nullable"] === "YES"
3257

3258
                            const primaryConstraint = columnConstraints.find(
4,857✔
3259
                                (constraint) =>
3260
                                    constraint["constraint_type"] === "PRIMARY",
2,667✔
3261
                            )
3262
                            if (primaryConstraint) {
4,857✔
3263
                                tableColumn.isPrimary = true
1,329✔
3264
                                // find another columns involved in primary key constraint
3265
                                const anotherPrimaryConstraints =
3266
                                    dbConstraints.filter(
1,329✔
3267
                                        (constraint) =>
3268
                                            constraint["table_name"] ===
8,724✔
3269
                                                dbColumn["table_name"] &&
3270
                                            constraint["table_schema"] ===
3271
                                                dbColumn["table_schema"] &&
3272
                                            constraint["column_name"] !==
3273
                                                dbColumn["column_name"] &&
3274
                                            constraint["constraint_type"] ===
3275
                                                "PRIMARY",
3276
                                    )
3277

3278
                                // collect all column names
3279
                                const columnNames =
3280
                                    anotherPrimaryConstraints.map(
1,329✔
3281
                                        (constraint) =>
3282
                                            constraint["column_name"],
162✔
3283
                                    )
3284
                                columnNames.push(dbColumn["column_name"])
1,329✔
3285

3286
                                // build default primary key constraint name
3287
                                const pkName =
3288
                                    this.connection.namingStrategy.primaryKeyName(
1,329✔
3289
                                        table,
3290
                                        columnNames,
3291
                                    )
3292

3293
                                // if primary key has user-defined constraint name, write it in table column
3294
                                if (
1,329✔
3295
                                    primaryConstraint["constraint_name"] !==
3296
                                    pkName
3297
                                ) {
3298
                                    tableColumn.primaryKeyConstraintName =
78✔
3299
                                        primaryConstraint["constraint_name"]
3300
                                }
3301
                            }
3302

3303
                            const uniqueConstraints = columnConstraints.filter(
4,857✔
3304
                                (constraint) =>
3305
                                    constraint["constraint_type"] === "UNIQUE",
2,748✔
3306
                            )
3307
                            const isConstraintComposite =
3308
                                uniqueConstraints.every((uniqueConstraint) => {
4,857✔
3309
                                    return dbConstraints.some(
759✔
3310
                                        (dbConstraint) =>
3311
                                            dbConstraint["constraint_type"] ===
3,666✔
3312
                                                "UNIQUE" &&
3313
                                            dbConstraint["constraint_name"] ===
3314
                                                uniqueConstraint[
3315
                                                    "constraint_name"
3316
                                                ] &&
3317
                                            dbConstraint["column_name"] !==
3318
                                                dbColumn["column_name"],
3319
                                    )
3320
                                })
3321
                            tableColumn.isUnique =
4,857✔
3322
                                uniqueConstraints.length > 0 &&
5,607✔
3323
                                !isConstraintComposite
3324

3325
                            if (
4,857✔
3326
                                dbColumn["column_default"] !== null &&
5,946✔
3327
                                dbColumn["column_default"] !== undefined
3328
                            ) {
3329
                                if (
1,089✔
3330
                                    dbColumn["column_default"] ===
3331
                                    "unique_rowid()"
3332
                                ) {
3333
                                    tableColumn.isGenerated = true
12✔
3334
                                    tableColumn.generationStrategy = "rowid"
12✔
3335
                                } else if (
1,077✔
3336
                                    dbColumn["column_default"].indexOf(
3337
                                        "nextval",
3338
                                    ) !== -1
3339
                                ) {
3340
                                    tableColumn.isGenerated = true
735✔
3341
                                    tableColumn.generationStrategy = "increment"
735✔
3342
                                } else if (
342✔
3343
                                    dbColumn["column_default"] ===
3344
                                    "gen_random_uuid()"
3345
                                ) {
3346
                                    tableColumn.isGenerated = true
18✔
3347
                                    tableColumn.generationStrategy = "uuid"
18✔
3348
                                } else {
3349
                                    tableColumn.default = dbColumn[
324✔
3350
                                        "column_default"
3351
                                    ].replace(/:::[\w\s[\]"]+/g, "")
3352
                                    tableColumn.default =
324✔
3353
                                        tableColumn.default.replace(
3354
                                            /^(-?[\d.]+)$/,
3355
                                            "($1)",
3356
                                        )
3357

3358
                                    if (enumType) {
324✔
3359
                                        tableColumn.default =
36✔
3360
                                            tableColumn.default.replace(
3361
                                                `.${enumType["name"]}`,
3362
                                                "",
3363
                                            )
3364
                                    }
3365
                                }
3366
                            }
3367

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

3386
                                const results = await this.query(
153✔
3387
                                    asExpressionQuery.query,
3388
                                    asExpressionQuery.parameters,
3389
                                )
3390
                                if (results[0] && results[0].value) {
153!
3391
                                    tableColumn.asExpression = results[0].value
153✔
3392
                                } else {
3393
                                    tableColumn.asExpression = ""
×
3394
                                }
3395
                            }
3396

3397
                            tableColumn.comment =
4,857✔
3398
                                dbColumn["description"] == null
4,857✔
3399
                                    ? undefined
3400
                                    : dbColumn["description"]
3401
                            if (dbColumn["character_set_name"])
4,857!
3402
                                tableColumn.charset =
×
3403
                                    dbColumn["character_set_name"]
3404

3405
                            if (
4,857✔
3406
                                tableColumn.type === "geometry" ||
9,666✔
3407
                                tableColumn.type === "geography"
3408
                            ) {
3409
                                const sql =
3410
                                    `SELECT * FROM (` +
78✔
3411
                                    `SELECT "f_table_schema" "table_schema", "f_table_name" "table_name", ` +
3412
                                    `"f_${tableColumn.type}_column" "column_name", "srid", "type" ` +
3413
                                    `FROM "${tableColumn.type}_columns"` +
3414
                                    `) AS _ ` +
3415
                                    `WHERE "column_name" = '${dbColumn["column_name"]}' AND ` +
3416
                                    `"table_schema" = '${dbColumn["table_schema"]}' AND ` +
3417
                                    `"table_name" = '${dbColumn["table_name"]}'`
3418

3419
                                const results: ObjectLiteral[] =
3420
                                    await this.query(sql)
78✔
3421

3422
                                if (results.length > 0) {
78✔
3423
                                    tableColumn.spatialFeatureType =
78✔
3424
                                        results[0].type
3425
                                    tableColumn.srid = results[0].srid
78!
3426
                                        ? parseInt(results[0].srid)
3427
                                        : undefined
3428
                                }
3429
                            }
3430

3431
                            return tableColumn
4,857✔
3432
                        }),
3433
                )
3434

3435
                // find unique constraints of table, group them by constraint name and build TableUnique.
3436
                const tableUniqueConstraints = OrmUtils.uniq(
1,254✔
3437
                    dbConstraints.filter((dbConstraint) => {
3438
                        return (
8,487✔
3439
                            dbConstraint["table_name"] ===
13,989✔
3440
                                dbTable["table_name"] &&
3441
                            dbConstraint["table_schema"] ===
3442
                                dbTable["table_schema"] &&
3443
                            dbConstraint["constraint_type"] === "UNIQUE"
3444
                        )
3445
                    }),
3446
                    (dbConstraint) => dbConstraint["constraint_name"],
1,377✔
3447
                )
3448

3449
                table.uniques = tableUniqueConstraints.map((constraint) => {
1,254✔
3450
                    const uniques = dbConstraints.filter(
549✔
3451
                        (dbC) =>
3452
                            dbC["constraint_name"] ===
3,915✔
3453
                            constraint["constraint_name"],
3454
                    )
3455
                    return new TableUnique({
549✔
3456
                        name: constraint["constraint_name"],
3457
                        columnNames: uniques.map((u) => u["column_name"]),
759✔
3458
                    })
3459
                })
3460

3461
                // find check constraints of table, group them by constraint name and build TableCheck.
3462
                const tableCheckConstraints = OrmUtils.uniq(
1,254✔
3463
                    dbConstraints.filter((dbConstraint) => {
3464
                        return (
8,487✔
3465
                            dbConstraint["table_name"] ===
13,989✔
3466
                                dbTable["table_name"] &&
3467
                            dbConstraint["table_schema"] ===
3468
                                dbTable["table_schema"] &&
3469
                            dbConstraint["constraint_type"] === "CHECK"
3470
                        )
3471
                    }),
3472
                    (dbConstraint) => dbConstraint["constraint_name"],
240✔
3473
                )
3474

3475
                table.checks = tableCheckConstraints.map((constraint) => {
1,254✔
3476
                    const checks = dbConstraints.filter(
195✔
3477
                        (dbC) =>
3478
                            dbC["constraint_name"] ===
1,653✔
3479
                            constraint["constraint_name"],
3480
                    )
3481
                    return new TableCheck({
195✔
3482
                        name: constraint["constraint_name"],
3483
                        columnNames: checks.map((c) => c["column_name"]),
207✔
3484
                        expression: constraint["expression"].replace(
3485
                            /^\s*CHECK\s*\((.*)\)\s*$/i,
3486
                            "$1",
3487
                        ),
3488
                    })
3489
                })
3490

3491
                // find exclusion constraints of table, group them by constraint name and build TableExclusion.
3492
                const tableExclusionConstraints = OrmUtils.uniq(
1,254✔
3493
                    dbConstraints.filter((dbConstraint) => {
3494
                        return (
8,487✔
3495
                            dbConstraint["table_name"] ===
13,989✔
3496
                                dbTable["table_name"] &&
3497
                            dbConstraint["table_schema"] ===
3498
                                dbTable["table_schema"] &&
3499
                            dbConstraint["constraint_type"] === "EXCLUDE"
3500
                        )
3501
                    }),
3502
                    (dbConstraint) => dbConstraint["constraint_name"],
×
3503
                )
3504

3505
                table.exclusions = tableExclusionConstraints.map(
1,254✔
3506
                    (constraint) => {
3507
                        return new TableExclusion({
×
3508
                            name: constraint["constraint_name"],
3509
                            expression: constraint["expression"].substring(8), // trim EXCLUDE from start of expression
3510
                        })
3511
                    },
3512
                )
3513

3514
                // find foreign key constraints of table, group them by constraint name and build TableForeignKey.
3515
                const tableForeignKeyConstraints = OrmUtils.uniq(
1,254✔
3516
                    dbForeignKeys.filter((dbForeignKey) => {
3517
                        return (
1,647✔
3518
                            dbForeignKey["table_name"] ===
2,100✔
3519
                                dbTable["table_name"] &&
3520
                            dbForeignKey["table_schema"] ===
3521
                                dbTable["table_schema"]
3522
                        )
3523
                    }),
3524
                    (dbForeignKey) => dbForeignKey["constraint_name"],
624✔
3525
                )
3526

3527
                table.foreignKeys = tableForeignKeyConstraints.map(
1,254✔
3528
                    (dbForeignKey) => {
3529
                        const foreignKeys = dbForeignKeys.filter(
447✔
3530
                            (dbFk) =>
3531
                                dbFk["constraint_name"] ===
1,041✔
3532
                                dbForeignKey["constraint_name"],
3533
                        )
3534

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

3545
                        return new TableForeignKey({
447✔
3546
                            name: dbForeignKey["constraint_name"],
3547
                            columnNames: foreignKeys.map(
3548
                                (dbFk) => dbFk["column_name"],
453✔
3549
                            ),
3550
                            referencedSchema:
3551
                                dbForeignKey["referenced_table_schema"],
3552
                            referencedTableName: referencedTableName,
3553
                            referencedColumnNames: foreignKeys.map(
3554
                                (dbFk) => dbFk["referenced_column_name"],
453✔
3555
                            ),
3556
                            onDelete: dbForeignKey["on_delete"],
3557
                            onUpdate: dbForeignKey["on_update"],
3558
                        })
3559
                    },
3560
                )
3561

3562
                // find index constraints of table, group them by constraint name and build TableIndex.
3563
                const tableIndexConstraints = OrmUtils.uniq(
1,254✔
3564
                    dbIndices.filter((dbIndex) => {
3565
                        return (
2,241✔
3566
                            dbIndex["table_name"] === dbTable["table_name"] &&
2,910✔
3567
                            dbIndex["table_schema"] === dbTable["table_schema"]
3568
                        )
3569
                    }),
3570
                    (dbIndex) => dbIndex["constraint_name"],
1,077✔
3571
                )
3572

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

3595
                return table
1,254✔
3596
            }),
3597
        )
3598
    }
3599

3600
    /**
3601
     * Builds create table sql.
3602
     */
3603
    protected createTableSql(table: Table, createForeignKeys?: boolean): Query {
3604
        const columnDefinitions = table.columns
13,017✔
3605
            .map((column) => this.buildCreateColumnSql(table, column))
41,241✔
3606
            .join(", ")
3607
        let sql = `CREATE TABLE ${this.escapePath(table)} (${columnDefinitions}`
13,017✔
3608

3609
        table.columns
13,017✔
3610
            .filter((column) => column.isUnique)
41,241✔
3611
            .forEach((column) => {
3612
                const isUniqueExist = table.uniques.some(
2,100✔
3613
                    (unique) =>
3614
                        unique.columnNames.length === 1 &&
3,246✔
3615
                        unique.columnNames[0] === column.name,
3616
                )
3617
                if (!isUniqueExist)
2,100✔
3618
                    table.uniques.push(
15✔
3619
                        new TableUnique({
3620
                            name: this.connection.namingStrategy.uniqueConstraintName(
3621
                                table,
3622
                                [column.name],
3623
                            ),
3624
                            columnNames: [column.name],
3625
                        }),
3626
                    )
3627
            })
3628

3629
        table.indices
13,017✔
3630
            .filter((index) => index.isUnique)
8,652✔
3631
            .forEach((index) => {
3632
                table.uniques.push(
9✔
3633
                    new TableUnique({
3634
                        name: this.connection.namingStrategy.uniqueConstraintName(
3635
                            table,
3636
                            index.columnNames,
3637
                        ),
3638
                        columnNames: index.columnNames,
3639
                    }),
3640
                )
3641
            })
3642

3643
        if (table.uniques.length > 0) {
13,017✔
3644
            const uniquesSql = table.uniques
1,704✔
3645
                .map((unique) => {
3646
                    const uniqueName = unique.name
2,697✔
3647
                        ? unique.name
3648
                        : this.connection.namingStrategy.uniqueConstraintName(
3649
                              table,
3650
                              unique.columnNames,
3651
                          )
3652
                    const columnNames = unique.columnNames
2,697✔
3653
                        .map((columnName) => `"${columnName}"`)
3,462✔
3654
                        .join(", ")
3655
                    return `CONSTRAINT "${uniqueName}" UNIQUE (${columnNames})`
2,697✔
3656
                })
3657
                .join(", ")
3658

3659
            sql += `, ${uniquesSql}`
1,704✔
3660
        }
3661

3662
        if (table.checks.length > 0) {
13,017✔
3663
            const checksSql = table.checks
204✔
3664
                .map((check) => {
3665
                    const checkName = check.name
207✔
3666
                        ? check.name
3667
                        : this.connection.namingStrategy.checkConstraintName(
3668
                              table,
3669
                              check.expression!,
3670
                          )
3671
                    return `CONSTRAINT "${checkName}" CHECK (${check.expression})`
207✔
3672
                })
3673
                .join(", ")
3674

3675
            sql += `, ${checksSql}`
204✔
3676
        }
3677

3678
        if (table.foreignKeys.length > 0 && createForeignKeys) {
13,017✔
3679
            const foreignKeysSql = table.foreignKeys
12✔
3680
                .map((fk) => {
3681
                    const columnNames = fk.columnNames
15✔
3682
                        .map((columnName) => `"${columnName}"`)
18✔
3683
                        .join(", ")
3684
                    if (!fk.name)
15✔
3685
                        fk.name = this.connection.namingStrategy.foreignKeyName(
9✔
3686
                            table,
3687
                            fk.columnNames,
3688
                            this.getTablePath(fk),
3689
                            fk.referencedColumnNames,
3690
                        )
3691
                    const referencedColumnNames = fk.referencedColumnNames
15✔
3692
                        .map((columnName) => `"${columnName}"`)
18✔
3693
                        .join(", ")
3694

3695
                    let constraint = `CONSTRAINT "${
15✔
3696
                        fk.name
3697
                    }" FOREIGN KEY (${columnNames}) REFERENCES ${this.escapePath(
3698
                        this.getTablePath(fk),
3699
                    )} (${referencedColumnNames})`
3700
                    if (fk.onDelete) constraint += ` ON DELETE ${fk.onDelete}`
15✔
3701
                    if (fk.onUpdate) constraint += ` ON UPDATE ${fk.onUpdate}`
15✔
3702

3703
                    return constraint
15✔
3704
                })
3705
                .join(", ")
3706

3707
            sql += `, ${foreignKeysSql}`
12✔
3708
        }
3709

3710
        const primaryColumns = table.columns.filter(
13,017✔
3711
            (column) => column.isPrimary,
41,241✔
3712
        )
3713
        if (primaryColumns.length > 0) {
13,017✔
3714
            const primaryKeyName = primaryColumns[0].primaryKeyConstraintName
12,984✔
3715
                ? primaryColumns[0].primaryKeyConstraintName
3716
                : this.connection.namingStrategy.primaryKeyName(
3717
                      table,
3718
                      primaryColumns.map((column) => column.name),
16,197✔
3719
                  )
3720

3721
            const columnNames = primaryColumns
12,984✔
3722
                .map((column) => `"${column.name}"`)
16,269✔
3723
                .join(", ")
3724
            sql += `, CONSTRAINT "${primaryKeyName}" PRIMARY KEY (${columnNames})`
12,984✔
3725
        }
3726

3727
        sql += `)`
13,017✔
3728

3729
        table.columns
13,017✔
3730
            .filter((it) => it.comment)
41,241✔
3731
            .forEach(
3732
                (it) =>
3733
                    (sql += `; COMMENT ON COLUMN ${this.escapePath(table)}."${
174✔
3734
                        it.name
3735
                    }" IS ${this.escapeComment(it.comment)}`),
3736
            )
3737

3738
        return new Query(sql)
13,017✔
3739
    }
3740

3741
    /**
3742
     * Loads Cockroachdb version.
3743
     */
3744
    async getVersion(): Promise<string> {
3745
        const result: [{ version: string }] = await this.query(
3,819✔
3746
            `SELECT version() AS "version"`,
3747
        )
3748
        const versionString = result[0].version
3,819✔
3749

3750
        return versionString.replace(/^CockroachDB CCL v([\d.]+) .*$/, "$1")
3,819✔
3751
    }
3752

3753
    /**
3754
     * Builds drop table sql.
3755
     */
3756
    protected dropTableSql(tableOrPath: Table | string): Query {
3757
        return new Query(`DROP TABLE ${this.escapePath(tableOrPath)}`)
13,017✔
3758
    }
3759

3760
    protected createViewSql(view: View): Query {
3761
        if (typeof view.expression === "string") {
18!
3762
            return new Query(
×
3763
                `CREATE VIEW ${this.escapePath(view)} AS ${view.expression}`,
3764
            )
3765
        } else {
3766
            return new Query(
18✔
3767
                `CREATE VIEW ${this.escapePath(view)} AS ${view
3768
                    .expression(this.connection)
3769
                    .getQuery()}`,
3770
            )
3771
        }
3772
    }
3773

3774
    protected async insertViewDefinitionSql(view: View): Promise<Query> {
3775
        const currentSchema = await this.getCurrentSchema()
18✔
3776
        let { schema, tableName: name } = this.driver.parseTableName(view)
18✔
3777
        if (!schema) {
18!
3778
            schema = currentSchema
×
3779
        }
3780

3781
        const expression =
3782
            typeof view.expression === "string"
18!
3783
                ? view.expression.trim()
3784
                : view.expression(this.connection).getQuery()
3785
        return this.insertTypeormMetadataSql({
18✔
3786
            type: MetadataTableType.VIEW,
3787
            schema: schema,
3788
            name: name,
3789
            value: expression,
3790
        })
3791
    }
3792

3793
    /**
3794
     * Builds drop view sql.
3795
     */
3796
    protected dropViewSql(viewOrPath: View | string): Query {
3797
        return new Query(`DROP VIEW ${this.escapePath(viewOrPath)}`)
18✔
3798
    }
3799

3800
    /**
3801
     * Builds remove view sql.
3802
     */
3803
    protected async deleteViewDefinitionSql(
3804
        viewOrPath: View | string,
3805
    ): Promise<Query> {
3806
        const currentSchema = await this.getCurrentSchema()
18✔
3807

3808
        let { schema, tableName: name } = this.driver.parseTableName(viewOrPath)
18✔
3809

3810
        if (!schema) {
18!
3811
            schema = currentSchema
×
3812
        }
3813

3814
        return this.deleteTypeormMetadataSql({
18✔
3815
            type: MetadataTableType.VIEW,
3816
            schema,
3817
            name,
3818
        })
3819
    }
3820

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

3834
    /**
3835
     * Checks if enum with the given name exist in the database.
3836
     */
3837
    protected async hasEnumType(
3838
        table: Table,
3839
        column: TableColumn,
3840
    ): Promise<boolean> {
3841
        let { schema } = this.driver.parseTableName(table)
336✔
3842

3843
        if (!schema) {
336!
3844
            schema = await this.getCurrentSchema()
×
3845
        }
3846

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

3856
    /**
3857
     * Builds create ENUM type sql.
3858
     */
3859
    protected createEnumTypeSql(
3860
        table: Table,
3861
        column: TableColumn,
3862
        enumName?: string,
3863
    ): Query {
3864
        if (!enumName) enumName = this.buildEnumName(table, column)
348✔
3865
        const enumValues = column
348✔
3866
            .enum!.map((value) => `'${value.replace("'", "''")}'`)
1,107✔
3867
            .join(", ")
3868
        return new Query(`CREATE TYPE ${enumName} AS ENUM(${enumValues})`)
348✔
3869
    }
3870

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

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

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

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

3938
    /**
3939
     * Builds drop primary key sql.
3940
     */
3941
    protected dropPrimaryKeySql(table: Table): Query {
3942
        if (!table.primaryColumns.length)
×
3943
            throw new TypeORMError(`Table ${table} has no primary keys.`)
×
3944

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

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

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

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

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

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

4045
        return new Query(sql)
8,208✔
4046
    }
4047

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

4067
    /**
4068
     * Builds sequence name from given table and column.
4069
     */
4070
    protected buildSequenceName(
4071
        table: Table,
4072
        columnOrName: TableColumn | string,
4073
    ): string {
4074
        const { tableName } = this.driver.parseTableName(table)
22,644✔
4075

4076
        const columnName = InstanceChecker.isTableColumn(columnOrName)
22,644!
4077
            ? columnOrName.name
4078
            : columnOrName
4079

4080
        return `${tableName}_${columnName}_seq`
22,644✔
4081
    }
4082

4083
    protected buildSequencePath(
4084
        table: Table,
4085
        columnOrName: TableColumn | string,
4086
    ): string {
4087
        const { schema } = this.driver.parseTableName(table)
22,644✔
4088

4089
        return schema
22,644!
4090
            ? `${schema}.${this.buildSequenceName(table, columnOrName)}`
4091
            : this.buildSequenceName(table, columnOrName)
4092
    }
4093

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

4118
    protected async getUserDefinedTypeName(table: Table, column: TableColumn) {
4119
        let { schema, tableName: name } = this.driver.parseTableName(table)
21✔
4120

4121
        if (!schema) {
21!
4122
            schema = await this.getCurrentSchema()
×
4123
        }
4124

4125
        const result = await this.query(
21✔
4126
            `SELECT "udt_schema", "udt_name" ` +
4127
                `FROM "information_schema"."columns" WHERE "table_schema" = '${schema}' AND "table_name" = '${name}' AND "column_name"='${column.name}'`,
4128
        )
4129

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

4145
    /**
4146
     * Escapes a given comment so it's safe to include in a query.
4147
     */
4148
    protected escapeComment(comment?: string) {
4149
        if (comment === undefined || comment.length === 0) {
204✔
4150
            return "NULL"
18✔
4151
        }
4152

4153
        comment = comment.replace(/'/g, "''").replace(/\u0000/g, "") // Null bytes aren't allowed in comments
186✔
4154

4155
        return `'${comment}'`
186✔
4156
    }
4157

4158
    /**
4159
     * Escapes given table or view path.
4160
     */
4161
    protected escapePath(target: Table | View | string): string {
4162
        const { schema, tableName } = this.driver.parseTableName(target)
91,836✔
4163

4164
        if (schema && schema !== this.driver.searchSchema) {
91,836!
4165
            return `"${schema}"."${tableName}"`
×
4166
        }
4167

4168
        return `"${tableName}"`
91,836✔
4169
    }
4170

4171
    /**
4172
     * Builds a query for create column.
4173
     */
4174
    protected buildCreateColumnSql(table: Table, column: TableColumn) {
4175
        let c = '"' + column.name + '"'
41,370✔
4176

4177
        if (column.isGenerated) {
41,370✔
4178
            if (column.generationStrategy === "increment") {
7,959✔
4179
                c += ` INT DEFAULT nextval('${this.escapePath(
7,548✔
4180
                    this.buildSequencePath(table, column),
4181
                )}')`
4182
            } else if (column.generationStrategy === "rowid") {
411✔
4183
                c += " INT DEFAULT unique_rowid()"
12✔
4184
            } else if (column.generationStrategy === "uuid") {
399✔
4185
                c += " UUID DEFAULT gen_random_uuid()"
399✔
4186
            }
4187
        }
4188

4189
        if (column.type === "enum" || column.type === "simple-enum") {
41,370✔
4190
            c += " " + this.buildEnumName(table, column)
348✔
4191
            if (column.isArray) c += " array"
348✔
4192
        } else if (!column.isGenerated) {
41,022✔
4193
            c += " " + this.connection.driver.createFullType(column)
33,063✔
4194
        }
4195

4196
        if (column.asExpression) {
41,370✔
4197
            c += ` AS (${column.asExpression}) ${
117✔
4198
                column.generatedType ? column.generatedType : "VIRTUAL"
117!
4199
            }`
4200
        } else {
4201
            if (column.charset) c += ' CHARACTER SET "' + column.charset + '"'
41,253!
4202
            if (column.collation) c += ' COLLATE "' + column.collation + '"'
41,253✔
4203
        }
4204

4205
        if (!column.isNullable) c += " NOT NULL"
41,370✔
4206
        if (
41,370✔
4207
            !column.isGenerated &&
76,923✔
4208
            column.default !== undefined &&
4209
            column.default !== null
4210
        )
4211
            c += " DEFAULT " + column.default
2,142✔
4212

4213
        return c
41,370✔
4214
    }
4215
    /**
4216
     * Change table comment.
4217
     */
4218
    changeTableComment(
4219
        tableOrName: Table | string,
4220
        comment?: string,
4221
    ): Promise<void> {
4222
        throw new TypeORMError(
×
4223
            `cockroachdb driver does not support change table comment.`,
4224
        )
4225
    }
4226
}
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