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

typeorm / typeorm / 14796576772

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

Pull #11434

github

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

5216 of 12761 branches covered (40.87%)

Branch coverage included in aggregate %.

11439 of 23951 relevant lines covered (47.76%)

15712.55 hits per line

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

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

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

42
    /**
43
     * Database driver used by connection.
44
     */
45
    driver: 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[] }[] = []
×
65

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

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

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

80
    constructor(driver: CockroachDriver, mode: ReplicationMode) {
81
        super()
×
82
        this.driver = driver
×
83
        this.connection = driver.connection
×
84
        this.mode = mode
×
85
        this.broadcaster = new Broadcaster(this)
×
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)
×
98
            return Promise.resolve(this.databaseConnection)
×
99

100
        if (this.databaseConnectionPromise)
×
101
            return this.databaseConnectionPromise
×
102

103
        if (this.mode === "slave" && this.driver.isReplicated) {
×
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
×
126
                .obtainMasterConnection()
127
                .then(([connection, release]: any[]) => {
128
                    this.driver.connectedQueryRunners.push(this)
×
129
                    this.databaseConnection = connection
×
130

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

142
                    return this.databaseConnection
×
143
                })
144
        }
145

146
        return this.databaseConnectionPromise
×
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) {
×
155
            return
×
156
        }
157

158
        this.isReleased = true
×
159
        if (this.releaseCallback) {
×
160
            this.releaseCallback(err)
×
161
            this.releaseCallback = undefined
×
162
        }
163

164
        const index = this.driver.connectedQueryRunners.indexOf(this)
×
165

166
        if (index !== -1) {
×
167
            this.driver.connectedQueryRunners.splice(index, 1)
×
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()
×
177
    }
178

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

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

204
        this.transactionDepth += 1
×
205
        this.storeQueries = true
×
206

207
        await this.broadcaster.broadcast("AfterTransactionStart")
×
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()
×
216

217
        await this.broadcaster.broadcast("BeforeTransactionCommit")
×
218

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

234
        await this.broadcaster.broadcast("AfterTransactionCommit")
×
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()
×
243

244
        await this.broadcaster.broadcast("BeforeTransactionRollback")
×
245

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

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

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

272
        const databaseConnection = await this.connect()
×
273

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

277
        const broadcasterResult = new BroadcasterResult()
×
278
        const queryStartTime = Date.now()
×
279

280
        if (this.isTransactionActive && this.storeQueries) {
×
281
            this.queries.push({ query, parameters })
×
282
        }
283

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

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

310
            const result = new QueryResult()
×
311

312
            if (raw.hasOwnProperty("rowCount")) {
×
313
                result.affected = raw.rowCount
×
314
            }
315

316
            if (raw.hasOwnProperty("rows")) {
×
317
                result.records = raw.rows
×
318
            }
319

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

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

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

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

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

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

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

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

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

425
        return stream
×
426
    }
427

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

631
        upQueries.push(this.createTableSql(table, createForeignKeys))
×
632
        downQueries.push(this.dropTableSql(table))
×
633

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

838
        newTable.name = schemaName
×
839
            ? `${schemaName}.${newTableName}`
840
            : newTableName
841

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

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

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

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

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

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

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

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

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

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

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

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

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

962
            // replace constraint name
963
            index.name = newIndexName
×
964
        })
965

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1234
        await this.executeQueries(upQueries, downQueries)
×
1235

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1456
                    // replace constraint name
1457
                    unique.name = newUniqueName
×
1458
                })
1459

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

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

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

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

1496
                    // replace constraint name
1497
                    index.name = newIndexName
×
1498
                })
1499

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

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

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

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

1549
                        // replace constraint name
1550
                        foreignKey.name = newForeignKeyName
×
1551
                    })
1552

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2301
        await this.executeQueries(upQueries, downQueries)
×
2302

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2889
        if (!viewNames) {
×
2890
            viewNames = []
×
2891
        }
2892

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

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

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

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

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

2939
        const currentSchema = await this.getCurrentSchema()
×
2940
        const currentDatabase = await this.getCurrentDatabase()
×
2941

2942
        const dbTables: { table_schema: string; table_name: string }[] = []
×
2943

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

2960
            dbTables.push(...(await this.query(tablesSql)))
×
2961
        }
2962

2963
        if (dbTables.length === 0) {
×
2964
            return []
×
2965
        }
2966

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

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

2988
        const constraintsSql =
2989
            `SELECT "ns"."nspname" AS "table_schema", "t"."relname" AS "table_name", "cnst"."conname" AS "constraint_name", ` +
×
2990
            `pg_get_constraintdef("cnst"."oid") AS "expression", ` +
2991
            `CASE "cnst"."contype" WHEN 'p' THEN 'PRIMARY' WHEN 'u' THEN 'UNIQUE' WHEN 'c' THEN 'CHECK' WHEN 'x' THEN 'EXCLUDE' END AS "constraint_type", "a"."attname" AS "column_name" ` +
2992
            `FROM "pg_constraint" "cnst" ` +
2993
            `INNER JOIN "pg_class" "t" ON "t"."oid" = "cnst"."conrelid" ` +
2994
            `INNER JOIN "pg_namespace" "ns" ON "ns"."oid" = "cnst"."connamespace" ` +
2995
            `LEFT JOIN "pg_attribute" "a" ON "a"."attrelid" = "cnst"."conrelid" AND "a"."attnum" = ANY ("cnst"."conkey") ` +
2996
            `WHERE "t"."relkind" = 'r' AND (${constraintsCondition})`
2997

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

3011
        const foreignKeysCondition = dbTables
×
3012
            .map(({ table_name, table_schema }) => {
3013
                return `("ns"."nspname" = '${table_schema}' AND "cl"."relname" = '${table_name}')`
×
3014
            })
3015
            .join(" OR ")
3016
        const foreignKeysSql =
3017
            `SELECT "con"."conname" AS "constraint_name", "con"."nspname" AS "table_schema", "con"."relname" AS "table_name", "att2"."attname" AS "column_name", ` +
×
3018
            `"ns"."nspname" AS "referenced_table_schema", "cl"."relname" AS "referenced_table_name", "att"."attname" AS "referenced_column_name", "con"."confdeltype" AS "on_delete", "con"."confupdtype" AS "on_update" ` +
3019
            `FROM ( ` +
3020
            `SELECT UNNEST ("con1"."conkey") AS "parent", UNNEST ("con1"."confkey") AS "child", "con1"."confrelid", "con1"."conrelid", "con1"."conname", "con1"."contype", "ns"."nspname", "cl"."relname", ` +
3021
            `CASE "con1"."confdeltype" WHEN 'a' THEN 'NO ACTION' WHEN 'r' THEN 'RESTRICT' WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' END as "confdeltype", ` +
3022
            `CASE "con1"."confupdtype" WHEN 'a' THEN 'NO ACTION' WHEN 'r' THEN 'RESTRICT' WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' END as "confupdtype" ` +
3023
            `FROM "pg_class" "cl" ` +
3024
            `INNER JOIN "pg_namespace" "ns" ON "cl"."relnamespace" = "ns"."oid" ` +
3025
            `INNER JOIN "pg_constraint" "con1" ON "con1"."conrelid" = "cl"."oid" ` +
3026
            `WHERE "con1"."contype" = 'f' AND (${foreignKeysCondition}) ` +
3027
            `) "con" ` +
3028
            `INNER JOIN "pg_attribute" "att" ON "att"."attrelid" = "con"."confrelid" AND "att"."attnum" = "con"."child" ` +
3029
            `INNER JOIN "pg_class" "cl" ON "cl"."oid" = "con"."confrelid" ` +
3030
            `INNER JOIN "pg_namespace" "ns" ON "cl"."relnamespace" = "ns"."oid" ` +
3031
            `INNER JOIN "pg_attribute" "att2" ON "att2"."attrelid" = "con"."conrelid" AND "att2"."attnum" = "con"."parent"`
3032

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

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

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

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

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

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

3104
                            const tableColumn = new TableColumn()
×
3105
                            tableColumn.name = dbColumn["column_name"]
×
3106

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3427
                            return tableColumn
×
3428
                        }),
3429
                )
3430

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

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

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

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

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

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

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

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

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

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

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

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

3591
                return table
×
3592
            }),
3593
        )
3594
    }
3595

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

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

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

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

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

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

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

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

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

3699
                    return constraint
×
3700
                })
3701
                .join(", ")
3702

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

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

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

3723
        sql += `)`
×
3724

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

3734
        return new Query(sql)
×
3735
    }
3736

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

4041
        return new Query(sql)
×
4042
    }
4043

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

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

4072
        const columnName = InstanceChecker.isTableColumn(columnOrName)
×
4073
            ? columnOrName.name
4074
            : columnOrName
4075

4076
        return `${tableName}_${columnName}_seq`
×
4077
    }
4078

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

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

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

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

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

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

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

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

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

4151
        return `'${comment}'`
×
4152
    }
4153

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

4160
        if (schema && schema !== this.driver.searchSchema) {
×
4161
            return `"${schema}"."${tableName}"`
×
4162
        }
4163

4164
        return `"${tableName}"`
×
4165
    }
4166

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

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

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

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

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

4209
        return c
×
4210
    }
4211
    /**
4212
     * Change table comment.
4213
     */
4214
    changeTableComment(
4215
        tableOrName: Table | string,
4216
        comment?: string,
4217
    ): Promise<void> {
4218
        throw new TypeORMError(
×
4219
            `cockroachdb driver does not support change table comment.`,
4220
        )
4221
    }
4222
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc